/*
 * EPC_Defines.cpp
 *
 *  Created on: 06.05.2009
 *      Author: stefan.detter
 */

#include "../../inc/epc/EPC_Defines.h"

#define EPC_TID_CLASS_ID_1		0xE0
#define EPC_TID_CLASS_ID_2		0xE2


typedef enum {
	Impinj = 0x01,
	TI = 0x02,
	Alien = 0x03,
	Intelleflex = 0x04,
	Atmel = 0x05,
	NXP = 0x06,
	ST = 0x07,
	EP = 0x08,
	Motorola = 0x09,
	Sentech = 0x0A,
	EM = 0x0B,
	Renesas = 0x0C,
	Mstar = 0x0D,
	Tyco = 0x0E,
	Quanray = 0x0F,
	Fujitsu = 0x10,
	LSIS = 0x11,
	CAEN_RFID = 0x12,
	Impinj_Monza4 = 0x801,
} MDID;

class TAG_MODEL_INFO {
public:
	TAG_MODEL_INFO()
		: m_modelName("")
		, m_memorySize(0)
		, m_hasSerial(false)
		, m_serialStart(0)
		, m_serialStop(0)
	{}

	TAG_MODEL_INFO(QString modelName, uint memorySize)
		: m_modelName(modelName)
		, m_memorySize(memorySize)
		, m_hasSerial(false)
		, m_serialStart(0)
		, m_serialStop(0)
	{}

	TAG_MODEL_INFO(QString modelName, uint memorySize, ushort serialStart, ushort serialStop)
		: m_modelName(modelName)
		, m_memorySize(memorySize)
		, m_hasSerial(true)
		, m_serialStart(serialStart)
		, m_serialStop(serialStop)
	{}

	TAG_MODEL_INFO(const TAG_MODEL_INFO& other)
		: m_modelName(other.m_modelName)
		, m_memorySize(other.m_memorySize)
		, m_hasSerial(other.m_hasSerial)
		, m_serialStart(other.m_serialStart)
		, m_serialStop(other.m_serialStop)
	{}

	QString 	modelName() const 	{return m_modelName;};
	uint 		memorySize() const 	{return m_memorySize;};
	bool 		hasSerial() const	{return m_hasSerial;};
	ushort 		serialStart() const	{return m_serialStart;};
	ushort 		serialStop() const 	{return m_serialStop;};


private:
	QString 	m_modelName;
	uint 		m_memorySize;
	bool 		m_hasSerial;
	ushort 		m_serialStart;
	ushort 		m_serialStop;
};


QMap<uchar, QPair<QString, TAG_MODEL_INFO> > 	g_class1_epcManufacturer;
QMap<uint,QString>								g_class2_epcManufacturer;
QMap<QString, QMap<uint, TAG_MODEL_INFO> >		g_tagInfo;


QString EPCGlobal::memBankToString( const uint memBank )
{
	switch(memBank)
	{
	case 0:
		return "Reserved";
	case 1:
		return "EPC";
	case 2:
		return "TID";
	case 3:
		return "User";
	}

	return "";
}

int EPCGlobal::getTIDInfoSize ( const QByteArray& tid )
{
    if(tid.size() >= 1)
    {
        if((uchar)tid.at(0) == EPC_TID_CLASS_ID_1)
            return 8;
        else if((uchar)tid.at(0) == EPC_TID_CLASS_ID_2)
        {
            if(tid.size() >= 4)
            {
                EPCGlobal::TID_Info tidInfo;
                quint16 manufacturer = 0;
                quint64 modelNr = 0;

                manufacturer |= ((quint16)(uchar)tid.at(1)) << 4;
                manufacturer |= ((quint16)((uchar)tid.at(2) & 0xF0)) >> 4;
                modelNr += ((quint16) ((uchar)tid.at(2) & 0x0F)) << 8;
                modelNr += (uchar)tid.at(3);

                if(g_tagInfo.contains(tidInfo.manufacturer) && g_tagInfo.value(tidInfo.manufacturer).contains(modelNr))
                {
                    const TAG_MODEL_INFO& info = g_tagInfo.value(tidInfo.manufacturer).value(modelNr);

                    if(info.hasSerial())
                    {
                        return (info.serialStop() + 1) / 8;
                    }

                }
            }

        }
    }

    return 4;
}

EPCGlobal::TID_Info EPCGlobal::getTIDInfo ( const QByteArray& tid )
{
	EPCGlobal::TID_Info tidInfo;
	quint16 manufacturer = 0;
	quint64 modelNr = 0;

	tidInfo.manufacturer 	= "";
	tidInfo.model			= "";
	tidInfo.userMemSize 	= -1;
	tidInfo.hasSerialNumber = false;
	tidInfo.serialNumber 	= 0;

	if((uchar)tid.at(0) == EPC_TID_CLASS_ID_1)
	{
		manufacturer = (quint8)tid.at(1);
		modelNr += ((quint64)(uchar)tid.at(2)) << 40;
		modelNr += ((quint64)(uchar)tid.at(3)) << 32;
		modelNr += ((quint64)(uchar)tid.at(4)) << 24;
		modelNr += ((quint64)(uchar)tid.at(5)) << 16;
		modelNr += ((quint64)(uchar)tid.at(6)) << 8;
		modelNr += ((quint64)(uchar)tid.at(7)) << 0;

		tidInfo.manufacturer = QString("0x%1").arg(manufacturer, 4, 16, QChar('0'));
		if(g_class1_epcManufacturer.contains(manufacturer)){
			tidInfo.manufacturer = g_class1_epcManufacturer.value(manufacturer).first;
			tidInfo.model = g_class1_epcManufacturer.value(manufacturer).second.modelName();
			tidInfo.userMemSize = g_class1_epcManufacturer.value(manufacturer).second.memorySize();
		}

		tidInfo.hasSerialNumber = true;
		tidInfo.serialNumber = modelNr;
	}
	else if((uchar)tid.at(0) == EPC_TID_CLASS_ID_2)
	{
		manufacturer |= ((quint16)(uchar)tid.at(1)) << 4;
		manufacturer |= ((quint16)((uchar)tid.at(2) & 0xF0)) >> 4;
		modelNr += ((quint16) ((uchar)tid.at(2) & 0x0F)) << 8;
		modelNr += (uchar)tid.at(3);

		tidInfo.manufacturer = QString("0x%1").arg(manufacturer, 4, 16, QChar('0'));
		if(g_class2_epcManufacturer.contains(manufacturer & 0x7FF))
			tidInfo.manufacturer = g_class2_epcManufacturer.value(manufacturer & 0x7FF);

		tidInfo.serialNumber = 0;
		tidInfo.hasSerialNumber = false;

		if(g_tagInfo.contains(tidInfo.manufacturer) && g_tagInfo.value(tidInfo.manufacturer).contains(modelNr)){
			const TAG_MODEL_INFO& info = g_tagInfo.value(tidInfo.manufacturer).value(modelNr);
			tidInfo.model = info.modelName();
			tidInfo.userMemSize = info.memorySize();

			if(info.hasSerial())
			{
				for(uchar pos = info.serialStart(); pos <= info.serialStop(); pos++)
				{
					uchar byte = pos/8;
					uchar bit  = pos%8;
					tidInfo.serialNumber = (tidInfo.serialNumber << 1);
					tidInfo.serialNumber |=  ((tid.at(byte) >> (7-bit)) & 0x01);
				}
				tidInfo.hasSerialNumber = true;
			}
		}
		else{
			tidInfo.model = QString("0x%1").arg(modelNr, 6, 16, QChar('0'));
			tidInfo.userMemSize = -1;
		}
	}

	return tidInfo;
}


static void initEPC_Constants()
{
	g_class1_epcManufacturer.insert( 0x36, QPair<QString, TAG_MODEL_INFO>("Austria Microsystems", TAG_MODEL_INFO("SL900A", 1052)) );

	g_class2_epcManufacturer.insert( 0x000,	"Unknown" );
	g_class2_epcManufacturer.insert( 0x001,	"Impinj" );
	g_class2_epcManufacturer.insert( 0x002,	"Texas Instruments " );
	g_class2_epcManufacturer.insert( 0x003,	"Alien Technology" );
	g_class2_epcManufacturer.insert( 0x004,	"Intelleflex" );
	g_class2_epcManufacturer.insert( 0x005,	"Atmel" );
	g_class2_epcManufacturer.insert( 0x006,	"NXP" );
	g_class2_epcManufacturer.insert( 0x007,	"ST Microelectronics" );
	g_class2_epcManufacturer.insert( 0x008,	"EP Microelectronics" );
	g_class2_epcManufacturer.insert( 0x009,	"Motorola" );
	g_class2_epcManufacturer.insert( 0x00A,	"Sentech Snd Bhd" );
	g_class2_epcManufacturer.insert( 0x00B,	"EM Microelectronics" );
	g_class2_epcManufacturer.insert( 0x00C,	"Renesas Technology Corp." );
	g_class2_epcManufacturer.insert( 0x00D,	"Mstar" );
	g_class2_epcManufacturer.insert( 0x00E,	"Tyco International" );
	g_class2_epcManufacturer.insert( 0x00F,	"Quanray Electronics" );
	g_class2_epcManufacturer.insert( 0x010,	"Fujitsu" );
	g_class2_epcManufacturer.insert( 0x011,	"LSIS" );
	g_class2_epcManufacturer.insert( 0x012,	"CAEN RFID srl");
	g_class2_epcManufacturer.insert( 0x013,	"Productivity Engineering Gesellschaft fuer IC Design mbH");
	g_class2_epcManufacturer.insert( 0x014,	"Federal Electric Corp.");
	g_class2_epcManufacturer.insert( 0x015,	"ON Semiconductor");
	g_class2_epcManufacturer.insert( 0x016,	"Ramtron");
	g_class2_epcManufacturer.insert( 0x017,	"Tego");
	g_class2_epcManufacturer.insert( 0x018,	"Ceitec S.A.");
	g_class2_epcManufacturer.insert( 0x019,	"CPA Wernher von Braun");
	g_class2_epcManufacturer.insert( 0x01A,	"TransCore");
	g_class2_epcManufacturer.insert( 0x01B,	"Nationz");
	g_class2_epcManufacturer.insert( 0x01C,	"Invengo");
	g_class2_epcManufacturer.insert( 0x01D,	"Kiloway");
	g_class2_epcManufacturer.insert( 0x01E,	"Longjing Microelectronics Co. Ltd.");
	g_class2_epcManufacturer.insert( 0x01F,	"Chipus Microelectronics");
	g_class2_epcManufacturer.insert( 0x020,	"ORIDAO");
	g_class2_epcManufacturer.insert( 0x021,	"Maintag");
	g_class2_epcManufacturer.insert( 0x022,	"Yangzhou Daoyuan Microelectronics Co. Ltd	");
	g_class2_epcManufacturer.insert( 0x023,	"Gate Elektronik");


	QMap<uint,TAG_MODEL_INFO> models;

	g_tagInfo.insert("Unknown", models);

	{
		models.insert(0x050, TAG_MODEL_INFO("Monza", 0));
		models.insert(0x093, TAG_MODEL_INFO("Monza 3", 0));
		models.insert(0x105, TAG_MODEL_INFO("Monza 4QT", 512));
		models.insert(0x104, TAG_MODEL_INFO("Monza 4U", 0));
		models.insert(0x10C, TAG_MODEL_INFO("Monza 4E", 128, 0x30, 0x5F));
		models.insert(0x100, TAG_MODEL_INFO("Monza 4D", 16, 0x30, 0x5F));
		models.insert(0x130, TAG_MODEL_INFO("Monza 5", 0, 0x30, 0x5F));
		g_tagInfo.insert("Impinj", models);
		models.clear();
	}

	{
		models.insert(0x044, TAG_MODEL_INFO("4325 Uncalibrated", 384));
		models.insert(0x040, TAG_MODEL_INFO("4325 Calibrated", 384));
		g_tagInfo.insert("EM Microelectronics", models);
		models.clear();
	}

	{
		models.insert(0x001, TAG_MODEL_INFO("UCODE EPC G2", 28));
		models.insert(0x003, TAG_MODEL_INFO("UCODE EPC G2XM", 64, 0x20, 0x3F));
		models.insert(0x004, TAG_MODEL_INFO("UCODE EPC G2XL", 64, 0x20, 0x3F));

        models.insert(0x805, TAG_MODEL_INFO("UCODE G2iL", 0, 0x20, 0x3F));
        models.insert(0x806, TAG_MODEL_INFO("UCODE G2iL", 0, 0x20, 0x3F));
        models.insert(0x807, TAG_MODEL_INFO("UCODE G2iL+", 0, 0x20, 0x3F));
        models.insert(0x906, TAG_MODEL_INFO("UCODE G2iL", 0, 0x20, 0x3F));
        models.insert(0x907, TAG_MODEL_INFO("UCODE G2iL+", 0, 0x20, 0x3F));
        models.insert(0xB06, TAG_MODEL_INFO("UCODE G2iL", 0, 0x20, 0x3F));
        models.insert(0xB07, TAG_MODEL_INFO("UCODE G2iL+", 0, 0x20, 0x3F));

        models.insert(0x80A, TAG_MODEL_INFO("UCODE G2iM", 64, 0x30, 0x5F));
        models.insert(0x80B, TAG_MODEL_INFO("UCODE G2iM+", 64, 0x30, 0x5F));

        models.insert(0x80D, TAG_MODEL_INFO("UCODE I²C", 416, 0x30, 0x5F));
        models.insert(0x88D, TAG_MODEL_INFO("UCODE I²C", 416, 0x30, 0x5F));
        models.insert(0x98C, TAG_MODEL_INFO("UCODE I²C", 416, 0x30, 0x5F));

        models.insert(0x810, TAG_MODEL_INFO("UCODE 7", 0, 0x30, 0x5F));
        models.insert(0x890, TAG_MODEL_INFO("UCODE 7", 32, 0x30, 0x5F));
        models.insert(0x811, TAG_MODEL_INFO("UCODE 7m", 0, 0x30, 0x5F));
        models.insert(0x891, TAG_MODEL_INFO("UCODE 7m", 32, 0x30, 0x5F));

        models.insert(0x892, TAG_MODEL_INFO("UCODE DNA", 0));
        models.insert(0xB12, TAG_MODEL_INFO("UCODE DNA-City", 0));
        models.insert(0xC12, TAG_MODEL_INFO("UCODE DNA Track", 0));

        models.insert(0xD12, TAG_MODEL_INFO("UCODE 7xm (1k)", 1024, 0x30, 0x5F));
        models.insert(0xF12, TAG_MODEL_INFO("UCODE 7xm (2k)", 2048, 0x30, 0x5F));
        models.insert(0xD92, TAG_MODEL_INFO("UCODE 7xm+", 2048, 0x30, 0x5F));

        models.insert(0x894, TAG_MODEL_INFO("UCODE 8", 0, 0x30, 0x5F));
        models.insert(0x994, TAG_MODEL_INFO("UCODE 8m", 32, 0x30, 0x5F));

        g_tagInfo.insert("NXP", models);
		models.clear();
	}

}

EPCGlobal::Loader EPCGlobal::Loader::loader;

EPCGlobal::Loader::Loader()
{
	initEPC_Constants();
}
