#include "AbstractFSController.h"
#include "ZZ_Math_HDRONLY.h"
CAbstractFSController::CAbstractFSController(QObject* parent /*= nullptr*/)
{
	iFlagInit = 0;
	m_pFSCtrl = NULL;
	m_iThreadID = -1;
	m_vecDataFrameDark.clear();
	m_vecDataFrameSignal.clear();
}

CAbstractFSController::~CAbstractFSController()
{
	if (m_pFSCtrl!= 0 )
	{
		delete m_pFSCtrl;
	}
}

int CAbstractFSController::SetRunParas(int iThreadID, FSInfo fsInfo)
{
	connect(this, &CAbstractFSController::SignalInit_Self, this, &CAbstractFSController::InitializeFSControl);
	m_iThreadID = iThreadID;
	m_fsInfo = fsInfo;
	return 0;
}



int CAbstractFSController::InitializeFSControl()
{
	using namespace ZZ_MISCDEF::IRIS;
	if (m_iThreadID == -1/*|| m_iDeviceType == -1*/)
	{
		qDebug() << "Params Err. Call SetRunParas first";
		return 1;
	}
	switch (m_fsInfo.ucDeviceModel)
	{
	case DeviceModel::OSIFAlpha:  
		m_pFSCtrl = new OceanOptics_lib;
		if (m_pFSCtrl->Initialize(false, m_fsInfo.strInterface, m_fsInfo.strSN) != 0)
		{
			qDebug() << "OSIFAlpha Not Opened";
			return 2;
		}

		break;
	case DeviceModel::OSIFBeta:
		m_pFSCtrl = new OceanOptics_lib;
 		if (m_pFSCtrl->Initialize(false, m_fsInfo.strInterface, m_fsInfo.strSN) !=0)
 		{
 			qDebug() << "OSIFBeta Not Opened";
 			return 2;
 		}
		break;
	case DeviceModel::ISIF: 
		m_pFSCtrl = new ZZ_ATPControl_Serial_Qt;
		//m_pFSCtrl->Initialize(false, m_fsInfo.strInterface, NULL);
		if (m_pFSCtrl->Initialize(false, m_fsInfo.strInterface, m_fsInfo.strSN) != 0)
		{
			qDebug() << "ISIF Not Opened";
			return 3;
		}
		break;
	case DeviceModel::IS1:
		m_pFSCtrl = new ZZ_ATPControl_Serial_Qt;
		//m_pFSCtrl->Initialize(false, m_fsInfo.strInterface, NULL);
		if (m_pFSCtrl->Initialize(false, m_fsInfo.strInterface, m_fsInfo.strSN) != 0)
		{
			qDebug() << "IS1 Not Opened";
			return 3;
		}
		break;
	case DeviceModel::IS2:
		m_pFSCtrl = new ZZ_ATPControl_Serial_Qt;
		//m_pFSCtrl->Initialize(false, m_fsInfo.strInterface, NULL);
		if (m_pFSCtrl->Initialize(false, m_fsInfo.strInterface, m_fsInfo.strSN) != 0)
		{
			qDebug() << "IS2 Not Opened";
			return 3;
		}
		break;
	default: 
		break;
	}

	int iRes = m_pFSCtrl->GetDeviceAttribute(m_daDeviceAttr);
	if (iRes != 0)
	{
		qDebug() << "GetDeviceAttribute Failed" << iRes;
		return 4;
	}
	
	iRes = m_pFSCtrl->SetDeviceTemperature(0);
	if (iRes != 0)
	{
		qDebug() << "SetDeviceTemperature Failed" << iRes;
		//return 5;
	}
	iFlagInit = 1;
	return 0;
}

int CAbstractFSController::InitializeFSControl_Self()
{
	//InitializeFSControl();
	emit SignalInit_Self();
	return 0;
}

int CAbstractFSController::GetDeviceAttr(DeviceAttribute &daAttr)
{
	daAttr = m_daDeviceAttr;
	return 0;
}

int CAbstractFSController::PerformAutoExposure()
{
	qDebug() << "--------------------------Starting PerformAutoExposure" << " Thread ID:" << m_iThreadID;
	using namespace ZZ_MATH;
	float fPredictedExposureTime;
	int iDeviceDepth = (int)m_fsInfo.lDepth;

	qDebug() << "MAX---Min" << m_fsInfo.fMaxFactor << "---" << m_fsInfo.fMinFactor << " Thread ID:" << m_iThreadID;

	bool bFlagIsOverTrying = false;
	bool bFlagIsLowerMinExposureTime = false;
	bool bFlagIsOverMaxExposureTime = false;
	bool bFlagIsAutoExposureOK = false;
	bool bFlagIsAutoExposureFailed = false;

	bool bIsValueOverflow = false;
	bool bIsLastValueOverflow = false;

	int  iExposureTime = 0;
	float  fTempExposureTime = 0;
	double fLastExposureTime = 0.1;
	int    iRepeatCount = 0;

	//int iRes = m_pFSCtrl->SetExposureTime(1000);//need change to load from files
	int iRes = 0;
	if (iRes != 0)
	{
		qDebug() << "Err:PerformAutoExposure Failed.Exit Code:1" << " Thread ID:" << m_iThreadID;
		return 1;
	}

	while (!bFlagIsAutoExposureOK && !bFlagIsAutoExposureFailed)
	{
		DataFrame dfTemp;

		if (iRepeatCount++ > 30)
		{
			bFlagIsAutoExposureFailed = true;
			bFlagIsOverTrying = true;
			break;
		}
		//m_pFSCtrl->SetExposureTime(5000);
		m_pFSCtrl->GetExposureTime(iExposureTime);
		qDebug() << "Current ExpTime:" << iExposureTime << " Thread ID:" << m_iThreadID;
		//m_pFSCtrl->SetExposureTime(2500);
		//fExposureTime = (float)m_daDeviceAttr.iMinIntegrationTimeInMS;
		fTempExposureTime = iExposureTime;

		iRes = m_pFSCtrl->SingleShot(dfTemp);
		//iRes = m_pFSCtrl->SingleShot(dfTemp);
		if (iRes != 0)
		{
			qDebug() << "Err:PerformAutoExposure Failed.Exit Code:2" << " Thread ID:" << m_iThreadID;
			return 2;
		}

		HeapSort(dfTemp.lData, m_daDeviceAttr.iPixels);

		double dSum = 0;
		int iCount = m_daDeviceAttr.iPixels / 200;
		for (int i = 0; i < iCount; i++)
		{
			dSum += dfTemp.lData[i];
		}
		double dTemp = dSum / iCount;

		qDebug() << "Avg " << dTemp << " Thread ID:" << m_iThreadID;

		if (dTemp >= iDeviceDepth * 0.99)
		{
			bIsValueOverflow = true;
			if (!bIsLastValueOverflow)
			{
				iExposureTime = (float)(fLastExposureTime + iExposureTime) / 2;
			}
			else
			{
				iExposureTime = iExposureTime / 2;
			}
		}

		else if (iDeviceDepth * m_fsInfo.fMaxFactor >= dTemp && dTemp >= iDeviceDepth * m_fsInfo.fMinFactor)
		{
			qDebug() << "trace bFlagIsAutoExposureOK =1 " << iExposureTime << " Thread ID:" << m_iThreadID;
			bFlagIsAutoExposureOK = 1;
		}
		else if (dTemp > iDeviceDepth * m_fsInfo.fMaxFactor)
		{
			bIsValueOverflow = true;
			if (!bIsLastValueOverflow)
			{
				iExposureTime = (float)(fLastExposureTime + iExposureTime) / 2;
			}
			else
			{
				iExposureTime = iExposureTime * 3 / 4;
			}
		}
		else if (dTemp < iDeviceDepth * m_fsInfo.fMinFactor)
		{
			bIsValueOverflow = false;
			if (bIsLastValueOverflow)
			{
				iExposureTime = (float)(fLastExposureTime + iExposureTime) / 2;
			}
			else
			{
				double dFactor;
				dFactor = dTemp / (iDeviceDepth * m_fsInfo.fMaxFactor);
				iExposureTime = (float)(iExposureTime / dFactor);
			}
			
		}
		bIsLastValueOverflow = bIsValueOverflow;
		fLastExposureTime = fTempExposureTime;

		

		if (/*fExposureTime > 100 || */iExposureTime <= m_daDeviceAttr.iMinIntegrationTimeInMS)
		{
			bFlagIsAutoExposureOK = false;
			bFlagIsAutoExposureFailed = true;
			bFlagIsLowerMinExposureTime = true;

// 			qDebug() << "Warning:PerformAutoExposure lower than min integration time.Will be limited to " << m_daDeviceAttr.iMinIntegrationTimeInMS - 1 << "MS" << " Thread ID:" << m_iThreadID;
// 			iRes = m_pFSCtrl->SetExposureTime((int)iExposureTime);
// 			if (iRes != 0)
// 			{
// 				qDebug() << "Err:PerformAutoExposure Failed.Exit Code:4" << " Thread ID:" << m_iThreadID;
// 				return 3;
// 			}
// 			else
// 			{
// 				qDebug() << "Success:PerformAutoExposure. Value" << iExposureTime << " Thread ID:" << m_iThreadID;
// 			}

			iRes = m_pFSCtrl->SetExposureTime(m_daDeviceAttr.iMinIntegrationTimeInMS);
			if (iRes != 0)
			{
				qDebug() << "Err:PerformAutoExposure Failed.Exit Code:3" << " Thread ID:" << m_iThreadID;
				return 3;
			}
			else
			{
				qDebug() << "Warning:PerformAutoExposure lower than min integration time.Will be limited to " << m_daDeviceAttr.iMinIntegrationTimeInMS << "MS" << " Thread ID:" << m_iThreadID;
			}

			break;
		}

		if (iExposureTime > m_daDeviceAttr.iMaxIntegrationTimeInMS-1)
		{
			bFlagIsAutoExposureOK = false;
			bFlagIsAutoExposureFailed = true;
			bFlagIsOverMaxExposureTime = true;
			//float fPredictedExposureTime = m_daDeviceAttr.iMaxIntegrationTimeInMS-1;
			//iRes = m_pFSCtrl->SetExposureTime(m_daDeviceAttr.iMaxIntegrationTimeInMS-1);
			//if (iRes != 0)
			//{
				//qDebug() << "Err:PerformAutoExposure Failed.Exit Code:3" << " Thread ID:" << m_iThreadID;
				//return 3;
			//}
			//else
			//{
				//qDebug() << "Warning:PerformAutoExposure exceed max integration time.Will be limited to 30sec";
			//}

			iRes = m_pFSCtrl->SetExposureTime(m_daDeviceAttr.iMaxIntegrationTimeInMS - 1);
			if (iRes != 0)
			{
				qDebug() << "Err:PerformAutoExposure Failed.Exit Code:3" << " Thread ID:" << m_iThreadID;
				return 3;
			}
			else
			{
				qDebug() << "Warning:PerformAutoExposure exceed max integration time.Will be limited to " << m_daDeviceAttr.iMaxIntegrationTimeInMS - 1 << "MS" << " Thread ID:" << m_iThreadID;
			}

			break;
		}

		iRes = m_pFSCtrl->SetExposureTime((int)iExposureTime);
		if (iRes != 0)
		{
			qDebug() << "Err:PerformAutoExposure Failed.Exit Code:4" << " Thread ID:" << m_iThreadID;
			return 3;
		}
		else
		{
			qDebug() << "Success:PerformAutoExposure. Value" << iExposureTime << " Thread ID:" << m_iThreadID;
		}
	}
	
	fPredictedExposureTime = iExposureTime;
	qDebug() << "--------------------------Stop PerformAutoExposure" << " Thread ID:" << m_iThreadID;
	//emit SignalAcqFinished(m_iThreadID, 1);

	return 0;
}

int CAbstractFSController::TakeDarkFrame()
{
	qDebug() << "Starting TakeDarkFrame" << " Thread ID:" << m_iThreadID;

	m_vecDataFrameDark.push_back(TakeOneFrame());
	

	qDebug() << "Stop TakeDarkFrame" << " Thread ID:" << m_iThreadID;
	//emit SignalAcqFinished(m_iThreadID, 1);
	return 0;
}

int CAbstractFSController::TakeSignalFrame()
{
	qDebug() << "Starting TakeSignal" << " Thread ID:" << m_iThreadID;

	m_vecDataFrameSignal.push_back(TakeOneFrame());

	qDebug() << "Stop TakeSignal" << " Thread ID:" << m_iThreadID;
	//emit SignalAcqFinished(m_iThreadID, 1);
	return 0;
}

DataFrame CAbstractFSController::TakeOneFrame()
{
	using namespace ZZ_MISCDEF::IRIS;
	//int iExpTime = 0;
	DataFrame dfTemp;
// 	m_pFSCtrl->GetExposureTime(iExpTime);
// 	dfTemp.usExposureTimeInMS = iExpTime;
// 	m_pFSCtrl->GetDeviceTemperature(dfTemp.fTemperature);

	if (m_fsInfo.ucDeviceModel== DeviceModel::ISIF)
	{
		float fTemp;
		m_pFSCtrl->GetDeviceTemperature(fTemp);
		dfTemp.fTemperature = fTemp;
	}
	else if(m_fsInfo.ucDeviceModel == DeviceModel::IS1)
	{
		dfTemp.fTemperature = 0;
	}

	int iRes = m_pFSCtrl->SingleShot(dfTemp);
	if (iRes != 0)
	{
		qDebug() << "Err. SingleShot" << " Thread ID:" << m_iThreadID;
	}

	return dfTemp;
// 	DataFrame dfTemp;
// 	int iRes = m_pFSCtrl->SingleShot(dfTemp);
// 	if (iRes != 0)
// 	{
// 		qDebug() << "Err. SingleShot" << " Thread ID:" << m_iThreadID;
// 	}
// 
// 	return dfTemp;
}

int CAbstractFSController::SaveDataFile()
{
	return 0;
}

int CAbstractFSController::StartAcquisitionSignal()
{
	//
	qDebug() << "Starting acq Signal" << " Thread ID:" << m_iThreadID;
// 	DataFrame struDF;
// 	int iii;
// 	m_pFSCtrl->SetExposureTime(10000000);
// 	m_pFSCtrl->GetExposureTime(iii);
// 	m_pFSCtrl->SingleShot(struDF);



	PerformAutoExposure();
	TakeSignalFrame();

	qDebug() << "Stop acq Signal" << " Thread ID:" << m_iThreadID;
	emit SignalAcqFinished_Signal(m_iThreadID, 1);
	return 0;
}

int CAbstractFSController::StartAcquisitionDark()
{
	qDebug() << "Starting acq Dark" << " Thread ID:" << m_iThreadID;
	TakeDarkFrame();
	qDebug() << "Stop acq Dark"<< " Thread ID:" << m_iThreadID;
	emit SignalAcqFinished_Dark(m_iThreadID, 1);

	return 0;
}

int CAbstractFSController::StopAcquisition()
{
	return 0;
}

int CAbstractFSController::ClearBuffer()
{
	m_vecDataFrameDark.clear();
	m_vecDataFrameSignal.clear();
	return 0;
}

int CAbstractFSController::GetBuffer(std::vector<DataFrame> &pvecDataFrameDark, std::vector<DataFrame> &pvecDataFrameSignal)
{
	for (size_t i=0; i < m_vecDataFrameSignal.size(); i++)
	{
		pvecDataFrameSignal.push_back(m_vecDataFrameSignal[i]);
		pvecDataFrameDark.push_back(m_vecDataFrameDark[i]);
	}
	return 0;
}

