/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/

/*
    Program obsługi portu COM i innych na potrzeby sterownika MWDevice
        (oraz innych wykorzystujących komunikację przez port COM)
    dla Symulatora Pojazdów Szynowych MaSzyna
    author: Maciej Witek 2016
        Autor nie ponosi odpowiedzialności za niewłaciwe używanie lub działanie programu!
*/
#include "stdafx.h"
#include "MWD.h"
#include "Globals.h"
#include "Logs.h"
#include "World.h"

#include <windows.h>

HANDLE hComm;

TMWDComm::TMWDComm() // konstruktor
{
	MWDTime = 0;
	bSHPstate = false;
	bPrzejazdSHP = false;
	bKabina1 = true;	// pasuje wyciągnąć dane na temat kabiny 
	bKabina2 = false;	// i ustawiać te dwa parametry!
	bHamowanie = false;
	bCzuwak = false;

	bRysik1H = false;
	bRysik1L = false;
	bRysik2H = false;
	bRysik2L = false;

	bocznik = 0;
	nastawnik = 0;
	kierunek = 0;
	bnkMask = 0;

	int i = 6;

	while (i)
	{
		i--;
		lastStateData[i] = 0;
		maskData[i] = 0;
		maskSwitch[i] = 0;
		bitSwitch[i] = 0;
	}

	i = 0;
	while (i<BYTETOWRITE)
	{
		if (i<BYTETOREAD)ReadDataBuff[i] = 0;
		WriteDataBuff[i] = 0;
		i++;
	}
	i = 0;
	while (i<6)
	{
		lastStateData[i] = 0;
		maskSwitch[i] = 0;
		bitSwitch[i] = 0;
		i++;
	}
}

TMWDComm::~TMWDComm() // destruktor
{
    Close();
}

bool TMWDComm::Open() // otwieranie portu COM
{
    LPCSTR portId = Global.sMWDPortId.c_str();
    hComm = CreateFile(portId, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
                       FILE_ATTRIBUTE_NORMAL, NULL);

    if (hComm == INVALID_HANDLE_VALUE)
    {
        if (GetLastError() == ERROR_FILE_NOT_FOUND)
        {
            WriteLog("PortCOM ERROR: serial port does not exist"); // serial port does not exist.
                                                                   // Inform user.
        }
        WriteLog("PortCOM ERROR! not open!");
        // some other error occurred. Inform user.
        return FALSE;
    }

    DCB CommDCB;
    CommDCB.DCBlength = sizeof(DCB);
    GetCommState(hComm, &CommDCB);

    CommDCB.BaudRate = Global.iMWDBaudrate;
    CommDCB.fBinary = TRUE;
    CommDCB.fParity = FALSE;
    CommDCB.fOutxCtsFlow = FALSE; // No CTS output flow control
    CommDCB.fOutxDsrFlow = FALSE; // No DSR output flow control
    CommDCB.fDtrControl = FALSE; // DTR flow control type

    CommDCB.fDsrSensitivity = FALSE; // DSR sensitivity
    CommDCB.fTXContinueOnXoff = FALSE; // XOFF continues Tx
    CommDCB.fOutX = FALSE; // No XON/XOFF out flow control
    CommDCB.fInX = FALSE; // No XON/XOFF in flow control
    CommDCB.fErrorChar = FALSE; // Disable error replacement
    CommDCB.fNull = FALSE; // Disable null stripping
    CommDCB.fRtsControl = RTS_CONTROL_DISABLE;

    CommDCB.fAbortOnError = FALSE;

    CommDCB.ByteSize = 8;
    CommDCB.Parity = NOPARITY;
    CommDCB.StopBits = ONESTOPBIT;

    // konfiguracja portu
    if (!SetCommState(hComm, &CommDCB))
    {
        // dwError = GetLastError ();
        WriteLog("Unable to configure the serial port!");
        return FALSE;
    }
    WriteLog("PortCOM OPEN and CONFIG!");
    return TRUE;
}

bool TMWDComm::Close() // zamykanie portu COM
{
	Global.bMWDmasterEnable = false;              // główne włączenie portu!
	Global.bMWDInputEnable = false;               // włącz wejścia
	Global.bMWDBreakEnable = false;               // włącz wejścia analogowe

    WriteLog("COM Port is closing...");
    int i = 0;
    while (i < BYTETOWRITE) // zerowanie danych...
    {
        WriteDataBuff[i] = 0;
        i++;
    }
    Sleep(100);
    SendData(); // wysyłanie do pulpitu: zatrzymanie haslera i zgaszenie lampek
    Sleep(700);
    CloseHandle(hComm);
    WriteLog("COM is close!");
    return TRUE;
}

bool TMWDComm::GetMWDState() // sprawdzanie otwarcia portu COM
{
    if (hComm > 0)
        return 1;
    else
        return 0;
}

bool TMWDComm::ReadData() // odbieranie danych + odczyta danych analogowych i zapis do zmiennych
{
	DWORD bytes_read;
	ReadFile(hComm, &ReadDataBuff[0], BYTETOREAD, &bytes_read, NULL);
	if (Global.bMWDdebugEnable && Global.iMWDDebugMode == 128) WriteLog("Data receive. Checking data...");
	if (Global.bMWDBreakEnable)
	{
		uiAnalog[0] = (ReadDataBuff[9] << 8) + ReadDataBuff[8];
		uiAnalog[1] = (ReadDataBuff[11] << 8) + ReadDataBuff[10];
		uiAnalog[2] = (ReadDataBuff[13] << 8) + ReadDataBuff[12];
		uiAnalog[3] = (ReadDataBuff[15] << 8) + ReadDataBuff[14];
		if (Global.bMWDdebugEnable && (Global.iMWDDebugMode & 1))
		{
			WriteLog("Main Break = " + to_string(uiAnalog[0]));
			WriteLog("Locomotiv Break = " + to_string(uiAnalog[1]));
		}
		if (Global.bMWDdebugEnable && (Global.iMWDDebugMode & 2))
		{
			WriteLog("Analog input 1 = " + to_string(uiAnalog[2]));
			WriteLog("Analog imput 2 = " + to_string(uiAnalog[3]));
		}
	}
	if (Global.bMWDInputEnable) CheckData();
	return TRUE;
}

bool TMWDComm::SendData() // wysyłanie danych
{
    DWORD bytes_write;

    WriteFile(hComm, &WriteDataBuff[0], BYTETOWRITE, &bytes_write, NULL);

    return TRUE;
}

bool TMWDComm::Run() // wywoływanie obsługi MWD + generacja większego opóźnienia
{
	if (GetMWDState())
	{
		MWDTime++;
		if (!(MWDTime % Global.iMWDdivider))
		{
			MWDTime = 0;
			if (Global.bMWDdebugEnable && Global.iMWDDebugMode == 128) WriteLog("Sending data...");
			SendData();
			if (Global.bMWDdebugEnable && Global.iMWDDebugMode == 128) WriteLog(" complet!\nReceiving data...");
			ReadData();
			if (Global.bMWDdebugEnable && Global.iMWDDebugMode == 128) WriteLog(" complet!");
			return 1;
		}
	}
	else
	{
		WriteLog("Port COM: connection ERROR!");
		Close();
		// może spróbować się połączyć znowu?
		return 0;
	}
	return 1;
}

void TMWDComm::CheckData() // sprawdzanie wejść cyfrowych i odpowiednie sterowanie maszyną
{
	int i = 0;
	while (i < 6)
	{
		maskData[i] = ReadDataBuff[i] ^ lastStateData[i];
		lastStateData[i] = ReadDataBuff[i];
		i++;
	}
	/*
				Rozpiska portów!
				Port0: 	0 	NC			odblok. przek. sprężarki i wentyl. oporów
						1 	M			wyłącznik wył. szybkiego
						2 	Shift+M		impuls załączający wył. szybki
						3 	N			odblok. przekaźników nadmiarowych
	   i różnicowego obwodu głównegoMMm
						4 	NC			rezerwa
						5 	Ctrl+N		odblok. przek. nadmiarowych
	   przetwornicy, ogrzewania pociągu i różnicowych obw. pomocniczych
						6 	L			wył. styczników liniowych
						7 	SPACE		kasowanie czuwaka

				Port1: 	0 	NC
						1 	(Shift)	X	przetwornica
						2 	(Shift)	C	sprężarka
						3 	S			piasecznice
						4 	(Shift)	H	ogrzewanie składu
						5 				przel. hamowania	Shift+B
	   pspbpwy Ctrl+B pospieszny B towarowy
						6 				przel. hamowania
						7 	(Shift)	F	rozruch w/n

				Port2: 	0 	(Shift) P	pantograf przedni
						1 	(Shift)	O	pantograf tylni
						2 	ENTER		przyhamowanie przy poślizgu
						3 	()		przyciemnienie świateł
						4 	()		przyciemnienie świateł
						5 	NUM6		odluźniacz
						6 	a			syrena lok W
						7	A			syrena lok N

				Port3: 	0 	Shift+J		bateria
						1
						2
						3
						4
						5
						6
						7
		*/

		/*	po przełączeniu bistabilnego najpierw wciskamy klawisz i przy następnym
			wejściu w pętlę MWD puszczamy bo inaczej nie działa
		*/

		// wciskanie przycisków klawiatury
		/*PORT0*/
	if (maskData[0] & 0x02) if (lastStateData[0] & 0x02)
		KeyBoard('M', 1);	// wyłączenie wyłącznika szybkiego
	else KeyBoard('M', 0);	// monostabilny
	if (maskData[0] & 0x04) if (lastStateData[0] & 0x04) // impuls załączający wyłącznik szybki
	{
		KeyBoard(0x10, 1);	// monostabilny
		KeyBoard('M', 1);
	}
	else
	{
		KeyBoard('M', 0);
		KeyBoard(0x10, 0);
	}
	if (maskData[0] & 0x08) if (lastStateData[0] & 0x08)
		KeyBoard('N', 1);	// odblok nadmiarowego silników trakcyjnych
	else KeyBoard('N', 0);	// monostabilny
	if (maskData[0] & 0x20) if (lastStateData[0] & 0x20)
	{							// odblok nadmiarowego przetwornicy, ogrzewania poc.
		KeyBoard(0x11, 1);      // różnicowego obwodów pomocniczych
		KeyBoard('N', 1);         // monostabilny
	}
	else
	{
		KeyBoard('N', 0);
		KeyBoard(0x11, 0);
	}
	if (maskData[0] & 0x40) if (lastStateData[0] & 0x40) KeyBoard('L', 1);	// wył. styczników liniowych
	else						KeyBoard('L', 0);	// monostabilny
	if (maskData[0] & 0x80) if (lastStateData[0] & 0x80) KeyBoard(0x20, 1);	// kasowanie czuwaka/SHP
	else 						KeyBoard(0x20, 0);	// kasowanie czuwaka/SHP

	/*PORT1*/

	// puszczanie przycisku bistabilnego klawiatury
	if (maskSwitch[1] & 0x02) 
	{
		if (bitSwitch[1] & 0x02) 
		{
			KeyBoard('X', 0);
			KeyBoard(0x10, 0);
		}
		else KeyBoard('X', 0);
			maskSwitch[1] &= ~0x02;
	}
	if (maskSwitch[1] & 0x04) {
		if (bitSwitch[1] & 0x04) {
			KeyBoard('C', 0);
			KeyBoard(0x10, 0);
		}
		else KeyBoard('C', 0);
		maskSwitch[1] &= ~0x04;
	}
	if (maskSwitch[1] & 0x10) {
		if (bitSwitch[1] & 0x10) {
			KeyBoard('H', 0);
			KeyBoard(0x10, 0);
		}
		else KeyBoard('H', 0);
		maskSwitch[1] &= ~0x10;
	}
	if (maskSwitch[1] & 0x20 || maskSwitch[1] & 0x40) {
		if (maskSwitch[1] & 0x20) KeyBoard(0x10, 0);
		if (maskSwitch[1] & 0x40) KeyBoard(0x11, 0);
		KeyBoard('B', 0);
		maskSwitch[1] &= ~0x60;
	}
	if (maskSwitch[1] & 0x80) {
		if (bitSwitch[1] & 0x80) {
			KeyBoard('F', 0);
			KeyBoard(0x10, 0);
		}
		else KeyBoard('F', 0);
		maskSwitch[1] &= ~0x80;
	}

	// przetwornica
	if (maskData[1] & 0x02) if (lastStateData[1] & 0x02)
	{
		KeyBoard(0x10, 1);	// bistabilny
		KeyBoard('X', 1);
		maskSwitch[1] |= 0x02;
		bitSwitch[1] |= 0x02;
	}
	else
	{
		maskSwitch[1] |= 0x02;
		bitSwitch[1] &= ~0x02;
		KeyBoard('X', 1);
	}
	// sprężarka
	if (maskData[1] & 0x04) if (lastStateData[1] & 0x04)
	{
		KeyBoard(0x10, 1);	// bistabilny
		KeyBoard('C', 1);
		maskSwitch[1] |= 0x04;
		bitSwitch[1] |= 0x04;
	}
	else
	{
		maskSwitch[1] |= 0x04;
		bitSwitch[1] &= ~0x04;
		KeyBoard('C', 1);
	}
	// piasecznica
	if (maskData[1] & 0x08) if (lastStateData[1] & 0x08)
		KeyBoard('S', 1);
	else
		KeyBoard('S', 0);	// monostabilny
	// ogrzewanie składu
	if (maskData[1] & 0x10) if (lastStateData[1] & 0x10)
	{
		KeyBoard(0x11, 1);	// bistabilny
		KeyBoard('H', 1);
		maskSwitch[1] |= 0x10;
		bitSwitch[1] |= 0x10;
	}
	else
	{
		maskSwitch[1] |= 0x10;
		bitSwitch[1] &= ~0x10;
		KeyBoard('H', 1);
	}
	// przełącznik hamowania
	if (maskData[1] & 0x20 || maskData[1] & 0x40)
	{
		if (lastStateData[1] & 0x20)
		{	// Shift+B
			KeyBoard(0x10, 1);
			maskSwitch[1] |= 0x20;
		}
		else if (lastStateData[1] & 0x40)
		{	// Ctrl+B
			KeyBoard(0x11, 1);
			maskSwitch[1] |= 0x40;
		}
		KeyBoard('B', 1);
	}
	// rozruch wysoki/niski
	if (maskData[1] & 0x80) if (lastStateData[1] & 0x80)
	{
		KeyBoard(0x10, 1);	// bistabilny
		KeyBoard('F', 1);
		maskSwitch[1] |= 0x80;
		bitSwitch[1] |= 0x80;
	}
	else
	{
		maskSwitch[1] |= 0x80;
		bitSwitch[1] &= ~0x80;
		KeyBoard('F', 1);
	}


	//PORT2
	if (maskSwitch[2] & 0x01)
	{
		if (bitSwitch[2] & 0x01)
		{
			KeyBoard('P', 0);
			KeyBoard(0x10, 0);
		}
		else KeyBoard('P', 0);
		maskSwitch[2] &= ~0x01;
	}
	if (maskSwitch[2] & 0x02)
	{
		if (bitSwitch[2] & 0x02)
		{
			KeyBoard('O', 0);
			KeyBoard(0x10, 0);
		}
		else KeyBoard('O', 0);
		maskSwitch[2] &= ~0x02;
	}

	// pantograf przedni
	if (maskData[2] & 0x01) if (lastStateData[2] & 0x01)
	{
		KeyBoard(0x10, 1);	// bistabilny
		KeyBoard('P', 1);
		maskSwitch[2] |= 0x01;
		bitSwitch[2] |= 0x01;
	}
	else
	{
		maskSwitch[2] |= 0x01;
		bitSwitch[2] &= ~0x01;
		KeyBoard('P', 1);
	}
	// pantograf tylni
	if (maskData[2] & 0x02) if (lastStateData[2] & 0x02)
	{
		KeyBoard(0x10, 1);	// bistabilny
		KeyBoard('O', 1);
		maskSwitch[2] |= 0x02;
		bitSwitch[2] |= 0x02;
	}
	else
	{
		maskSwitch[2] |= 0x02;
		bitSwitch[2] &= ~0x02;
		KeyBoard('O', 1);
	}
	// przyhamowanie przy poślizgu
	if (maskData[2] & 0x04) if (lastStateData[2] & 0x04) {
		KeyBoard(0x10, 1);	// monostabilny
		KeyBoard(0x0D, 1);
	}
	else
	{
		KeyBoard(0x0D, 0);
		KeyBoard(0x10, 0);
	}
	/*if(maskData[2] & 0x08) if (lastStateData[2] & 0x08){           // przyciemnienie świateł
	KeyBoard(' ',0);	// bistabilny
	KeyBoard(0x10,1);
	KeyBoard(' ',1);
	}else{
	KeyBoard(' ',0);
	KeyBoard(0x10,0);
	KeyBoard(' ',1);
	}
	if(maskData[2] & 0x10) if (lastStateData[2] & 0x10)  {       // przyciemnienie świateł
	KeyBoard(' ',0);	// bistabilny
	KeyBoard(0x11,1);
	KeyBoard(' ',1);
	}else{
	KeyBoard(' ',0);
	KeyBoard(0x11,0);
	KeyBoard(' ',1);
	}*/
	// odluźniacz
	if (maskData[2] & 0x20) if (lastStateData[2] & 0x20)
		KeyBoard(0x66, 1);
	else
		KeyBoard(0x66, 0);	// monostabilny
	// syrena wysoka
	if (maskData[2] & 0x40) if (lastStateData[2] & 0x40)
	{
		KeyBoard(0x10, 1);	// monostabilny
		KeyBoard('A', 1);
	}
	else
	{
		KeyBoard('A', 0);
		KeyBoard(0x10, 0);
	}
	if (maskData[2] & 0x80) if (lastStateData[2] & 0x80)
		KeyBoard('A', 1);	// syrena niska
	else
		KeyBoard('A', 0);	// monostabilny


	//PORT3

	if (maskSwitch[3] & 0x01)
	{
		if (bitSwitch[3] & 0x01)
		{
			KeyBoard('J', 0);
			KeyBoard(0x10, 0);
		}
		else KeyBoard('J', 0);
		maskSwitch[3] &= ~0x01;
	}
	if (maskSwitch[3] & 0x02)
	{
		if (bitSwitch[3] & 0x02)
		{
			KeyBoard('Y', 0);
			KeyBoard(0x10, 0);
		}
		else KeyBoard('Y', 0);
		maskSwitch[3] &= ~0x02;
	}
	if (maskSwitch[3] & 0x04)
	{
		if (bitSwitch[3] & 0x04)
		{
			KeyBoard('U', 0);
			KeyBoard(0x10, 0);
		}
		else KeyBoard('U', 0);
		maskSwitch[3] &= ~0x04;
	}
	if (maskSwitch[3] & 0x08)
	{
		if (bitSwitch[3] & 0x08)
		{
			KeyBoard('I', 0);
			KeyBoard(0x10, 0);
		}
		else KeyBoard('I', 0);
		maskSwitch[3] &= ~0x08;
	}


	// bateria
	if (maskData[3] & 0x01) if (lastStateData[3] & 0x01)
	{
		KeyBoard(0x10, 1);	// bistabilny
		KeyBoard('J', 1);
		maskSwitch[3] |= 0x01;
		bitSwitch[3] |= 0x01;
	}
	else
	{
		maskSwitch[3] |= 0x01;
		bitSwitch[3] &= ~0x01;
		KeyBoard('J', 1);
	}
	//Światło lewe
	if (maskData[3] & 0x02) if (lastStateData[3] & 0x02)
	{
		KeyBoard(0x10, 1);	// bistabilny
		KeyBoard('Y', 1);
		maskSwitch[3] |= 0x02;
		bitSwitch[3] |= 0x02;
    }else
	{
		maskSwitch[3] |= 0x02;
		bitSwitch[3] &= ~0x02;
		KeyBoard('Y', 1);
    }
    //światło górne
	if (maskData[3] & 0x04) if (lastStateData[3] & 0x04)
	{
		KeyBoard(0x10, 1);	// bistabilny
		KeyBoard('U', 1);
		maskSwitch[3] |= 0x04;
		bitSwitch[3] |= 0x04;
	}
	else
	{
		maskSwitch[3] |= 0x04;
		bitSwitch[3] &= ~0x04;
		KeyBoard('U', 1);
	}
	//światło prawe
	if (maskData[3] & 0x08) if (lastStateData[3] & 0x08)
	{
		KeyBoard(0x10, 1);	// bistabilny
		KeyBoard('I', 1);
		maskSwitch[3] |= 0x08;
		bitSwitch[3] |= 0x08;
	}
	else
	{
		maskSwitch[3] |= 0x08;
		bitSwitch[3] &= ~0x08;
		KeyBoard('I', 1);
	}



    /* NASTAWNIK, BOCZNIK i KIERUNEK */

    if (bnkMask & 1)
    { // puszczanie klawiszy
        KeyBoard(0x6B, 0);
        bnkMask &= ~1;
    }
    if (bnkMask & 2)
    {
        KeyBoard(0x6D, 0);
        bnkMask &= ~2;
    }
    if (bnkMask & 4)
    {
        KeyBoard(0x6F, 0);
        bnkMask &= ~4;
    }
    if (bnkMask & 8)
    {
        KeyBoard(0x6A, 0);
        bnkMask &= ~8;
    }

    if (nastawnik < ReadDataBuff[6])
    {
        bnkMask |= 1;
        nastawnik++;
        KeyBoard(0x6B, 1); // wciśnij + i dodaj 1 do nastawnika
    }
    if (nastawnik > ReadDataBuff[6])
    {
        bnkMask |= 2;
        nastawnik--;
        KeyBoard(0x6D, 1); // wciśnij - i odejmij 1 do nastawnika
    }
    if (bocznik < ReadDataBuff[7])
    {
        bnkMask |= 4;
        bocznik++;
        KeyBoard(0x6F, 1); // wciśnij / i dodaj 1 do bocznika
    }
    if (bocznik > ReadDataBuff[7])
    {
        bnkMask |= 8;
        bocznik--;
        KeyBoard(0x6A, 1); // wciśnij * i odejmij 1 do bocznika
    }

    /* Obsługa HASLERA */
    if (ReadDataBuff[0] & 0x80)
        bCzuwak = true;

    if (bKabina1)
    { // logika rysika 1
        bRysik1H = true;
        bRysik1L = false;
        if (bPrzejazdSHP)
            bRysik1H = false;
    }
    else if (bKabina2)
    {
        bRysik1L = true;
        bRysik1H = false;
        if (bPrzejazdSHP)
            bRysik1L = false;
    }
    else
    {
        bRysik1H = false;
        bRysik1L = false;
    }

    if (bHamowanie)
    { // logika rysika 2
        bRysik2H = false;
        bRysik2L = true;
    }
    else
    {
        if (bCzuwak)
            bRysik2H = true;
        else
            bRysik2H = false;
        bRysik2L = false;
    }
    bCzuwak = false;
    if (bRysik1H)
        WriteDataBuff[6] |= 1 << 0;
    else
        WriteDataBuff[6] &= ~(1 << 0);
    if (bRysik1L)
        WriteDataBuff[6] |= 1 << 1;
    else
        WriteDataBuff[6] &= ~(1 << 1);
    if (bRysik2H)
        WriteDataBuff[6] |= 1 << 2;
    else
        WriteDataBuff[6] &= ~(1 << 2);
    if (bRysik2L)
        WriteDataBuff[6] |= 1 << 3;
    else
        WriteDataBuff[6] &= ~(1 << 3);
}

void TMWDComm::KeyBoard(int key, bool s) // emulacja klawiatury
{
    INPUT ip;
    // Set up a generic keyboard event.
    ip.type = INPUT_KEYBOARD;
    ip.ki.wScan = 0; // hardware scan code for key
    ip.ki.time = 0;
    ip.ki.dwExtraInfo = 0;

    ip.ki.wVk = key; // virtual-key code for the "a" key

    if (s)
    { // Press the "A" key
        ip.ki.dwFlags = 0; // 0 for key press
        SendInput(1, &ip, sizeof(INPUT));
    }
    else
    {
        ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
        SendInput(1, &ip, sizeof(INPUT));
    }

    // return 1;
}
