//------------------------------------------------------------------------------

#pragma hdrstop

#include <IniFiles.hpp>
#include "Validator.h"
#include "ud_Module.h"

//---------------------------------------------------------------------------
Validator* validator=NULL;
//---------------------------------------------------------------------------

#pragma package(smart_init)
//---------------------------------------------------------------------------
#define POLYNOMIAL 0x08408
unsigned int GetCRC16(unsigned char* bufData, unsigned int sizeData)
{
	unsigned int CRC, i;
	unsigned char j;
	CRC = 0;
	for(i=0; i < sizeData; i++)
	{
		CRC ^= bufData[i];
		for(j=0; j < 8; j++)
		{
			if(CRC & 0x0001) {CRC >>= 1; CRC ^= POLYNOMIAL;}
			else CRC >>= 1;
		}
	}
	return CRC;
}
//---------------------------------------------------------------------------
/*unsigned int GetCRC16(char* bufData, unsigned int sizeData)
{	return GetCRC16((unsigned char*)bufData, sizeData);
}*/
//---------------------------------------------------------------------------
//Добавить нули к цифре
int addZero(int val,int cnt)
{
	for(int i=0;i<cnt;i++)
		val=val*10;
	return val;
}
//---------------------------------------------------------------------------
Validator::Validator()
{
	Pay=0;
	cPort=new ComPort();
	bNominals=false;
        vThread=NULL;

	LoadSetup();
}
//---------------------------------------------------------------------------
Validator::~Validator()
{
	delete cPort;
}
//---------------------------------------------------------------------------
//Загрузить настройки из ini файла
void Validator::LoadSetup()
{
	TIniFile* ini=new TIniFile(ExtractFilePath(ParamStr(0))+"\\Setup.ini");
	cPort->ComNumber=ini->ReadInteger("Validator","Port",0);
	cPort->BaudRate=ini->ReadInteger("Validator","BaudRate",0);
	delete ini;
}
//---------------------------------------------------------------------------
void Validator::SaveSetup()
{
	TIniFile* ini=new TIniFile(ExtractFilePath(ParamStr(0))+"\\Setup.ini");
	ini->WriteInteger("Validator","Port",cPort->ComNumber);
	ini->WriteInteger("Validator","BaudRate",cPort->BaudRate);
	delete ini;
}
//---------------------------------------------------------------------------
bool Validator::startPay() //Стартуем поток
{
	if(vThread!=NULL) vThread->Stop(); //На всяк случай
	vThread = new ValidatorThread(false);
}
//---------------------------------------------------------------------------
bool Validator::stopPay() //Стопим поток
{
	if(vThread!=NULL)
		vThread->Stop();
}
//---------------------------------------------------------------------------
//Сравнить последние 2 байта с CRC данных
bool Validator::CheckOnCRC(unsigned char* lpBuffer,unsigned long nSize)
{
	if(nSize<2) return false;
	unsigned int CRC=GetCRC16(lpBuffer,nSize-2);
	WORD* pCRC=(WORD*)&lpBuffer[nSize-2];
	return (CRC==*pCRC);
}
//---------------------------------------------------------------------------
//req - Строка запроса без CRC
//ans - Ответ на команду
//Результата совпал CRC или нет
bool Validator::SendCommand(std::string req, std::string &ans)
{
	unsigned short crc=GetCRC16((unsigned char*)req.c_str(),req.size());
	req+=((char*)(&crc))[0]; req+=((char*)(&crc))[1];
	DWORD BytesWritten=cPort->Write(req.c_str(),req.size());
        //Чтение ответа
        ans="";
	for(int i=0;i<255;i++)
        {
		Sleep(10);
        	char ch;
        	DWORD BytesRead=cPort->Read(&ch,1);
                if (BytesRead>0) ans+=ch; else break;
        }
        //Проверка ответа на CRC
        if(ans.size()>=2)
        {
		crc=GetCRC16((unsigned char*)ans.c_str(),ans.size()-2);
                if(crc==((unsigned short*)(&ans.c_str()[ans.size()-2]))[0]) return true;
        }
        return false;
}
//---------------------------------------------------------------------------
//Инициализация переменных валидатора
bool Validator::Start()
{
	if(cPort->Open(cPort->ComNumber))
	{
		cPort->BaudRate=cPort->BaudRate;
		if(!cPort->Setup(2)) return false;

		if(!Reset()) return false; //Перезагрузить валидатор
		if(!getSerialNumber()) return false; //Получить серийный номер
		if(!getNominals()) return false; //Получить номиналы прошитых купюр
                if(!SetEnableBillTypes(true)) return false; //Разрешить приём всех прошитых купюр
	}else return false;
}
//---------------------------------------------------------------------------
//Перезагрузить валидатор (RESET 30H Command for Bill-to-Bill unit to self-reset)
bool Validator::Reset()
{
	std::string req,ans;
	req+=(char)0x02; req+=(char)0x03; req+=(char)0x06; req+=(char)0x30;
	return SendCommand(req,ans);
}
//---------------------------------------------------------------------------
//Получить статус валидатора (GET STATUS 31H Request for Bill-to-Bill unit set-up status)
bool Validator::GetStatus()
{
	std::string req,ans;
        req+=(char)0x02; req+=(char)0x03; req+=(char)0x06; req+=(char)0x31; //req+=(char)0x41; req+=(char)0xB3;
        bool r=SendCommand(req,ans);
	if(r && ans.length()>6) //Почему то всегда без данных
        {
        	//Первые 3 байта показывают какие типы платижей разрешены
        	//Следующие 3 байта указывают какой уровень защиты установлен
        	//Следующие 3 По моему какие касеты готовы принимать деньги
        }
        return r;
}
//---------------------------------------------------------------------------
//Разрешить либо запретить приём заданных видов купюр
//enable - true разрешить всё, false запретить всё
bool Validator::SetEnableBillTypes(bool enable)
{
	std::string req,ans;
	if(enable)
        {	req+=(char)0x02; req+=(char)0x03; req+=(char)0x0C; req+=(char)0x34;
                req+=(char)0xFF; req+=(char)0xFF; req+=(char)0xFF; //24 бита
                req+=(char)0x00; req+=(char)0x00; req+=(char)0x00; //24 бита
        }else
        {	req+=(char)0x02; req+=(char)0x03; req+=(char)0x0C; req+=(char)0x34;
                req+=(char)0x00; req+=(char)0x00; req+=(char)0x00; //24 бита
                req+=(char)0x00; req+=(char)0x00; req+=(char)0x00; //24 бита
        }
        bool r=SendCommand(req,ans);
        return r;
}
//---------------------------------------------------------------------------
bool Validator::getSerialNumber()
{
	DWORD BytesWritten=cPort->Write(ConIdent,sizeof(ConIdent));
	DWORD BytesRead=cPort->Read(ReadAr,sizeof(ReadAr));
	if(!CheckOnCRC(ReadAr,BytesRead)) return false;
	//Нужный ли нам ответ
	if(BytesRead!=39)
	{
		saveLog(1,"Не удалось получить номер партии валидатора");
		return false;
	}
	//Выбираем № партии
	AnsiString str1((char*)&ReadAr[3],15);
	BatchNumber=str1.Trim();

	AnsiString str2((char*)&ReadAr[18],12);
	SerialNumber=str2.Trim();

	Sleep(100);
	return true;
}
//---------------------------------------------------------------------------
//Получить номиналыы купюр принимаемые валидатором
bool Validator::getNominals()
{
        bNominals=false;
	Sleep(100); //на всяк
	DWORD BytesWritten=cPort->Write(ConGetBT,sizeof(ConGetBT));
	DWORD BytesRead=cPort->Read(ReadAr,sizeof(ReadAr));
	if(!CheckOnCRC(ReadAr,BytesRead)) return bNominals;
	Sleep(100); //на всяк
	for(unsigned char i=0;i<23;i++)
	{
		bt[i].val=addZero(ReadAr[3+i*5],ReadAr[3+i*5+4]);
		//Код страны 3 буквы
		bt[i].country[0]=ReadAr[3+i*5+1];
		bt[i].country[1]=ReadAr[3+i*5+2];
		bt[i].country[2]=ReadAr[3+i*5+3];
		bt[i].val=addZero(ReadAr[3+i*5],ReadAr[3+i*5+4]);
	}
        bNominals=true;
	return bNominals;
}
//---------------------------------------------------------------------------
//Попытка приёма платежа, выполняется в цикле в отдельном потоке.
//state - текущее состояние валидатора (См. документацию)
//результат принятая сумма или 0 если ничего не принято
int Validator::getPay(int &state)
{
	state=0;
	if(!cPort->bOpen){Sleep(1000); return 0;}

	int result=0;
	DWORD BytesWritten=0;
	DWORD BytesRead=0;

	BytesWritten=cPort->Write(ConReACK, sizeof(ConReACK)); //Ответ валидатору что всё нормально
	Sleep(10);

	BytesWritten=cPort->Write(ConStPoll, sizeof(ConStPoll)); //Перейти в режим получения платежей
	BytesRead=cPort->Read(ReadAr,sizeof(ReadAr));
	if(CheckOnCRC(ReadAr,BytesRead))
	{
		if(ReadAr[3] == 0x13) //19 Ещё в режиме инициализации после выполнения команды RESET
		{
			//saveLog(1,"(State)0x13 Инициализация");
                }else
		if(ReadAr[3]==0x14) //20 Означает что находимся в режиме ожидания приёма платежа (IDLING – The state in which Bill-to-Bill is ready accept bills)
		{
			//saveLog(1,"(State)0x14 В режиме ожидания купюры");
                }else
		if(ReadAr[3]==0x15) //21 Производит сканирование купюры и определяется наминал (У меня 3 раза на купюру)
		{
                	state=0x15;
			//saveLog(1,"(State)21 Прошла 1 из проверок");
                }else
		if(ReadAr[3]==0x17) //23 Приём купюры в отсек если не получится положить то вернёт купюру обратно
		{
			//saveLog(1,"(State)23 Приём купюры!");
                }else
		if(ReadAr[3]==0x19) //25 DISABLED – The Bill-to-Bill unit has been disabled by the Controller and also the state in which Bill-to-Bill unit is after initialization.
		{
                	state=0x19;
			//saveLog(1,"(State)25 отменён приём!!!");

                        SetEnableBillTypes(true);
		}else
		if(ReadAr[3]==0x1c) //28 Извлечена купюра
		{
			//saveLog(1,"(State)28 Купюра извлечена");
                }else
		if(ReadAr[3]==0x41) //41H Заполнился мешок под завязку...
		{
			//saveLog(1,"(State)0x41 Заполнился мешок под завязку...");
                }else
		if(ReadAr[3]==0x45) //69 Обнаружена попытка обмана "фальшивка".
		{
			//saveLog(1,"(State)69 Незнаю");
                }else
		if(ReadAr[3]==0x81) //129 В какую касету попала купюра
		{
	                state=0x81;
			//saveLog(1,"(State)129 Принята купюра");

			if(ReadAr[4]<23)
			{
				result=bt[ReadAr[4]].val; //В касете ReadAr[5]
				saveLog(1,"(Pay)"+IntToStr(result));
			}
		}else
			saveLog(1,"(State???)"+IntToStr(ReadAr[3]));
	}
	Sleep(100);

	return result;
}
//---------------------------------------------------------------------------
//Завершение работы по приёму платежей
void Validator::endPay()
{
	SetEnableBillTypes(false);

        DWORD BytesWritten,BytesRead;
	BytesWritten=cPort->Write(ConReACK , sizeof(ConReACK));

	BytesWritten=cPort->Write(ConStPoll , sizeof(ConStPoll));
	BytesRead=cPort->Read(ReadAr,sizeof(ReadAr));
}
//---------------------------------------------------------------------------
int Validator::GetState() //Если 0 то всё в порядке
{
	//if(! bNominals) getNominals();  //todo не подходит для проверки
	if(! bNominals) return 1;

	return 0;
}
//---------------------------------------------------------------------------
AnsiString Validator::GetStatusDescription(BYTE StatusCode) //Описание статуса
{
	if (StatusCode == 0x00)
		return "OK";
	else
	if (StatusCode == 1)
		return "Не удалось получить номиналы купюр";
	else
		return "Error";
}
//---------------------------------------------------------------------------
