#include "CGen2TagDialog.h"
#include <QMessageBox>
#include <QStandardItemModel>
#include <QRegExp>
#include <QDesktopWidget>

#include <QrfeTagManipulatorInterface>
#include <reader/QrfeRfePurReader>
#include <tag/QrfeGen2Tag>
#include <tag/specific/QrfeNXPTag>

#include <Cache.h>
#include <def.h>
#include <QrfeGuiLibGlobal>

#include <QFileInfo>
#include <QDir>

#include "sub/CGen2PassDialog.h"
#include "sub/CGen2ExplicitReadDialog.h"
#include "sub/CGen2KillDialog.h"
#include "sub/CGen2LockDialog.h"
#include "sub/CGen2SetPasswordDialog.h"
#include "sub/CGen2SetEPCDialog.h"
#include "sub/CGen2StoreText.h"

#include "sub/specific/nxp/CGen2NxpG2iMConfigWord.h"
#include "sub/specific/nxp/CGen2NxpG2iLConfigWord.h"
#include "sub/specific/nxp/CGen2NxpG2iLDialog.h"
#include "sub/specific/nxp/CGen2NxpG2I2CConfigWord.h"
#include "sub/specific/nxp/CGen2NxpG2I2CDialog.h"
#include "sub/specific/nxp/CGen2NxpUcode7ConfigWord.h"
#include "sub/specific/nxp/CGen2NxpUcode8ConfigWord.h"
#include "sub/specific/nxp/CGen2NxpUcodeDNAConfigWord.h"

#include "sub/specific/ams/CGen2AmsSL900aDialog.h"
#include "sub/specific/em/CGen2EM4325Dialog.h"



CGen2TagDialog::CGen2TagDialog(QWidget *parent)
    : QDialog(parent)
	, m_reader(0)
	, m_tag(0)
	, m_antennaId(0)
{
	ui.setupUi(this);
	setInfoEdit(ui.infoEdit);

	m_progressBar			= new QrfeProgressDialog(QrfeGlobal::STATIC, parent);

	m_explicitReadDialog	= new CGen2ExplicitReadDialog(this);
	m_passwordDialog 		= new CGen2PassDialog(this);
	m_killDialog 			= new CGen2KillDialog(this);
	m_lockDialog 			= new CGen2LockDialog(this);
	m_setPasswordDialog 	= new CGen2SetPasswordDialog(this);
	m_setEPCDialog 			= new CGen2SetEPCDialog(this);
	m_storeText 			= new CGen2StoreText(this);

    m_nxpG2iMConfigWordDialog       = new CGen2NxpG2iMConfigWord(this);
    m_nxpG2iLConfigWordDialog       = new CGen2NxpG2iLConfigWord(this);
    m_nxpG2iLDialog                 = new CGen2NxpG2iLDialog(this);
    m_nxpI2CConfigWordDialog        = new CGen2NxpG2I2CConfigWord(this);
    m_nxpG2I2CDialog                = new CGen2NxpG2I2CDialog(this);
    m_nxpUcode7ConfigWordDialog     = new CGen2NxpUcode7ConfigWord(this);
    m_nxpUcode8ConfigWordDialog     = new CGen2NxpUcode8ConfigWord(this);
    m_nxpUcodeDNAConfigWordDialog   = new CGen2NxpUcodeDNAConfigWord(this);

	m_amsSL900ADemo 			= new CGen2AmsSL900aDialog(this);
	m_em4325ADemo 				= new CGen2EM4325Dialog(this);

	m_memoryModel 			= new QStandardItemModel(this);
	m_memoryModel->setColumnCount(8);
	ui.memoryTableView->setModel(m_memoryModel);

	connect(ui.okButton, 					SIGNAL(clicked()),
			this, 							  SLOT(accept()));
	connect(ui.refreshTIDButton, 			SIGNAL(clicked()),
			this, 							  SLOT(getTagInfos()));

	connect(ui.readMemButton, 				SIGNAL(clicked()),
			this, 							  SLOT(readMem()));
	connect(ui.setMemButton, 				SIGNAL(clicked()),
			this, 							  SLOT(setMem()));
	connect(ui.exportMemButton,				SIGNAL(clicked()),
			this, 							  SLOT(exportMem()));

	connect(ui.readButton,	 				SIGNAL(clicked()),
			this, 							  SLOT(read()));
	connect(ui.setEPCButton, 				SIGNAL(clicked()),
			this, 							  SLOT(setEPC()));
	connect(ui.setPasswordButton,			SIGNAL(clicked()),
			this, 							  SLOT(setPassword()));
	connect(ui.lockButton, 					SIGNAL(clicked()),
			this, 							  SLOT(lock()));
	connect(ui.killTagButton, 				SIGNAL(clicked()),
			this, 							  SLOT(killTag()));
	connect(ui.storeTextButton,				SIGNAL(clicked()),
			this, 							  SLOT(storeText()));

	connect(ui.nxpSetReadProtectButton, 	SIGNAL(clicked()),
			this, 							  SLOT(nxpSetReadProtect()));
    connect(ui.nxpConfigWordButton,         SIGNAL(clicked()),
            this, 							  SLOT(nxpConfigWord()));
	connect(ui.nxpG2iLDemoButton, 			SIGNAL(clicked()),
			this, 							  SLOT(nxpG2iLDemo()));
	connect(ui.nxpI2CDemoButton,			SIGNAL(clicked()),
			this, 							  SLOT(nxpI2CDemo()));

	connect(ui.amsSL900ADemoButton,			SIGNAL(clicked()),
			this, 							  SLOT(amsSL900ADemo()));
	connect(ui.em4325DemoButton,			SIGNAL(clicked()),
			this, 							  SLOT(em4325Demo()));


	connect(ui.memBankBox, 					SIGNAL(currentIndexChanged(int)),
			this, 							  SLOT(memBankChanged(int)));
	connect(m_memoryModel, 					SIGNAL(itemChanged(QStandardItem*)),
			this, 							  SLOT(itemChanged(QStandardItem*)));

	this->resize(500, this->height());

	m_readActive = false;
}

CGen2TagDialog::~CGen2TagDialog()
{
	delete m_explicitReadDialog;
	delete m_passwordDialog;
	delete m_killDialog;
	delete m_lockDialog;
	delete m_setPasswordDialog;
	delete m_setEPCDialog;
	delete m_storeText;
	delete m_nxpG2iLConfigWordDialog;
	delete m_nxpG2iLDialog;

	delete m_progressBar;
}

int CGen2TagDialog::execPrivate ( )
{
	connect(m_tag, 				SIGNAL(stepDone(int, int)),
			m_progressBar,		  SLOT(progressChanged(int, int)));

	ui.tagIdEdit->setText(m_tag->tagId());
	ui.readerIdEdit->setText(m_reader->name());

	ui.manufacturerInfoEdit->setText("");
	ui.modelInfoEdit->setText("");
	ui.userMemSizeInfoEdit->setText("");

	ui.serialNrLineEdit->setText("");
	ui.serialNrLabel->setVisible(false);
	ui.serialNrLineEdit->setVisible(false);

	ui.accessPasswordEdit->setText("00-00-00-00");
	ui.memBankBox->setCurrentIndex(0);
	ui.setMemButton->setVisible(false);
	ui.exportMemButton->setEnabled(false);

	ui.nxpBox->setVisible(false);
	ui.nxpSetReadProtectButton->setVisible(false);
    ui.nxpConfigWordButton->setVisible(false);
	ui.nxpG2iLDemoButton->setVisible(false);
	ui.nxpI2CDemoButton->setVisible(false);

	ui.amsBox->setVisible(false);
	ui.amsSL900ADemoButton->setVisible(false);

	ui.emBox->setVisible(false);
	ui.em4325DemoButton->setVisible(false);

	clearInfo();

	for(int i = 0; i < 4; i++){
		if(getTagInfos())
			break;
	}

	clearMemory();

	QTimer::singleShot(0, this, SLOT(shrink()));

	return QDialog::exec();
}


int CGen2TagDialog::exec(QrfeReaderInterface* ph, QString tagId, uint antennaId)
{
	m_reader = ph;
	m_antennaId = antennaId;

	if(m_reader->tagType() != QrfeGlobal::TAG_GEN2)
		return 0;

	QrfeTagManipulatorInterface* 	tagManipulator;
	if((tagManipulator = m_reader->getTagManipulator()) == 0 || dynamic_cast<QrfeGen2ManipulatorInterface*>(tagManipulator) == 0)
		return 0;

	m_tag = new QrfeGen2Tag(tagId, dynamic_cast<QrfeGen2ManipulatorInterface*>(tagManipulator), this);

	if(m_antennaId != 0)
	{
		m_reader->setWorkingAntenna(antennaId);
	}

	return execPrivate();
}

void CGen2TagDialog::done(int r)
{
	delete m_tag;

	QDialog::done(r);
}



bool CGen2TagDialog::getTagInfos()
{
	m_progressBar->setText(tr("Reading data from tag..."));
	m_progressBar->show();

	QrfeGlobal::TMIResult res = m_tag->readInformations();
	if(res != QrfeGlobal::TMI_OK){
		m_progressBar->hide();
		handleError(res, "Could not read Informations");
		return false;
	}

	ui.manufacturerInfoEdit->setText(m_tag->manufacturer());
	ui.modelInfoEdit->setText(m_tag->model());
	ui.userMemSizeInfoEdit->setText(m_tag->userMemSize());

	if(m_tag->hasSerialNr())
	{
		ui.serialNrLabel->setVisible(true);
		ui.serialNrLineEdit->setVisible(true);
		ui.serialNrLineEdit->setText(m_tag->serialNr());
	}

	infoSetOK("-- Load tag informations - OK --");

    QrfeGen2AdvancedManipulatorInterface* advancedManipulator = dynamic_cast<QrfeGen2AdvancedManipulatorInterface*>(m_tag->gen2TagManipulatorInterface());

    if(m_tag->manufacturer().contains("NXP") && advancedManipulator != 0)
	{
		ui.nxpBox->setVisible(true);
		ui.nxpSetReadProtectButton->setVisible(true);
		if(m_tag->model().contains("G2iL"))
		{
            ui.nxpConfigWordButton->setVisible(true);
			ui.nxpG2iLDemoButton->setVisible(true);
		}
		if(m_tag->model().contains("G2iM"))
		{
            ui.nxpConfigWordButton->setVisible(true);
		}
		if(m_tag->model().contains("I²C"))
		{
            ui.nxpConfigWordButton->setVisible(true);
			ui.nxpI2CDemoButton->setVisible(true);
		}
        if(m_tag->model().contains("UCODE 7"))
        {
            ui.nxpConfigWordButton->setVisible(true);
        }
        if(m_tag->model().contains("UCODE 8"))
        {
            ui.nxpConfigWordButton->setVisible(true);
        }
        if(m_tag->model().contains("UCODE DNA"))
        {
            ui.nxpConfigWordButton->setVisible(true);
        }
    }

    if(m_tag->manufacturer().contains("Austria Microsystems") && advancedManipulator != 0)
	{
		ui.amsBox->setVisible(true);
		if(m_tag->model().contains("SL900A"))
		{
			ui.amsSL900ADemoButton->setVisible(true);
		}
	}

    if(m_tag->manufacturer().contains("EM Microelectronics") && advancedManipulator != 0)
	{
		ui.emBox->setVisible(true);
		if(m_tag->model().contains("4325"))
		{
			ui.em4325DemoButton->setVisible(true);
		}
	}
	m_progressBar->hide();

	return true;
}


void CGen2TagDialog::readMem()
{
	m_progressBar->setText(tr("Reading data from tag..."));
	m_progressBar->progressChanged(0,10);
	m_progressBar->show();

	QrfeGen2Tag::MEMORY_BANK readBank = (QrfeGen2Tag::MEMORY_BANK)ui.memBankBox->currentIndex();

	uint size = 0;

	clearInfo();

	m_readActive = true;
	clearMemory();

	QrfeGlobal::TMIResult res = QrfeGlobal::TMI_ERROR;
	QByteArray mem, passw;

	if(!getPassword(passw))
	{
		m_progressBar->hide();
		QMessageBox::critical(this, tr("Error"), tr("The access password is not a valid!"));
		ui.setMemButton->setEnabled(false);
		m_readActive = false;
		return;
	}

	m_progressBar->increasePorgressBar();

    if(m_tag->tagManipulatorInterface()->tagManipulatorCanReadToEnd())
        size = 0;
    else
        size = 2;

//    if(readBank == QrfeGen2Tag::MEM_RES)
//        size = 8;

	int i = 1;
	while(1)
	{
		QByteArray temp;
		res = m_tag->readFromTag(readBank, 0 + (mem.size()/2), passw, size, temp);

        if(!m_tag->tagManipulatorInterface()->tagManipulatorCanReadToEnd())
        {
            if(res == QrfeGlobal::TMI_OK)
            {
                mem += temp;

                if(size != 2)
                    break;
            }
            else
            {
                res = QrfeGlobal::TMI_OK;
                break;
            }
        }
        else
        {
            if(res == QrfeGlobal::TMI_MEM_OVERRUN && mem.size() > 0 && mem.size()%m_tag->tagManipulatorInterface()->tagManipulatorMaxBufferSize() == 0)
            {
                res = QrfeGlobal::TMI_OK;
                break;
            }

            if(res != QrfeGlobal::TMI_OK)
            {
                handleError(res, "Could not read from tag");
                ui.setMemButton->setEnabled(false);
                m_readActive = false;
                m_progressBar->hide();
                return;
            }

            if(res == QrfeGlobal::TMI_OK)
            {
                mem += temp;
            }

            if(temp.size() < m_tag->tagManipulatorInterface()->tagManipulatorMaxBufferSize())
            {
                break;
            }
        }

		i++;
		m_progressBar->progressChanged(i, 10+i);
		QApplication::processEvents();
	}

	m_progressBar->increasePorgressBar();

	saveMemory(ui.memBankBox->currentText(), mem);

	m_progressBar->increasePorgressBar();


	ui.setMemButton->setEnabled(true);
	ui.memSizeEdit->setText(QString::number(mem.size()) + " Bytes");
	infoSetOK("-- Read " + QString::number(mem.size()) + " Bytes - OK --");

	m_readActive = false;

	m_progressBar->hide();
}

void CGen2TagDialog::setMem()
{
	m_progressBar->setText(tr("Writing data to tag..."));
	m_progressBar->progressChanged(0,10);
	m_progressBar->show();

	QByteArray passw;

	if(!getPassword(passw))
	{
		QMessageBox::critical(this, tr("Error"), tr("The access password is not a valid!"));
		return;
	}

	QrfeGlobal::TMIResult res = QrfeGlobal::TMI_OK;

	QStringList resultList;

	for(int i = 0; i < m_memoryChangeFlags.size(); i += 2)
	{
		if(m_memoryChangeFlags.at(i) != 0 || m_memoryChangeFlags.at(i+1) != 0)
		{
			QString result = QString("@ 0x%1: %2-%3 ")
					.arg((ushort) i/2, 4, 16, QChar('0'))
					.arg((uchar) m_memory.at(i+0), 2, 16, QChar('0'))
					.arg((uchar) m_memory.at(i+1), 2, 16, QChar('0'));

			res = m_tag->writeToTag(QrfeGen2Tag::MEM_USER, i/2, passw, m_memory.mid(i, 2));
			if(res != QrfeGlobal::TMI_OK){
				result += "Error";
			}
			else {
				result += "OK";
				m_memoryChangeFlags[i  ] = 0;
				m_memoryChangeFlags[i+1] = 0;
			}
			resultList << result;

			m_progressBar->progressChanged(resultList.size(), 10+resultList.size());
		}
	}


	if(res == QrfeGlobal::TMI_OK)
	{
		infoSetOK("-- Wrote data to tag memory - OK --");
		infoAppend("Result: \n\n" +  resultList.join("\n"));
	}
	else
		handleError(res, "Could not write to tag! \nResult: \n\n" +  resultList.join("\n"));

	m_progressBar->hide();

	// T  h  i  s  I  s  T  h  e  K  e  y
	// 54 68 69 73 49 73 54 68 65 4B 65 79

	// G  O
	// 47 4F
	if( m_tag->tagId().contains("54-68-69-73-49-73-54-68-65-4B-65-79", Qt::CaseInsensitive) &&
		m_memory.size() >= 2 &&
		m_memory.at(0) == 0x47 &&
		m_memory.at(1) == 0x4F )
		emit easterKeyUnlocked();
}

void CGen2TagDialog::exportMem()
{
	QString path = Cache::d.value(EXPORT_LAST_USED_FILEPATH, QDir::rootPath()).toString();

	QString prefix;
	prefix = "TagMemory_" + m_tag->tagId() + "_" + m_memoryName + "_";
	QString fileName = path + "/" + prefix + "Data_" + QDateTime::currentDateTime().toString("yyyy_MM_dd-hh_mm");
	QString outputFileName;


	if(!QrfeGlobal::saveBytesToFile(m_memory, this, "Save Data to File", fileName, outputFileName))
		return;

	QFileInfo info(outputFileName);
    Cache::d.setValue(EXPORT_LAST_USED_FILEPATH, info.absolutePath());
}


void CGen2TagDialog::read()
{
	m_explicitReadDialog->exec(m_tag);
}

void CGen2TagDialog::setEPC()
{
	if(m_setEPCDialog->exec(m_tag) == QDialog::Accepted){
		ui.tagIdEdit->setText(m_tag->tagId());
	}
}

void CGen2TagDialog::lock()
{
	m_lockDialog->exec(m_tag);
}

void CGen2TagDialog::setPassword()
{
	if(m_setPasswordDialog->exec(m_tag) == QDialog::Accepted)
		ui.accessPasswordEdit->setText("00-00-00-00");
}

void CGen2TagDialog::killTag()
{
	m_killDialog->exec(m_tag);
}

void CGen2TagDialog::storeText()
{
	m_storeText->exec(m_tag);
}


void CGen2TagDialog::nxpSetReadProtect()
{
    QrfeGen2AdvancedManipulatorInterface* manipulator = dynamic_cast<QrfeGen2AdvancedManipulatorInterface*>(m_tag->gen2TagManipulatorInterface());
    if(manipulator == 0)
        return;

    QrfeNXPTag nxpTag(m_tag->tagId(), manipulator, this);


	QByteArray passw;
	if(m_passwordDialog->exec(tr("Enter access password:"), passw) != QDialog::Accepted)
		return;

    QrfeGlobal::TMIResult res = nxpTag.nxp_setReadProtect(passw);

	if(res == QrfeGlobal::TMI_OK)
	{
		infoSetOK("-- Set ReadProtect - OK --");
		QMessageBox::information(this, tr("Set ReadProtect"), tr("The tag was successfully set ReadProtect. To detect the tag and reset the ReadProtect, change the Inventory Mode to \"NXP Read-Proetected Inventory\" in the Reader Settings."));
		accept();
	}
	else
		handleError(res, "Could not set ReadProtect");
}

void CGen2TagDialog::nxpConfigWord()
{
    if(m_tag->model().contains("G2iL"))
    {
        m_nxpG2iLConfigWordDialog->exec(m_tag);
    }
    if(m_tag->model().contains("G2iM"))
    {
        m_nxpG2iMConfigWordDialog->exec(m_tag);
    }
    if(m_tag->model().contains("I²C"))
    {
        m_nxpI2CConfigWordDialog->exec(m_tag);
    }
    if(m_tag->model().contains("UCODE 8"))
    {
        m_nxpUcode8ConfigWordDialog->exec(m_tag);
    }
    if(m_tag->model().contains("UCODE 7"))
    {
        m_nxpUcode7ConfigWordDialog->exec(m_tag);
    }
    if(m_tag->model().contains("UCODE DNA"))
    {
        m_nxpUcodeDNAConfigWordDialog->exec(m_tag);
    }
}

void CGen2TagDialog::nxpG2iLDemo()
{
	m_nxpG2iLDialog->exec(m_tag);
}

void CGen2TagDialog::nxpI2CDemo()
{
	m_nxpG2I2CDialog->exec(m_tag);
}


void CGen2TagDialog::amsSL900ADemo()
{
	m_amsSL900ADemo->exec(m_tag);
}

void CGen2TagDialog::em4325Demo()
{
	m_em4325ADemo->exec(m_tag);
}

void CGen2TagDialog::itemChanged(QStandardItem* item)
{
	if(m_readActive)
		return;

	if((item->row()*8) + item->column() >= m_memory.size()){
		QMessageBox::critical(this, tr("Error"), tr("The data written in the memory table are not valid!"));
		item->setText("");
		return;
	}

	int index = (item->row() * 8) + item->column();
	QString text = item->text();
	QRegExp regExp("[A-Za-z0-9]{1,2}", Qt::CaseInsensitive);

	if(!regExp.exactMatch(text)){
		QMessageBox::critical(this, tr("Error"), tr("The data written in the memory table are not valid!"));
		item->setText(QString("%1").arg((uchar)m_memory.at(index), 2, 16, QChar('0')));
		return;
	}

	bool ok = false;
	uchar new_value = (uchar)text.toUInt(&ok, 16);
	if(!ok){
		QMessageBox::critical(this, tr("Error"), tr("The data written in the memory table are not valid!"));
		item->setText(QString("%1").arg((uchar)m_memory.at(index), 2, 16, QChar('0')));
		return;
	}

	if(text.size() == 1)
		text = "0" + text;

	item->setText(text.toUpper());

	m_memory[index] 			= new_value;
	m_memoryChangeFlags[index] 	= 1;

	QFont f = item->font();
	f.setBold(true);
	item->setFont(f);
}

void CGen2TagDialog::memBankChanged(int index)
{
	ui.setMemButton->setEnabled(false);
	if(index == QrfeGen2Tag::MEM_USER){
		ui.memoryTableView->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed | QAbstractItemView::AnyKeyPressed);
		ui.setMemButton->setVisible(true);
	}
	else{
		ui.memoryTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
		ui.setMemButton->setVisible(false);
	}

	clearMemory();
}



bool CGen2TagDialog::getPassword(QByteArray& passwd)
{
	if(!ui.accessPasswordEdit->hasAcceptableInput())
		return false;

	bool ok = false;
	passwd = QrfeGlobal::stringToBytes(ui.accessPasswordEdit->text(), &ok);
	if(!ok)
		return false;

	return true;
}



void CGen2TagDialog::clearMemory()
{
	m_memoryModel->clear();
	m_memoryModel->setColumnCount(8);
	m_memoryModel->setHorizontalHeaderLabels(QStringList()  << "0x00" << "0x01"
															<< "0x02" << "0x03"
															<< "0x04" << "0x05"
															<< "0x06" << "0x07");
	ui.memSizeEdit->setText("- Bytes");
	m_memory.clear();
	m_memoryChangeFlags.clear();

	ui.exportMemButton->setEnabled(false);
}

void CGen2TagDialog::saveMemory(const QString& name, const QByteArray& mem)
{
	m_memoryName = name;
	m_memory = mem;
	m_memoryChangeFlags = QByteArray(mem.size(), (char)0x00);
	for(int i = 0; i < mem.size(); i++)
	{
		QString data = QString("%1").arg((uchar)mem.at(i), 2, 16, QChar('0')).toUpper();
		QStandardItem* item = new QStandardItem(data);
		item->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
		m_memoryModel->setItem( (i/8), (i%8), item);
	}

	ui.exportMemButton->setEnabled(true);
}


void CGen2TagDialog::shrink()
{
    this->resize(0,0);

    QPoint centerparent(
			this->parentWidget()->x() + ((this->parentWidget()->frameGeometry().width() - this->frameGeometry().width()) /2),
			this->parentWidget()->y() + ((this->parentWidget()->frameGeometry().height() - this->frameGeometry().height()) /2));

	QDesktopWidget * pDesktop = QApplication::desktop();
	QRect sgRect = pDesktop->screenGeometry(pDesktop->screenNumber(this->parentWidget()));
	QRect childFrame = this->frameGeometry();

	if(centerparent.x() < sgRect.left())
		centerparent.setX(sgRect.left());
	else if((centerparent.x() + childFrame.width()) > sgRect.right())
		centerparent.setX(sgRect.right() - childFrame.width());

	if(centerparent.y() < sgRect.top())
		centerparent.setY(sgRect.top());
	else if((centerparent.y() + childFrame.height()) > sgRect.bottom())
		centerparent.setY(sgRect.bottom() - childFrame.height());

	this->move(centerparent);
}
