/*
 * ReaderEntry.cpp
 *
 *  Created on: 22.10.2009
 *      Author: stefan.detter
 */

#include "ReaderEntry.h"
#include "TagEntry.h"

#include "AntennaEntry.h"
#include "TagEntry.h"

#include <QSet>

#include <reader/QrfeRfePurReader>

ReaderEntry::ReaderEntry(TreeItem* parent, Reader* r)
	: TreeItem(parent)
	, m_reader(r)
    , QrfeTraceModule("ReaderEntry")
{
	m_readerId 			= m_reader->readerId();
	m_isOffline		 	= false;
	m_readTagCount 		= 0;
	m_readRate 			= 0.0;
	m_maxReadRate		= 0.0;
	m_scanRunning 		= false;
	m_attachedTime 		= QDateTime::currentDateTime();

    m_heartBeatCounter = 0;
	m_heartBeatDisplayTimer = new QTimer(this);
	m_heartBeatDisplayTimer->setInterval(100);
	m_heartBeatDisplayTimer->setSingleShot(true);
	connect(m_heartBeatDisplayTimer, SIGNAL(timeout()), this, SLOT(heartBeatDisplayTimer()) );
	m_onlineUnboldIcon = QPixmap(":/icons/online_small");
	m_onlineBoldIcon = QPixmap(":/icons/online_big");
	m_onlineIcon = &m_onlineUnboldIcon;


	connect(m_reader, SIGNAL(heartBeat()), this, SLOT(heartBeat()));
    connect(m_reader, SIGNAL(notification ( const uchar, const QString&, const QVariant&)),
            this, 	    SLOT(notification ( const uchar, const QString&, const QVariant&)));
    connect(m_reader, SIGNAL(signatureChanged()), this, SLOT(readerChangedSignature()));
    connect(m_reader, SIGNAL(tagTypeChanged(int)), this, SLOT(tagTypeChanged()));
    connect(m_reader, SIGNAL(changedAction(int)), this, SLOT(readerChangedAction()));
	connect(m_reader, SIGNAL(changedState(int)), this, SLOT(readerChangedState()));
	connect(m_reader, SIGNAL(changedStatusRegister(qulonglong)), this, SLOT(readerChangedStatusRegister()));
	connect(m_reader, SIGNAL(changedGpioValues(ulong)), this, SLOT(readerChangedGpioValues()));
	connect(m_reader, SIGNAL(changedRssiEnable(bool,uchar,const QStringList&, int, int)),
			this, 		SLOT(changedRssiEnable(bool,uchar,const QStringList&, int, int)));
	connect(m_reader, SIGNAL(changedReadFrequencyEnable(bool)),
			this, 		SLOT(changedReadFrequencyEnable(bool)));
	connect(m_reader, SIGNAL(changedReaderName(const QString&)),
			this, 		SLOT(readerNameChanged(const QString&)));
	connect(m_reader, SIGNAL(changedReaderLocation(const QString&)),
			this, 		SLOT(readerLocationChanged(const QString&)));

    connect(m_reader, SIGNAL(inventoryAboutToStart()), this, SLOT(inventoryAboutToStart()));
    connect(m_reader, SIGNAL(inventoryStarted()), this, SLOT(inventoryStarted()));
    connect(m_reader, SIGNAL(inventoryStoped()), this, SLOT(inventoryStoped()));
    connect(m_reader, SIGNAL(inventoryPaused()), this, SLOT(inventoryPaused()));
    connect(m_reader, SIGNAL(inventoryContinued()), this, SLOT(inventoryContinued()));
    connect(m_reader, SIGNAL( tagEvent ( const TagEvent& )),
			this, 	    SLOT( tagEvent ( const TagEvent& )));

    readSignature();
}

ReaderEntry::~ReaderEntry()
{
    m_reader->destroy();
}

void ReaderEntry::readSignature()
{
    removeAllChildren();

    m_reader->readSettings();

    if(/*m_reader->antennaCount() > 1 && */m_reader->currentSystem() != QrfeGlobal::BOOTLOADER)
    {
		emit beginInsertRows(this, 0, m_reader->antennaCount()-1);
        
		m_usingAntennas = true;
        for(int i = 0; i < m_reader->antennaCount(); i++)
            appendAntenna(i+1);
		
		emit endInsertRows();
    }
    else
        m_usingAntennas = false;

    prepareInformation();

    if(m_reader->currentAction() == QrfeGlobal::ACTION_SCANNING)
        inventoryStarted();
}


QString	ReaderEntry::readerName() const
{
	return m_reader->readerName();
}

QString ReaderEntry::readerId() const
{
	return m_readerId;
}

QString ReaderEntry::readerSerial() const
{
	return m_reader->serialNumber();
}

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

QList<QString> ReaderEntry::tags() const
{
	return m_tagIndex.keys();
}

bool ReaderEntry::isOffline()
{
	return m_reader->currentState() == QrfeGlobal::STATE_OFFLINE;
}

bool ReaderEntry::isInventoryRunning()
{
	return m_scanRunning;
}


AntennaEntry* ReaderEntry::anntenna ( const uint antennaId ) const
{
	return (AntennaEntry*)m_antennaIndex.at(antennaId);
}

AntennaEntry* ReaderEntry::appendAntenna ( const uint antennaId )
{
	AntennaEntry* t = new AntennaEntry(this, antennaId);
	m_children.append(t);
	m_antennaIndex.insert(antennaId-1, t);

	connectChild(t);

	connect(t, 		SIGNAL(updateInformation()),
			this, 	  SLOT(updateInformation()) );

	updateInformation();

	return t;
}


int	ReaderEntry::tagCount() const
{
	int count = 0;
	if(m_usingAntennas)
	{
        QSet<QString> tagIds;
		foreach(AntennaEntry* e, m_antennaIndex)
            tagIds += e->uniqueTagIds();

        count += tagIds.count();
		count += m_tagIndex.size();
	}
	else
	{
		count = m_tagIndex.size();
	}
	return count;
}

QSet<QString> ReaderEntry::uniqueTagIds() const
{
    QSet<QString> tagIds;
    foreach(AntennaEntry* e, m_antennaIndex)
        tagIds += e->uniqueTagIds();

    return tagIds.unite(m_tagIndex.keys().toSet());
}

TagEntry* ReaderEntry::tag ( const QString& tagId ) const
{
	return (TagEntry*)m_tagIndex.value(tagId);
}

TagEntry* ReaderEntry::appendTag ( const QString& type, const QString& tagId, const QString &visualTagId)
{
	emit beginInsertRows(this, m_tagIndex.size(), m_tagIndex.size() );
    TagEntry* t = new TagEntry(this, this, 0, type, tagId, visualTagId);
	m_children.append(t);
	m_tagIndex.insert(tagId, t);

	connectChild(t);

	connect(t, 		SIGNAL(shouldBeRemoved(const QString&)),
			this, 	SLOT(removeTag(const QString&)) );

	emit endInsertRows();

	updateInformation();

	return t;
}

void ReaderEntry::removeTag ( const QString& tagId )
{
	if(m_tagIndex.contains(tagId))
	{
		TagEntry* t = dynamic_cast<TagEntry*>(m_tagIndex.value(tagId));

		if(t == 0)
			return;

		emit aboutToBeRemoved(t);

		emit beginRemoveRows( this, m_children.indexOf(t), m_children.indexOf(t) );
		m_children.removeAt(m_children.indexOf(t));
		m_tagIndex.remove(tagId);
		emit endRemoveRows();

		delete t;

		updateInformation();
	}
}

void ReaderEntry::removeAllChildren ( )
{
    foreach(TreeItem* ti, m_children)
    {
        emit aboutToBeRemoved(ti);
        ti->deleteLater();
    }

    emit beginRemoveRows( this, 0, m_children.size()-1 );
    m_children.clear();
    emit endRemoveRows();

    m_tagIndex.clear();
    m_antennaIndex.clear();

    updateInformation();
}



void ReaderEntry::updateDynamicInfo()
{
	if(!isOffline() && m_scanRunning == true)
	{
        quint64 time = m_timeRunning + m_lastStarted.elapsed();

        if(time == 0)
            time = 1;

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

		if(m_readRate > m_maxReadRate)
			m_maxReadRate = m_readRate;

		emit dataChanged(this, COL_ReadCount, COL_ReadRate);

		updateInformation();

		TreeItem::updateDynamicInfo();
	}
}

void ReaderEntry::clearTags()
{
	foreach(QString tagId, m_tagIndex.keys())
	{
		removeTag(tagId);
	}

	TreeItem::clearTags();
}


void ReaderEntry::heartBeat()
{
    if(++m_heartBeatCounter >= 4)
    {
        m_heartBeatCounter = 0;
        m_onlineIcon = &m_onlineBoldIcon;
        emit dataChanged(this, COL_Action, COL_State);
        m_heartBeatDisplayTimer->start();
    }
}

void ReaderEntry::notification (const uchar id, const QString& /*idStr*/, const QVariant & value)
{
    if(qobject_cast<QrfeRfePurReader*>(m_reader->readerInterface()) != 0)
    {
        if(id == QrfeRfePurReader::QrfeRfePurReader::NOTIFICATION_ID_ANTENNA_CHANNEL_CHANGED)
        {
            uchar antennaId = value.toUInt();

            if( antennaId > (uint)m_antennaIndex.size() )
                return;

            for(int i = 0; i < m_antennaIndex.size(); i++)
            {
                AntennaEntry* e = m_antennaIndex.at(i);
                e->setActive((i == (antennaId-1) ));
            }
        }
        else if(id == QrfeRfePurReader::QrfeRfePurReader::NOTIFICATION_ID_ANTENNA_RFP_VALUE_MEASURED)
        {
            QDataStream stream(value.toByteArray());
            uchar antennaId;
            int value;

            stream >> antennaId;
            stream >> value;

            if( antennaId > (uint)m_antennaIndex.size() )
                return;

            AntennaEntry* e = m_antennaIndex.at(antennaId-1);
            if(e != 0)
                e->setReflectedPowerValue(value);
        }
        else if(id == QrfeRfePurReader::QrfeRfePurReader::NOTIFICATION_ID_ANTENNA_RFP_ERROR)
        {
            QDataStream stream(value.toByteArray());
            uchar antennaId;
            bool error;
            int value;

            stream >> antennaId;
            stream >> error;
            stream >> value;

            if( antennaId > (uint)m_antennaIndex.size() )
                return;

            AntennaEntry* e = m_antennaIndex.at(antennaId-1);
            if(e != 0)
                e->setReflectedPowerError(error, value);
        }
        else if(id == QrfeRfePurReader::QrfeRfePurReader::NOTIFICATION_ID_DEBUG_CODES)
        {
            QDataStream stream(value.toByteArray());
            uchar id;
            ushort code;
            QByteArray info;

            stream >> id;
            stream >> code;
            stream >> info;

            error(readerId() + " - DEBUG_CODE: " + QString("%1").arg(id, 2, 16) + "-" + QString::number(code) + "-" + info.toHex());
        }
    }


	return;
	// TODO
//	if(!m_informationKeys.contains(id)){
//		emit informationBeginInsertRow(m_informationKeys.size());
//		m_informationKeys.append(id);
//		m_informationValues.append(value);
//		emit informationEndInsertRow();
//	}
//
//	int index = m_informationKeys.indexOf(id);
//	m_informationValues.replace(index, value);
//	emit informationChanged(index, index);
}

void ReaderEntry::tagTypeChanged ( )
{
	clearTags();
	m_informationValues.replace(READER_ROW_TagType, m_reader->tagTypeString());
	emit informationChanged(READER_ROW_TagType, READER_ROW_TagType);
	emit dataChanged(this, COL_TagType, COL_TagType);
}

void ReaderEntry::readerNameChanged( const QString& name )
{
	if(m_informationValues.size() == 0)
		return;
	m_informationValues.replace(READER_ROW_Name, name);
	emit informationChanged(READER_ROW_Name, READER_ROW_Name);
	emit dataChanged(this, COL_ID, COL_ID);
}

void ReaderEntry::readerLocationChanged( const QString& location )
{
	if(m_informationValues.size() == 0)
		return;
	m_informationValues.replace(READER_ROW_Location, location);
	emit informationChanged(READER_ROW_Location, READER_ROW_Location);
	emit dataChanged(this, COL_ID, COL_ID);
}

void ReaderEntry::readerChangedAction ( )
{
	if(m_informationValues.size() == 0)
		return;
	m_informationValues.replace(READER_ROW_Action, m_reader->currentActionString());
	emit informationChanged(READER_ROW_Action, READER_ROW_Action);
	emit dataChanged(this, COL_Action, COL_State);
}

void ReaderEntry::readerChangedState ( )
{
	if(m_informationValues.size() == 0)
		return;
	m_informationValues.replace(READER_ROW_State, m_reader->currentStateString());
	emit informationChanged(READER_ROW_State, READER_ROW_State);
	emit dataChanged(this, COL_Action, COL_State);

	if(isOffline()){
        //clearTags();
        removeAllChildren();
		m_isOffline = true;
	}
}

void ReaderEntry::readerChangedStatusRegister ( )
{
	if(m_informationValues.size() == 0)
		return;
	m_informationValues.replace(READER_ROW_StatusRegister, QString("0x%1").arg(m_reader->statusRegister(), 16, 16, QChar('0')));
	emit informationChanged(READER_ROW_StatusRegister, READER_ROW_StatusRegister);
}

void ReaderEntry::readerChangedGpioValues( )
{
	if(m_informationValues.size() == 0)
		return;
	m_informationValues.replace(READER_ROW_GPIOPins, QString("0x%1").arg(m_reader->gpioValues(), 8, 16, QChar('0')));
    emit informationChanged(READER_ROW_GPIOPins, READER_ROW_GPIOPins);
}


void ReaderEntry::readerChangedSignature()
{
    readSignature();
}

void ReaderEntry::changedRssiEnable(bool on, uchar /*rssiChildCount*/, const QStringList& /*rssiChildNames*/, int /*rssiChildMin*/, int /*rssiChildMax*/)
{
	m_rssiOn = on;

	if(m_rssiOn == false)
	{
		foreach(TreeItem* t, m_children)
		{
			if(dynamic_cast<TagEntry*>(t) != 0)
				dynamic_cast<TagEntry*>(t)->removeRSSI();
			if(dynamic_cast<AntennaEntry*>(t) != 0)
				dynamic_cast<AntennaEntry*>(t)->removeRSSI();
		}
	}
}

void ReaderEntry::changedReadFrequencyEnable(bool on)
{
	m_readFrequencyOn = on;

	if(m_readFrequencyOn == false)
	{
		foreach(TreeItem* t, m_children)
		{
			if(dynamic_cast<TagEntry*>(t) != 0)
				dynamic_cast<TagEntry*>(t)->removeReadFrequency();
			if(dynamic_cast<AntennaEntry*>(t) != 0)
				dynamic_cast<AntennaEntry*>(t)->removeReadFrequency();
		}
	}
}

void ReaderEntry::inventoryAboutToStart()
{
    TreeItem::inventoryAboutToStart();
}

void ReaderEntry::inventoryStarted()
{
    m_timeRunning = 0;
    m_maxReadRate = 0;
    m_readTagCount = 0;

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

void ReaderEntry::inventoryStoped()
{
    inventoryPaused();
	TreeItem::inventoryStoped();
}

void ReaderEntry::inventoryContinued()
{
    m_lastStarted.start();
    m_scanRunning = true;

    emit dataChanged(this, COL_ID, COL_State);

    TreeItem::inventoryContinued();
}

void ReaderEntry::inventoryPaused()
{
    m_timeRunning += m_lastStarted.elapsed();
    m_scanRunning = false;

    emit dataChanged(this, COL_ID, COL_State);
    emit informationChanged(READER_ROW_HEADER, READER_ROW_HEADER);

    TreeItem::inventoryPaused();
}

void ReaderEntry::tagEvent(const TagEvent& event)
{
	TagEntry* t;
    AntennaEntry* antennaEntry = 0;

    if(m_usingAntennas)
	{
        if(event.hasInformation(ANTENNA_Information::id))
        {
            QVariant v = event.getInformation(ANTENNA_Information::id);

            if(!v.canConvert<ANTENNA_Information>())
                return;

            ANTENNA_Information i = v.value<ANTENNA_Information>();

            if( i.antennaId() > (uint)m_antennaIndex.size() )
                return;

            antennaEntry = m_antennaIndex.at(i.antennaId()-1);
        }
        else if(m_antennaIndex.size() == 1)
        {
            antennaEntry = m_antennaIndex.at(0);
        }
    }

    if(antennaEntry != 0)
    {
        if( antennaEntry->containsTag(event.tagId()) )
		{
            t = antennaEntry->tag(event.tagId());
		}
		else
		{
            t = antennaEntry->appendTag(event.tagType(), event.tagId(), event.visualTagId());
		}
	}
	else
	{
		if( m_tagIndex.contains(event.tagId()) )
		{
			t = tag(event.tagId());
		}
		else
		{
            t = appendTag(event.tagType(), event.tagId(), event.visualTagId());
		}
	}

	if(t == 0)
		return;

	t->update(event);
	m_readTagCount += event.readCount();
}

void ReaderEntry::heartBeatDisplayTimer()
{
	m_onlineIcon = &m_onlineUnboldIcon;
	emit dataChanged(this, COL_Action, COL_State);
}
