/*
 * TagEntry.cpp
 *
 *  Created on: 15.10.2009
 *      Author: stefan.detter
 */

#include "TagEntry.h"
#include "ReaderEntry.h"
#include "AntennaEntry.h"
#include "InfoEntry.h"

#include "../../Settings.h"


TagEntry::TagEntry(TreeItem* parent, ReaderEntry* reader, AntennaEntry* antenna, const QString &type, const QString &tagId, const QString &visualTagId)
	: TreeItem(parent)
{
	m_reader = reader;
	m_antenna = antenna;
    m_tag = TagData(tagId, visualTagId, type);
	m_justCreated = true;
	m_readCount = 0;
	m_readRate = 0.0;
	m_maxReadRate = 0.0;
	m_timeStamp = QDateTime::currentDateTime();
	m_lastRead = QTime::currentTime();
	m_state = STATE_ACTIVE;

	m_triggerTimer = new QTimer(this);
	m_triggerTimer->setSingleShot(true);
	m_triggerTimer->setInterval(500);
	connect(m_triggerTimer, 	SIGNAL(timeout()),
			this, 				  SLOT(triggerTimeOut()));
	m_triggered = false;

    if(Settings::d->showTagIdAscii())
    {
        QTimer::singleShot(10, this, SLOT(createAsciiInformation()));
    }

	prepareInformation();
	inventoryStarted();
}


TagEntry::~TagEntry()
{
	delete m_triggerTimer;
}


void TagEntry::update ( const TagEvent& event )
{
	if(m_justCreated)
        m_lastStarted.start();

	m_lastRead.start();
	m_timeStamp = event.readTime();
	m_readCount+= event.readCount();

	QList<QVariant> informations = event.getInformations();
	foreach(QVariant info, informations)
	{
		if(info.canConvert<RSSI_Information>())
			handleInformation(info.value<RSSI_Information>());
		else if(info.canConvert<MEMORY_Information>())
			handleInformation(info.value<MEMORY_Information>());
		else if(info.canConvert<TRIGGER_Information>())
			handleInformation(info.value<TRIGGER_Information>());
		else if(info.canConvert<FREQUENCY_Information>())
			handleInformation(info.value<FREQUENCY_Information>());
		else if(info.canConvert<STATE_Information>())
			handleInformation(info.value<STATE_Information>());
		else if(info.canConvert<BATTERY_Information>())
			handleInformation(info.value<BATTERY_Information>());
		else if(info.canConvert<HANDLE_Information>())
			handleInformation(info.value<HANDLE_Information>());
		else if(info.canConvert<PC_Information>())
			handleInformation(info.value<PC_Information>());
		else if(info.canConvert<MESSAGEID_Information>())
			handleInformation(info.value<MESSAGEID_Information>());
        else if(info.canConvert<RN16_Information>())
            handleInformation(info.value<RN16_Information>());
        else if(info.canConvert<NXP_BRANDID_Information>())
            handleInformation(info.value<NXP_BRANDID_Information>());
        else if(info.canConvert<GENERIC_Information>())
			handleInformation(info.value<GENERIC_Information>());
	}
}

ReaderEntry* TagEntry::reader() const
{
	return m_reader;
}

AntennaEntry* TagEntry::antenna() const
{
	return m_antenna;
}

uint TagEntry::antennaId() const
{
	if(m_antenna == 0)
		return 0;
	else
		return m_antenna->antennaId();
}

QString	TagEntry::tagId() const
{
    return m_tag.tagId();
}

QString TagEntry::visualTagId() const
{
    if(Settings::d->parseTagIds())
        return m_tag.visualTagId();
    return m_tag.tagId();
}

QString TagEntry::tagType() const
{
	return m_tag.tagType();
}


uint TagEntry::readCount() const
{
	return m_readCount;
}

QDateTime TagEntry::timeStamp() const
{
	return m_timeStamp;
}

QString	TagEntry::timeStampString() const
{
	return m_timeStamp.toString(Qt::ISODate);
}



void TagEntry::updateDynamicInfo()
{
	if(m_readRunning)
	{
        quint64 time = m_timeRunning + m_lastStarted.elapsed();

        if(time == 0)
            time = 1;

        m_readRate = ((double)m_readCount) / ((double)time);
		m_readRate *= 1000;

		if(m_readRate > m_maxReadRate && m_readCount > 1)
			m_maxReadRate = m_readRate;

		emit dataChanged(this, COL_ReadCount, COL_ReadRate);

		if(Settings::d->ttlOn() == true)
		{
			uint lastRead = m_lastRead.elapsed();
			if(lastRead < Settings::d->ttlInactive())
				m_state = STATE_ACTIVE;
			else if(lastRead < Settings::d->ttlOutOfRange())
				m_state = STATE_INACTIVE;
			else if(lastRead < Settings::d->ttlDelete())
				m_state = STATE_OUTOFRANGE;
			else{
				emit shouldBeRemoved(m_tag.tagId());
				return;
			}
		}
		else
			m_state = STATE_ACTIVE;

		updateInformation();

		TreeItem::updateDynamicInfo();
	}
}


void TagEntry::inventoryStarted()
{
    m_maxReadRate = 0;
    m_timeRunning = 0;
    m_frequencyCount.clear();

    if(!m_justCreated)
        m_readCount = 0;

    inventoryContinued();
	TreeItem::inventoryStarted();
}

void TagEntry::inventoryStoped()
{
    inventoryPaused();

    TreeItem::inventoryStoped();

    if(m_readCount == 0)
    {
        emit shouldBeRemoved(m_tag.tagId());
    }
}

void TagEntry::inventoryContinued()
{
    m_lastRead = QTime::currentTime();
    m_readRunning = true;
    m_justCreated = false;
    m_lastStarted.start();

    removeFrequencyEntries();
    TreeItem::inventoryContinued();
}

void TagEntry::inventoryPaused()
{
    m_timeRunning += m_lastStarted.elapsed();
    m_readRunning = false;

    TreeItem::inventoryPaused();
}


void TagEntry::removeRSSI()
{
	if(!m_tagInfos.contains("RSSI"))
		return;

	TreeItem* t = m_tagInfos.value("RSSI");

	emit aboutToBeRemoved(t);

	emit beginRemoveRows( this, m_children.indexOf(t), m_children.indexOf(t) );
	m_children.removeAt(m_children.indexOf(t));
	m_tagInfos.remove("RSSI");
	emit endRemoveRows();

	delete t;
}

void TagEntry::removeReadFrequency()
{
	if(!m_tagInfos.contains("FREQUENCY"))
		return;

	TreeItem* t = m_tagInfos.value("FREQUENCY");

	emit aboutToBeRemoved(t);

	emit beginRemoveRows( this, m_children.indexOf(t), m_children.indexOf(t) );
	m_children.removeAt(m_children.indexOf(t));
	m_tagInfos.remove("FREQUENCY");
	emit endRemoveRows();

	delete t;
}


void TagEntry::triggerTimeOut()
{
	m_triggered = false;
	emit dataChanged(this, COL_ID, COL_State);
}


void TagEntry::createAsciiInformation()
{
    QString uniqueName = "ASCII";

    emit beginInsertRows( this, m_children.size(), m_children.size());

    QByteArray tagId = QrfeGlobal::stringToBytes(m_tag.tagId());
    InfoEntry* i = new InfoEntry(this, uniqueName, QString::fromLatin1(tagId));

    connectChild(i);

    m_children.append(i);
    m_tagInfos.insert(uniqueName, i);
    emit endInsertRows();
    emit requestToExpand(this);
}

void TagEntry::handleInformation( const RSSI_Information& info )
{
	if(!m_tagInfos.contains(info.typeString()))
	{
		emit beginInsertRows( this, m_children.size(), m_children.size());

		InfoEntry* i = new InfoEntry(this, info.typeString(), info.percent(), 0, 100, "%1 %");

		connectChild(i);

		m_children.append(i);
		m_tagInfos.insert(info.typeString(), i);
		emit endInsertRows();
		emit requestToExpand(this);


		const QList<RSSI_Information::CHANNEL>& channels = info.getChannels();
		foreach(RSSI_Information::CHANNEL channel, channels) 
		{
			i->addChild(channel.name, channel.value, channel.minimum, channel.maximum, "%1 " + channel.unit);
		}
	}
	else
	{
		InfoEntry* ie = dynamic_cast<InfoEntry*>(m_tagInfos.value(info.typeString()));
		if(ie == 0)
			return;

		ie->setValue(info.percent());
		for(int i = 0; i < info.getChannels().size(); i++)
		{ 
			ie->setChildValue(info.getChannels().at(i).name, info.getChannels().at(i).value);
		}
	}
}

void TagEntry::handleInformation( const MEMORY_Information& info )
{
	QString uniqueName = info.typeString() + " @ " + info.memBankName() + "." + QString::number(info.memAddr());
	if(!m_tagInfos.contains(uniqueName))
	{
		emit beginInsertRows( this, m_children.size(), m_children.size());

		InfoEntry* i = new InfoEntry(this, uniqueName, info.memData().toHex());

		connectChild(i);

		m_children.append(i);
		m_tagInfos.insert(uniqueName, i);
		emit endInsertRows();
		emit requestToExpand(this);
	}
	else
	{
		InfoEntry* ie = dynamic_cast<InfoEntry*>(m_tagInfos.value(uniqueName));
		if(ie == 0)
			return;

		ie->setValue(info.memData().toHex());
	}
}

void TagEntry::handleInformation( const TRIGGER_Information& info )
{
	m_triggerTimer->start();
	m_triggerSource = info.triggerName();
	m_triggered = true;
	emit dataChanged(this, COL_ID, COL_State);
}

void TagEntry::handleInformation( const FREQUENCY_Information& info )
{
	InfoEntry* infoEntry = 0;

	if(!m_tagInfos.contains(info.typeString()))
	{
		emit beginInsertRows( this, m_children.size(), m_children.size());

		infoEntry = new InfoEntry(this, info.typeString(), "");

		connectChild(infoEntry);

		m_children.append(infoEntry);
		m_tagInfos.insert(info.typeString(), infoEntry);
		emit endInsertRows();
		emit requestToExpand(this);
		emit requestToExpand(infoEntry);
	}
	else
	{
		infoEntry = dynamic_cast<InfoEntry*>(m_tagInfos.value(info.typeString()));
	}

	if(infoEntry == 0)
		return;

	if(!m_frequencyCount.keys().contains(info.frequency()))
		m_frequencyCount.insert(info.frequency(), 0);

	m_frequencyCount[info.frequency()]++;

	foreach(ulong frequ, m_frequencyCount.keys())
	{
		quint32 perc = ((double)m_frequencyCount.value(frequ) / (double)m_readCount) * 100;

		QString freqStr = QString::number((double)frequ/1000) + " MHz";

		if(! infoEntry->hasChild(freqStr) )
			infoEntry->addChild(freqStr, perc, 0, 100, "%1 %");
		else
			infoEntry->setChildValue(freqStr, perc);
	}
}

void TagEntry::handleInformation( const STATE_Information& info )
{
	if(!m_tagInfos.contains(info.typeString()))
	{
		emit beginInsertRows( this, m_children.size(), m_children.size());

		InfoEntry* i = new InfoEntry(this, info.typeString(), info.stateName() + QString(" (%1)").arg(info.state(), 4, 16, QChar('0')));

		connectChild(i);

		m_children.append(i);
		m_tagInfos.insert(info.typeString(), i);
		emit endInsertRows();
		emit requestToExpand(this);
	}
	else
	{
		InfoEntry* ie = dynamic_cast<InfoEntry*>(m_tagInfos.value(info.typeString()));
		if(ie == 0)
			return;

		ie->setValue(info.stateName() + QString(" (%1)").arg(info.state(), 4, 16, QChar('0')));
	}
}

void TagEntry::handleInformation( const BATTERY_Information& info )
{
	if(!m_tagInfos.contains(info.typeString()))
	{
		emit beginInsertRows( this, m_children.size(), m_children.size());

		InfoEntry* i = new InfoEntry(this, info.typeString(), info.battery(), 0, 100, "%1 %");

		connectChild(i);

		m_children.append(i);
		m_tagInfos.insert(info.typeString(), i);
		emit endInsertRows();
		emit requestToExpand(this);
	}
	else
	{
		InfoEntry* ie = dynamic_cast<InfoEntry*>(m_tagInfos.value(info.typeString()));
		if(ie == 0)
			return;

		ie->setValue(info.battery());
	}
}

void TagEntry::handleInformation( const HANDLE_Information& info )
{
	if(!m_tagInfos.contains(info.typeString()))
	{
		emit beginInsertRows( this, m_children.size(), m_children.size());

		InfoEntry* i = new InfoEntry(this, info.typeString(), info.handle().toHex());

		connectChild(i);

		m_children.append(i);
		m_tagInfos.insert(info.typeString(), i);
		emit endInsertRows();
		emit requestToExpand(this);
	}
	else
	{
		InfoEntry* ie = dynamic_cast<InfoEntry*>(m_tagInfos.value(info.typeString()));
		if(ie == 0)
			return;

		ie->setValue(info.handle().toHex());
	}
}

void TagEntry::handleInformation( const PC_Information& info )
{
	if(!m_tagInfos.contains(info.typeString()))
	{
		emit beginInsertRows( this, m_children.size(), m_children.size());

		InfoEntry* i = new InfoEntry(this, info.typeString(), info.pc().toHex());

		connectChild(i);

		m_children.append(i);
		m_tagInfos.insert(info.typeString(), i);
		emit endInsertRows();
		emit requestToExpand(this);
	}
	else
	{
		InfoEntry* ie = dynamic_cast<InfoEntry*>(m_tagInfos.value(info.typeString()));
		if(ie == 0)
			return;

		ie->setValue(info.pc().toHex());
	}
}

void TagEntry::handleInformation( const MESSAGEID_Information& info )
{
	if(!m_tagInfos.contains(info.typeString()))
	{
		emit beginInsertRows( this, m_children.size(), m_children.size());

		InfoEntry* i = new InfoEntry(this, info.typeString(), QString::number(info.messageId()));

		connectChild(i);

		m_children.append(i);
		m_tagInfos.insert(info.typeString(), i);
		emit endInsertRows();
		emit requestToExpand(this);
	}
	else
	{
		InfoEntry* ie = dynamic_cast<InfoEntry*>(m_tagInfos.value(info.typeString()));
		if(ie == 0)
			return;

		ie->setValue(QString::number(info.messageId()));
	}
}

void TagEntry::handleInformation( const RN16_Information& info )
{
    if(!m_tagInfos.contains(info.typeString()))
    {
        emit beginInsertRows( this, m_children.size(), m_children.size());

        InfoEntry* i = new InfoEntry(this, info.typeString(), info.rn16().toHex());

        connectChild(i);

        m_children.append(i);
        m_tagInfos.insert(info.typeString(), i);
        emit endInsertRows();
        emit requestToExpand(this);
    }
    else
    {
        InfoEntry* ie = dynamic_cast<InfoEntry*>(m_tagInfos.value(info.typeString()));
        if(ie == 0)
            return;

        ie->setValue(info.rn16().toHex());
    }
}

void TagEntry::handleInformation(const NXP_BRANDID_Information &info)
{
    if(!m_tagInfos.contains(info.typeString()))
    {
        emit beginInsertRows( this, m_children.size(), m_children.size());

        InfoEntry* i = new InfoEntry(this, info.typeString(), info.brandId().toHex().toUpper());
        i->setDecorator(":/icons/nxp");

        connectChild(i);

        m_children.append(i);
        m_tagInfos.insert(info.typeString(), i);
        emit endInsertRows();
        emit requestToExpand(this);
    }
    else
    {
        InfoEntry* ie = dynamic_cast<InfoEntry*>(m_tagInfos.value(info.typeString()));
        if(ie == 0)
            return;

        ie->setValue(info.brandId().toHex().toUpper());
    }
}

void TagEntry::handleInformation( const GENERIC_Information& info )
{
	QString tagInfoName = info.typeString() + "_" + QString::number(info.infoId());
	if(!m_tagInfos.contains(tagInfoName))
	{
		emit beginInsertRows( this, m_children.size(), m_children.size());

		InfoEntry* i = new InfoEntry(this, info.description(), info.value());

		connectChild(i);

		m_children.append(i);
		m_tagInfos.insert(tagInfoName, i);
		emit endInsertRows();
		emit requestToExpand(this);
	}
	else
	{
		InfoEntry* ie = dynamic_cast<InfoEntry*>(m_tagInfos.value(tagInfoName));
		if(ie == 0)
			return;

		ie->setValue(info.value());
	}
}


void TagEntry::removeFrequencyEntries()
{
	if(!m_tagInfos.contains("FREQUENCY"))
		return;

	TreeItem* t = m_tagInfos.value("FREQUENCY");

	if(dynamic_cast<InfoEntry*>(t) != 0)
		dynamic_cast<InfoEntry*>(t)->removeAllChildren();
}

