/*******************************************************************************
*                                                                              *
* Société de Transports de Montréal.                                    	   *
* 2012 - 2013                                                                        *
*                                                                              *
* Projet Zones Tests                                                           *
*                                                                              *
*                                                                              *
*                                                                              *
*******************************************************************************/
/*
	Description:
	Cette classe est un thread qui roule en parallèle au programme.  Elle est
	responsable de l'analyse en temps réel du passage d'un train.  Elle fait la
	lecture des entrées sur la carte PCI et analyse les déclenchements au fur
	et à mesure qu'un train chemine sur la ZT2
	
*/

/* ************************************************************************** */
/* Revision:
### YYYMMDD JFM
	Verision d'origine.
	
### YYYYMMDD Description du besoin ou du bug
	Description du changement.
 */

/* ************************************************************************** */

#include "GlobalDefine.h"
#include "SimulatorThread.h"
#include "PCIIOMgr.h"
#include "AbstractLazerProbe.h"
#include <QElapsedTimer>
#include "ZTconfigmgr.h"

//const char * ErrorString[25] = {"Erreur de comptage S1-S2",
//									   "Déclenchement frotteur négatif",
//									   "Déclenchement pneu de guidage intérieur",
//									   "Déclenchement pneu de guidage extérieur",
//									   "Déclenchement pneu porteur intérieur",
//									   "Déclenchement pneu porteur extérieur",
//									   "Pré-détection frotteur négatif"};

CSimulatorThread::CSimulatorThread()
{
	mExitLoop = false;
	mSimThreadRunning = false;
	mLogElement = 0;
}

unsigned int CSimulatorThread::Init(CSimulatorInputModule *InputModule, CSImulatorOutputModule *OutputModule, CSimulatorPCIIO *PCIIO, CSimulatorLazerProbe *InternalLazerProbe,CSimulatorLazerProbe  *ExternalLazerProbe,GenericInputMasks_t *StationInputMasks)
{
	mInputModule = InputModule;
	mOutputModule = OutputModule;
	mPCIIO = PCIIO;
	mInternalLazerProbe = InternalLazerProbe;
	mExternalLazerProbe = ExternalLazerProbe;
	mStationInputMasks = StationInputMasks;


	return RET_OK;
}

void CSimulatorThread::SetLogElement(CLogElement *element)
{
	mLogElement = element;
}

void CSimulatorThread::StartSim()
{

	if(mLogElement == 0)
		return;

	qDebug("Sim Thread started!");
	mExitLoop = false;
	mSimThreadRunning = true;

	bool Run = true;
	CZT1LogElement *ZT1LogData = 0;
	CZT2LogElement *ZT2LogData = 0;

	if(mLogElement->mZTLogType == ZT1_LOG_TYPE)
		ZT1LogData = (CZT1LogElement*) mLogElement;
	else
		ZT2LogData = (CZT2LogElement*) mLogElement;

	QElapsedTimer AcquisitionTimer;
	AcquisitionTimer.start();

	qint64 index = 0;
	qint64 NextEventTime;

	//Init the lazer probes values.


	if(ZT1LogData != 0)
	{
		bool ExtSet = false, IntSet = false;
		for(int i = 0; i < ZT1LogData->mZTLogData.size(); i++)
		{
			if(ZT1LogData->mZTLogData.at(i)->mZT1ThreadData != 0)
			{
				if(ExtSet == false)
				{
					if(ZT1LogData->mZTLogData.at(i)->mZT1ThreadData->mPGExtValue != 0)
					{
						mExternalLazerProbe->SetData(ZT1LogData->mZTLogData.at(i)->mZT1ThreadData->mPGExtValue);
						ExtSet = true;
					}
				}
				if(IntSet == false)
				{
					if(ZT1LogData->mZTLogData.at(i)->mZT1ThreadData->mPGIntValue != 0)
					{
						mInternalLazerProbe->SetData(ZT1LogData->mZTLogData.at(i)->mZT1ThreadData->mPGIntValue);
						IntSet = true;
					}
				}

				if(IntSet == true && ExtSet == true)
					break;
			}
		}

//		mInternalLazerProbe->SetData(ZT1LogData->mFlags.mIntPGOffset);
//		mExternalLazerProbe->SetData(ZT1LogData->mFlags.mExtPGOffset);
		NextEventTime = ZT1LogData->mZTLogData.at(index)->mTimestamp;
	}
	else
	{
//		if(ZT2LogData->mZTLogData.at(index)->mZT2ThreadData != 0)
//			NextEventTime = ZT2LogData->mZTLogData.at(index)->mZT2ThreadData->mTimeStamp;
//		else
			NextEventTime = ZT2LogData->mZTLogData.at(index)->mTimestamp;
	}

	unsigned int PCIStartBuf = mPCIIO->GetInputs();
//	mPCIIO->SetInputBufValue(0);
	unsigned int ModuleStartBuf = mInputModule->GetInputBuf();
//	mInputModule->SetInputBufValue(mInputModule);

	NextEventTime = 0;
	mSimTimer.start();

	while(Run == true)
	{


		if(mSimTimer.nsecsElapsed() >= NextEventTime)
		{
	//		qDebug("%lld - %lld",mSimTimer.nsecsElapsed(),NextEventTime);
			if(ZT1LogData != 0)
			{
				CZT1ThreadData *Data = ZT1LogData->mZTLogData.at(index)->mZT1ThreadData;
				if(Data != 0)
				{
					unsigned int PCIData = PCIStartBuf;

					if(Data->mFN == 1)
						PCIData ^= mStationInputMasks->InputZT1FNMask;
					if(Data->mPExt == 1)
						PCIData ^= mStationInputMasks->InputZT1PIMask;
					if(Data->mPInt == 1)
						PCIData ^= mStationInputMasks->InputZT1PIMask;
					if(Data->mS1 == 1)
						PCIData |= mStationInputMasks->InputZT1S1Mask;
					if(Data->mS2 == 1)
						PCIData |= mStationInputMasks->InputZT1S2Mask;

					mPCIIO->SetInputBufValue(PCIData);

					mExternalLazerProbe->SetData(Data->mPGExtValue);
					mInternalLazerProbe->SetData(Data->mPGIntValue);
				}

				unsigned int InputModuleData = ModuleStartBuf;

				CZT1LogData *LogData = ZT1LogData->mZTLogData.at(index);
				if(LogData->mCIZT1 == 1)
				{
					InputModuleData |= mStationInputMasks->InputZT1ITIMask;
				}
				if(LogData->mCDVARM_ZT1 == 1)
				{
                    InputModuleData &= ~(mStationInputMasks->InputCDVZT1Mask);
				}
				if(LogData->mCDVApproach_ZT1 == 1)
				{
                    InputModuleData &= ~(mStationInputMasks->InputCDVZT1ApproachMask);
				}

				mInputModule->SetInputBufValue(InputModuleData);

				index++;
				if(index >= ZT1LogData->mZTLogData.size())
				{
					Run = false;
				}
				else
				{
//					NextEventTime = ZT1LogData->mZTLogData.at(index)->mTimestamp;
					NextEventTime = ZT1LogData->mZTLogData.at(index)->mTimestamp - ZT1LogData->mZTLogData.at(index-1)->mTimestamp;
					//qDebug("Next %lld",NextEventTime);
					mSimTimer.start();
				}
			}
			else
			{
				CZT2ThreadData *Data = ZT2LogData->mZTLogData.at(index)->mZT2ThreadData;
				if(Data != 0)
				{
					unsigned int PCIData = PCIStartBuf;

					if(Data->mPPInt == 1)
						PCIData ^=  mStationInputMasks->InputZT2PIMask;
					if(Data->mPPExt == 1)
						PCIData ^=  mStationInputMasks->InputZT2PEMask;
					if(Data->mS1 == 1)
						PCIData |= mStationInputMasks->InputZT2S1Mask;

					mPCIIO->SetInputBufValue(PCIData);
				}


				unsigned int InputModuleData = ModuleStartBuf;
				CZT2LogData *LogData = ZT2LogData->mZTLogData.at(index);
				if(LogData->mCIZT2 == 1)
				{
					InputModuleData |= mStationInputMasks->InputZT2ITIMask;
				}
				if(LogData->mCDVARM_ZT2 == 1)
				{
					InputModuleData ^= mStationInputMasks->InputCDVZT2Mask;
				}
				if(LogData->mCDVApproach_ZT2 == 1)
				{
					InputModuleData ^= mStationInputMasks->InputCDVZT2ApproachMask;
				}

				mInputModule->SetInputBufValue(InputModuleData);

				index++;
				if(index >= ZT2LogData->mZTLogData.size())
				{
					Run = false;
				}
				else
				{
//					if(ZT2LogData->mZTLogData.at(index)->mZT2ThreadData != 0)
//						NextEventTime = ZT2LogData->mZTLogData.at(index)->mZT2ThreadData->mTimeStamp;
//					else
						NextEventTime = ZT2LogData->mZTLogData.at(index)->mTimestamp;
				}


			}
		}

		mMutex.lock();
		if(mExitLoop == true)
			Run = false;
		mMutex.unlock();
	}//while

	mSimThreadRunning = false;
	emit SimFinished();

}

void CSimulatorThread::Terminate()
{
	mMutex.lock();
	mExitLoop = true;
	mMutex.unlock();
}

