// PhysicalLayer.cpp : Vstupni bod aplikace, pokud je spustena v simulatoru pod Windows

//Struktura programu ve WinAPI byla prevzata z vzoroveho kodu, ktery je dostupny jako soucast Visual Studia.

//Knihovna obsahuje funkce pro praci se sériovými porty, aby se vlastni aplikaci jevily jako UART rozhrani

//Reseni jednotlivych funkci je mozna malinko neobratne, je to vsak nutne, aby principialne odpovidalo reseni, 

//ktere bylo realizovano na skutecnem hardwaru. S malymi upravami je tak mozne stejnou strukturu nadrazenych funkci pouzit

//behem simulace i ve skutecnosti.
#include "stdafx.h"

#include "PhysicalLayer.h"
//#include "GLCD.h"
#include <windows.h>
#include "main.h"
#include "OS.h"
#include "GRAPHICS.h"
#include "dbuffer.h"
#include "extra.h"
#include "Applications.h"
#include "Drivers.h"

#define MAX_LOADSTRING 100
dbuffer SerialOutRS232, SerialOutWiFi;
extern POSITION clickPosition;
unsigned short videoRam[MAX_X*MAX_Y];
unsigned short videoStore[MAX_X*MAX_Y];
unsigned short videoRamEmpty[MAX_X*MAX_Y];
HWND hWnd;
ANSWER serialSendCompleteFlag = YES;

// Global Variables:
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name
HANDLE mainThread;
DWORD ThreadId;


HANDLE readInterrupt;
DWORD ReaderThreadID;


HANDLE hSerialRS232, hSerialWiFi;

// Forward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);



int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

 	// TODO: Place code here.
	MSG msg;
	HACCEL hAccelTable;

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_PHYSICALLAYER, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_PHYSICALLAYER));

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage are only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PHYSICALLAYER));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_PHYSICALLAYER);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//



BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, 495,310 , NULL, NULL, hInstance, NULL);
		
   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
   //Nastartovani vlastniho kodu zarizeni (funkce main() v modulu main.cpp)
   mainThread =  CreateThread(NULL,0,main,0,0,&ThreadId);
   //Nastaveni priority tomuto vlaknu
   SetThreadPriority(mainThread,THREAD_PRIORITY_HIGHEST);
   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;
	     static int  cxClient, cyClient ;
	switch (message)
	{

	case WM_SIZE:
       cxClient = LOWORD (lParam) ;
       cyClient = HIWORD (lParam) ;
          return(0L) ;

	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// Parse the menu selections:
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;


		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		//Vykresleni obrazoveho bufferu na platno okna
		updateScreen();
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	case WM_LBUTTONDOWN:
		//Ziskani pozice kliknuti na okno, aby si ji mohl prevzit operacni system zarizeni
		clickPosition.x = LOWORD(lParam);
		clickPosition.y = HIWORD(lParam);
		clickPosition.isFresh = YES;
		//Vyvolani pozadavku na spusteni podsvetleni
		BacklightRequest();
		EndPaint(hWnd, &ps);
		break;
	case WM_TIMER:
		//Spusteni vlakna casovace pri kazde udalosti casovace
		Scheduler();
		InvalidateRect(hWnd,NULL,false);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}



/****************************************************************************
	Funkce provede vykresleni obrazoveho bufferu na platno okna

	Vstup: 
		neni

	Výstup:
		neni

*****************************************************************************/
void updateScreen(void)
{
	//Tvorba bitmapy z obrazoveho bufferu
	BITMAPINFO bi;
	HDC  hDC = GetDC(hWnd);
	memset(&bi,0,sizeof(BITMAPINFO));
	bi.bmiHeader.biSize      = sizeof(BITMAPINFOHEADER);
	bi.bmiHeader.biWidth    = MAX_X;
	bi.bmiHeader.biHeight    = MAX_Y;
	bi.bmiHeader.biPlanes    = 1;
	bi.bmiHeader.biBitCount    = NUM_BITS;
	bi.bmiHeader.biCompression  = BI_RGB;
	bi.bmiHeader.biSizeImage  = bi.bmiHeader.biWidth*bi.bmiHeader.biHeight*NUM_BITS/8;

	//Pokud je vypnute podsvetleni, kresli cesrou plochu, jinak obraz
	if (backlightState == BACKLIGHT_OFF)
		SetDIBitsToDevice(hDC,0,0,MAX_X,MAX_Y,0,0,0,bi.bmiHeader.biHeight,videoRamEmpty,&bi,DIB_RGB_COLORS);
	else
		SetDIBitsToDevice(hDC,0,0,MAX_X,MAX_Y,0,0,0,bi.bmiHeader.biHeight,videoRam,&bi,DIB_RGB_COLORS);
	ReleaseDC(hWnd,hDC);
	
}

/****************************************************************************
	Inicializuje casovac, takze se chova stejne jako counter na LPC2478

	Vstup: 
		neni

	Výstup:
		neni

*****************************************************************************/
void initPIT(int interval)
{
	SetTimer(hWnd,0,interval,0);
}

/****************************************************************************
	Inicializuje fyzickou vrstvu simulatoru

	Vstup: 
		neni

	Výstup:
		neni

*****************************************************************************/
void initPHY(void)
{
	initPIT(100);
	initSerial();
}


DWORD WINAPI readSerialRS232(LPVOID lpArg);
DWORD WINAPI readSerialWiFi(LPVOID lpArg);


/****************************************************************************
	Spusteni COM portu pro simulaci rozhrani WiFi i Serioveho portu

	Vstup: 
		neni

	Výstup:
		neni

*****************************************************************************/
void initSerial(void)
{
	
	hSerialRS232 = CreateFile(_T("\\\\.\\COM30"),GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,0,OPEN_EXISTING,FILE_ATTRIBUTE_DEVICE,0);
	hSerialWiFi	= CreateFile(_T("\\\\.\\COM32"),GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,0,OPEN_EXISTING,FILE_ATTRIBUTE_DEVICE,0);
	if(hSerialRS232==INVALID_HANDLE_VALUE)
	{
		if(GetLastError()==ERROR_FILE_NOT_FOUND)
		{
			DebugOut("Could not open the port, port not found");
		}
	}
	else
	{
		DebugOut("Port COM successfully opened.\n");
	}

	if(hSerialWiFi==INVALID_HANDLE_VALUE)
	{
		if(GetLastError()==ERROR_FILE_NOT_FOUND)
		{
			DebugOut("Could not open the port, port not found");
		}
	}
	else
	{
		DebugOut("Port WiFi successfully opened.\n");
	}
	//Nastaveni parametru portu
	COMMTIMEOUTS timeouts={0};
	timeouts.ReadIntervalTimeout=10;
	timeouts.ReadTotalTimeoutConstant=10;
	timeouts.ReadTotalTimeoutMultiplier=10;
	timeouts.WriteTotalTimeoutConstant=10;
	timeouts.WriteTotalTimeoutMultiplier=10;
	


	if(!SetCommTimeouts(hSerialRS232, &timeouts))
	{
		DebugOut("Could not set COM port parameters.\n");
	}

	if(!SetCommTimeouts(hSerialWiFi, &timeouts))
	{
		DebugOut("Could not set WiFi port parameters.\n");
	}

	DCB dcbSerialParams = {0};
	//dcbSerial.DCBlength=sizeof(dcbSerialParams);
	if (!GetCommState(hSerialRS232, &dcbSerialParams)) {
	//error getting state
	}
	dcbSerialParams.BaudRate=CBR_115200;
	dcbSerialParams.ByteSize=8;
	dcbSerialParams.StopBits=ONESTOPBIT;
	dcbSerialParams.Parity=NOPARITY;
	if(!SetCommState(hSerialRS232, &dcbSerialParams)){
	//error setting serial port state
	}

	if(!SetCommState(hSerialWiFi, &dcbSerialParams)){
	//error setting serial port state
	}
	//Spusteni vlaken, ktera emuluji preruseni na LPC2478 (alespon priblizne)
	CreateThread(NULL,0,readSerialRS232,0,0,&ReaderThreadID);
	CreateThread(NULL,0,readSerialWiFi,0,0,&ReaderThreadID);


}

/****************************************************************************
	Vlakno pro cteni dat z RS232 a ukladani techto dat do instance tridy dbuffer

	Vstup: 
		neni

	Výstup:
		neni

*****************************************************************************/
DWORD WINAPI readSerialRS232(LPVOID lpArg)
{
#ifdef FIX_ALL_WARNINGS
	lpArg;
#endif
	char szBuff[2] = "";
	int n = 1;
	

	DWORD dwBytesRead = 0;
//	while(1);
	while(true)
	{
		if(!ReadFile(hSerialRS232, szBuff, n, &dwBytesRead, NULL))
		{
	
		}
		if (dwBytesRead == 0)
		{
			if (SerialOutRS232.GetCount() == 0)
				continue;
			else
			{
				SerialOutRS232.DataRdy = YES;
				continue;
			}
		}
		while(SerialOutRS232.Lock == YES);
		SerialOutRS232.Lock = YES;
		SerialOutRS232.AddData(szBuff[0]);
		SerialOutRS232.Lock = NO;
	}
	return 0;
};


/****************************************************************************
	Vlakno pro cteni dat z RS232 a ukladani techto dat do instance tridy dbuffer

	Vstup: 
		neni

	Výstup:
		neni

*****************************************************************************/
DWORD WINAPI readSerialWiFi(LPVOID lpArg)
{
#ifdef FIX_ALL_WARNINGS
	lpArg;
#endif
	char szBuff[2] = "";
	int n = 1;
	

	DWORD dwBytesRead = 0;
//	while(1);
	while(true)
	{
		if(!ReadFile(hSerialWiFi, szBuff, n, &dwBytesRead, NULL))
		{
	
		}
		if (dwBytesRead == 0)
		{
			if (SerialOutWiFi.GetCount() == 0)
				continue;
			else
			{
				SerialOutWiFi.DataRdy = YES;
				continue;
			}
		}
		while(SerialOutWiFi.Lock == YES);
		SerialOutWiFi.Lock = YES;
		SerialOutWiFi.AddData(szBuff[0]);
		SerialOutWiFi.Lock = NO;
	}
	return 0;
};


/****************************************************************************
	Vraci aktualni mnozstvi dat prijate na danem rozhrani

	Vstup: 
		com:		komunikacni rozhrani

	Výstup:
		int:		Pocet bajtu

*****************************************************************************/
int GetSerialDataCount(OS_CONNECT com)
{
	dbuffer* SerialOut;
	switch(com)
	{
	case OS_SERIAL:
		SerialOut = &SerialOutRS232;
		break;
	case OS_WIFI:
		SerialOut = &SerialOutWiFi;
		break;
	default:
		return -1;
	}
	return SerialOut->GetCount();
}

/****************************************************************************
	Vraci informaci o tom, jestli jsou v aktualne pouzitem prijimacim bufferu data

	Vstup: 
		com:		rozhrani

	Výstup:
		ANSWER:		YES/NO

*****************************************************************************/
ANSWER CheckSerialBuffer(OS_CONNECT com)
{
	dbuffer* SerialOut;
	switch(com)
	{
	case OS_SERIAL:
		SerialOut = &SerialOutRS232;
		break;
	case OS_WIFI:
		SerialOut = &SerialOutWiFi;
		break;
	default:
		return NO;
	}
	return SerialOut->DataRdy;
}

/****************************************************************************
	Ziska data z bufferu a ulozi je na danou adresu

	Vstup: 
		com:		komunikacni rozhrani
		buffer:		Adresa pro ulozeni dat

	Výstup:
		int:		Mnozstvi dat

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

int GetSerialData(OS_CONNECT com, unsigned char* buffer)
{
	dbuffer* SerialOut;
	switch(com)
	{
	case OS_SERIAL:
		SerialOut = &SerialOutRS232;
		break;
	case OS_WIFI:
		SerialOut = &SerialOutWiFi;
		break;
	default:
		return -1;
	}
	while(SerialOut->Lock == YES);
	SerialOut->Lock = YES;
	int i = SerialOut->GetData(buffer);
	buffer[i] = 0;
	SerialOut->Lock = NO;
	return i;
}

/****************************************************************************
	Odesle data pres zvoleny seriovy port

	Vstup: 
		com:		komunikacni rozhrani
		buffer:		ukazatel odesilanych dat

	Výstup:
		int:		-1 pri chybe, jinak delka dat

*****************************************************************************/
int SendSerialData(OS_CONNECT com, unsigned char* buffer)
{
	serialSendCompleteFlag = NO;
	int n = get_length(buffer);
	DWORD dwBytesWritten;
	
	switch(com)
	{
	case OS_SERIAL:
		if(!WriteFile(hSerialRS232, buffer, n, &dwBytesWritten, NULL))
		{
	
		}
		break;
	case OS_WIFI:
		if(!WriteFile(hSerialWiFi, buffer, n, &dwBytesWritten, NULL))
		{
	
		}
		break;
	default:
		return -1;
	}
	serialSendCompleteFlag = YES;
	return n;
}


/****************************************************************************
	Pouze pro kompatibilitu simulatoru a hardwaru je vytvorena tato metoda

	Vstup: 
		neni

	Výstup:
		neni

*****************************************************************************/
unsigned char* GetSerialData(OS_CONNECT com)
{
	return 0;
}

/****************************************************************************
	Vraci, jestli byla data odeslana - v simulatoru opet postrada vyznam

	Vstup: 
		neni

	Výstup:
		neni

*****************************************************************************/
ANSWER CheckSerialSendComplete(void)
{
	return serialSendCompleteFlag;
}