/****************************************************************************
	Aplikace pro ziskani dostupnych siti okolo zarizeni a nasledny vyber site

*****************************************************************************/
#ifdef PCSIM
	#include "stdafx.h"
#endif

#include "Applications.h"
#include "GRAPHICS.h"
#include "OS.h"
#include "GLCD.h"
#include "WiFi.h"
#include "extra.h"
#include "Drivers.h"

#define NETWORK_BUFFER_LENGTH 5											//Delka bufferu pro site

/****************************************************************************
	Definice fontu pro aplikaci

*****************************************************************************/
FONT NetworkNamesScreenFont = {RGB_CYAN,FONT_SMALL,RGB_DARK_BLUE};
FONT NetworkNamesShapeFont = {RGB_CYAN,FONT_SMALL,RGB_DARK_GRAY};
FONT NetworkNamesButtonsFont = {RGB_DARK_GREEN,FONT_SMALL,RGB_LIGHT_GRAY};

TScreen networkNamesScreen;												//Obrazovka aplikace
TScreenObject networkNamesScreenObjects[NETWORK_NAMES_OBJECT_COUNT];	//Pole objektu aplikace

TApplicationInternal NetworkNamesInternal;								//Ridici objekt aplikace

/****************************************************************************
	Parametry objektu aplikace

*****************************************************************************/
TScreenObjectParams NetworkNamesMainShapeParams = {NETWORK_NAMES_XC-NETWORK_NAMES_XA,NETWORK_NAMES_YC-NETWORK_NAMES_YA,&NetworkNamesShapeFont};
TScreenObjectParams NetworkNamesButtonsParams = {NETWORK_NAMES_BUTTONS_WIDTH,NETWORK_NAMES_BUTTONS_HEIGHT,&NetworkNamesButtonsFont};
TScreenObjectParams NetworkNamesButtonsShortParams = {NETWORK_NAMES_BUTTONS_SHORT_WIDTH,NETWORK_NAMES_BUTTONS_HEIGHT,&NetworkNamesButtonsFont};

int NetworkNamesResult = -1;										//Vysledek po ukonceni aplikace
NETWORK_STRUCT myDetectedNetworks[NETWORK_BUFFER_LENGTH] = {};		//Detekovane site
NETWORK_NAMES_ACQUIRE nnAcquireState = NN_CLEAR;					//Stav detekce siti

/****************************************************************************
	Nacteni parametru serioveho portu

	Vstup: 
		msg:			zprava apliakci

	Vystup
		RESULT:			vysledek zpracovani zpravy

*****************************************************************************/
RESULT NNMsgBox(MESSAGE * msg)
{
	POSITION* tmpPosition = 0;
	switch (msg->msg)
	{
	case RUN:
		DebugOut("NetworkNames RUN command received.\n");
		if (NetworkNamesInternal.State == NETWORK_NAMES_READY)				//Pokud je aplikace zinicializovana
		{
			NetworkNamesResult = -1;										//Vynuluj posledni vysledek
			NetworkNamesInternal.State = NETWORK_NAMES_RESTART;				//Proved spusteni aplikace
			return PASSED;
		}
		DebugOut("NetworkNames could not be run.\n");
		return FAILED;
		break;
	case INIT:
		if (NetworkNamesInternal.State == NETWORK_NAMES_NOT_INITIALIZED)	//POkud neni aplikace zinicializovana
		{
			DebugOut("NetworkNames INIT command received.\n");
			NetworkNamesInternal.State = NETWORK_NAMES_INITIALIZE;			//Inicializuj
			return PASSED;
		}
		break;
	case GET_STATE:
		return (RESULT)NetworkNamesInternal.State;							//Vraci stav aplikace
		break;
	case GET_RESULT:
		return (RESULT)NetworkNamesResult;									//Vraci vysledek po ukonceni aplikace
		break;
	case APP_CLICK:
		NetworkNamesInternal.ClickPosition.isFresh = YES;					//Prijato kliknuti nalezejici aplikaci
		tmpPosition = (POSITION*)msg->data;									//Zkopirovani dat o kliknuti
		NetworkNamesInternal.ClickPosition.x = tmpPosition->x;
		NetworkNamesInternal.ClickPosition.y = tmpPosition->y;
		DebugOut("NetworkNames click received.\n");
		return PASSED;
		break;
	default:
		break;
		
	}
	return FAILED;
}



/****************************************************************************
	Vykresleni tlacitka s nazvem site

	Vstup: 
		neni

	Vystup
		neni

*****************************************************************************/
void DrawNetworkNameButton(TScreenObject* obj ,OBJECT_MESSAGE message)
{
	int i = obj->id-NETWORK_NAME_BUTTON_1;			//Vypocet vztahu tlacitka k poli detekovanych siti
	DrawButtonShape(obj,message);					//Vykresleni tvaru tlacitka
	LCD_Goto_XY(obj->x+5,obj->y+5);					//Presun na pozici tlacitka pro tisk
	if (myDetectedNetworks[i].ssid[0] == 0)			//Pokud pozic nema prirazene jmeno
	{
		LCD_Set_Font_Color(RGB_DARK_GRAY);			//tiskni sede
		LCD_printf(STR_NETWORK_NAMES_NONE);			
	}
	else
	{
		LCD_printf(myDetectedNetworks[i].ssid);		//jinak vytiskni nazev site
	}

}

/****************************************************************************
	Vymaze zasobnik zdetekovanych siti

	Vstup: 
		neni

	Vystup
		neni

*****************************************************************************/
void ClearNetworkBuffer(void)
{
	for (int i = 0; i < NETWORK_BUFFER_LENGTH; i++)
	{
		clearBuffer(myDetectedNetworks[i].ssid,NETWORK_NAME_LENGTH);		//vymazani polozky
		myDetectedNetworks[i].isValid = NO;									//zneplatneni zaznamu
	}
}

/****************************************************************************
	Odezva tlacitka jez umoznuje zadat rucne nazev site


	TODO: fonkce neni overena a dokoncena... chybi zpusob jak zadat sifrovani apod...

	Vstup: 
		obj:		instance objektu
		message:	parametr volani

	Vystup
		neni

*****************************************************************************/
void HiddenNetworkButtonCallback(TScreenObject* obj ,OBJECT_MESSAGE message)
{
#ifdef FIX_ALL_WARNINGS
	message;obj;
#endif
	NetworkNamesInternal.RunApplication(KEYBOARD);			//Spusteni klavesnice
}

/****************************************************************************
	Odezva tlacitka s nazvy siti

	Vstup: 
		obj:			instance objektu
		message:		parametr volani

	Vystup
		neni

*****************************************************************************/
void ScannedNetworkButtonCallback(TScreenObject* obj ,OBJECT_MESSAGE message)
{
#ifdef FIX_ALL_WARNINGS
	message;
#endif
	if (myDetectedNetworks[obj->id-NETWORK_NAME_BUTTON_1].isValid == NO)			//Pokud tlacitku neprislusi zadna zdetekovana sit
		return;																		//Konec
	NetworkNamesResult = obj->id-NETWORK_NAME_BUTTON_1;
	NetworkNamesInternal.State = NETWORK_NAMES_STOP;
}


/****************************************************************************
	Odezva ukoncovaciho tlacitka

	Vstup: 
		obj:			instance objektu
		message:		parametr volani

	Vystup
		neni

*****************************************************************************/
void EndNetworkButtonCallback(TScreenObject* obj ,OBJECT_MESSAGE message)
{
#ifdef FIX_ALL_WARNINGS
	message;obj;
#endif
	TDialogCfg cfg = {QUESTION,COUNTDOWN_NONE,100,(unsigned char*)STR_END_NETWORK_NAME_CONFIRMATION,30};	//sestaveni konfigurace dialogu
	MESSAGE dialogMessage = {CONFIG,&cfg};																	//Sestaveni konfiguracni zpravy
	SendTaskMessage(DIALOG,&dialogMessage);																	//Odeslani konfiguracni zpravy	
	NetworkNamesInternal.RunApplication(DIALOG);															//Spusteni dialogu
}


/****************************************************************************
	Inicializace aplikace

	Vstup: 
		neni

	Vystup
		neni

*****************************************************************************/
RESULT networkNamesInitialize(void)
{
	networkNamesScreen.Init(NETWORK_NAMES_OBJECT_COUNT,	networkNamesScreenObjects,&NetworkNamesScreenFont);									//Inicializace obrazovky
	NetworkNamesInternal.Init(NETWORK_NAMES,NETWORK_NAMES_XA,NETWORK_NAMES_YA,NETWORK_NAMES_XC,NETWORK_NAMES_YC,&networkNamesScreen,0,YES);	//Inicializace ridici struktury
		if (NetworkNamesInternal.Screen == 0)
		{
			return FAILED;
		}

		/****************************************************************************
			Definice objektu aplikace

		*****************************************************************************/
		NetworkNamesInternal.Screen->objects[NETWORK_NAMES_MAIN_SHAPE].Init(NETWORK_NAMES_XA,
																NETWORK_NAMES_YA,
																BACKGROUND,
																NETWORK_NAMES_MAIN_SHAPE,
																STR_NETWORK_NAMES,
																OBJECT_ENABLED,
																&NetworkNamesMainShapeParams,
																0,
																&DrawTextBox);
		for (char i = 0; i < 5; i++)
		{
			NetworkNamesInternal.Screen->objects[NETWORK_NAME_BUTTON_1+i].Init(	NETWORK_NAMES_XA+NETWORK_NAMES_BUTTONS_LEFT,
																	NETWORK_NAMES_YA+NETWORK_NAMES_BUTTONS_TOP+i*(NETWORK_NAMES_BUTTONS_HEIGHT+NETWORK_NAMES_BUTTONS_DISTANCE),
																	BUTTON,
																	NETWORK_NAME_BUTTON_1+i,
																	STR_NETWORK_NAMES,
																	OBJECT_ENABLED,
																	&NetworkNamesButtonsParams,
																	&ScannedNetworkButtonCallback,
																	&DrawNetworkNameButton);
		}
		NetworkNamesInternal.Screen->objects[NETWORK_NAME_BUTTON_OWN].Init(	NETWORK_NAMES_XA+NETWORK_NAMES_BUTTONS_LEFT,
																NETWORK_NAMES_YA+NETWORK_NAMES_BUTTONS_TOP+NETWORK_BUFFER_LENGTH*(NETWORK_NAMES_BUTTONS_HEIGHT+NETWORK_NAMES_BUTTONS_DISTANCE),	
																BUTTON,
																NETWORK_NAME_BUTTON_OWN,
																STR_NETWORK_NAMES_HIDDEN,
																OBJECT_ENABLED,
																&NetworkNamesButtonsShortParams,
																&HiddenNetworkButtonCallback,
																&DrawButton);
		NetworkNamesInternal.Screen->objects[NETWORK_NAME_BUTTON_BACK].Init(NETWORK_NAMES_XA+NETWORK_NAMES_BUTTONS_LEFT+NETWORK_NAMES_BUTTONS_SHORT_WIDTH+NETWORK_NAMES_BUTTONS_DISTANCE,
																NETWORK_NAMES_YA+NETWORK_NAMES_BUTTONS_TOP+NETWORK_BUFFER_LENGTH*(NETWORK_NAMES_BUTTONS_HEIGHT+NETWORK_NAMES_BUTTONS_DISTANCE),
																BUTTON,
																NETWORK_NAME_BUTTON_BACK,
																STR_EXIT,
																OBJECT_ENABLED,
																&NetworkNamesButtonsShortParams,
																&EndNetworkButtonCallback,
																&DrawButton);
	return PASSED;
}


/****************************************************************************
	Stavovy automat pro rizeni wifi modulu

	Priklad odpovedi modulu se seznamem siti

	Jetta,AP,06:14:6C:69:4A:7C,WPA,1,25
	RTL8186-default,AP,00:E0:4C:81:86:86,NONE,1,77
	dlink_test,AP,00:1C:F0:9A:63:7A,NONE,1,68
	Guest,AP,00:15:E9:0C:38:F2,WPA2,6,69
	ABC,AP,00:1C:F0:40:CC:60,NONE,6,65
	Yuval,AP,00:0E:2E:C6:B6:E1,NONE,6,62
	GANG_TEST,AP,00:17:3F:9F:89:6E,NONE,7,67
	Bora,AP,00:14:78:F7:11:BA,NONE,7,26
	3com_test,AP,00:0F:CB:FF:27:8F,NONE,7,81
	INET,AP,00:0F:CB:FF:7E:5D,WPA,7,82
	Blue-I The Lab,AP,00:1B:2F:57:65:62,WEP,7,45
	Mistral,AP,00:11:6B:3B:55:E2,WEP,9,27
	Sirocco,AP,00:18:4D:DE:D7:DF,WPA2,11,44
	Free Public WiFi,ADHOC,D2:B3:5B:06:CA:04,NONE,11,69
	BlueI,AP,00:0E:2E:55:39:A6,WEP,11,57
	private,AP,00:0E:2E:FD:F0:69,WPA,11,74
	I/OK

	Vstup: 
		neni

	Vystup
		neni

*****************************************************************************/
void NNHandleWifi(void)
{
	switch(nnAcquireState)
	{
	case NN_CLEAR:
		WifiSendCommand(DRV_WIFI_TEST);				//Odelani prikazu pro zjisteni stavu modulu
		nnAcquireState = NN_COMMUNICATING;			//prechod na dalsi stav
		break;
	case NN_COMMUNICATING:
        LedOn(LED_4);								//Indikace LED
		if (WifiBusy() == YES)						//Cekani na dokonceni prikazu
			break;
		if (WifiOK() == NO)							//Kontrola stavu po dokonceni
		{
            LedOff(LED_4);							//POkud je chyba
			nnAcquireState = NN_ERROR;				//Prejdi do chyboveho stavu
			break;
		}
        LedOff(LED_4);
		nnAcquireState = NN_REQUESTED;				//Prechod do dalsiho stavu
		WifiSendCommand(DRV_WIFI_DETECT);			//Odeslani prikazu k detekci
		break;
	case NN_REQUESTED:
        LedOn(LED_4);								//Indikace LED
		if (WifiBusy() == YES)						//Cekani na dokonceni
			break;
		if (WifiOK() == NO)							//Kontrola stavu
		{
			nnAcquireState = NN_ERROR;				//Prechod do chyboveho stavu
            LedOff(LED_4);							//Zhasnuti LED
			break;
		}
        LedOff(LED_4);								//Vypnuti LED
		nnAcquireState = NN_COMPLETED;				//Prechod do dalsiho stavu
		{
		NETWORK_STRING pos = NS_NAME;				//Parsovani prijateho textu
		
		int offset = 0;								//Inicializace parseru
		char tmpBuff[15] = "";						//Textovy zasobnik
		int lanNum = 0;								//Pocet siti			
		for (int i = 0; i < get_length(wifiBuffer); i++)	//Pro kazdy znak v prrijatych datech
		{
			switch(pos)								//POdle aktualni pozice v textu
			{
			case NS_NAME:							//Jmeno site
				if (wifiBuffer[i] == ',')			//Jmeno je ukonceno v textu carkou
				{
					pos = NS_TYPE;					//Dalsi polozkou je typ site
					offset = 0;						//Reset offsetu v docasnem zasobniku
				}
				else
					myDetectedNetworks[lanNum].ssid[offset++] = wifiBuffer[i];	//Kopie znaku do zasobniku
				break;
			case NS_TYPE:
				if (wifiBuffer[i] == ',')			//Typ je ukoncen carkou
				{
					pos = NS_MAC;					//Prechod na dalsi pozici - MAC
					if (offset > 3)					//Pokud je zkopirovany text z oblasti delsi nez 3
						myDetectedNetworks[lanNum].ssidType = SSID_ADHOC;	//je sit AD_HOC
					else
						myDetectedNetworks[lanNum].ssidType = SSID_AP;		//jinak AP
					offset = 0;						//Reset OFFSETU
				}
				offset++;							//posun v bufferu

				break;
			case NS_MAC:
				//MAC aktualne neni potreba - je ignorovana
				if (wifiBuffer[i] == ',')			//MAC je opet ukoncena carkou
				{
					pos = NS_CIP;					//prechod na dalsi cast informace o siti
					offset = 0;						//Reset offsetu
				}
				else
				{
					myDetectedNetworks[lanNum].mac[offset++] = wifiBuffer[i];		//kopie MAC
				}
				break;
			case NS_CIP:
				if (wifiBuffer[i] == ',')			//Typ sifrovani je ukoncen carkou
				{
					tmpBuff[offset] = 0;			//ukonceni textu v bufferu
					if (strcomp((unsigned char*)tmpBuff,(unsigned char*)"WPA2") || strcomp((unsigned char*)tmpBuff,(unsigned char*)"WPA|WPA2"))	//rozpoznani typu sifrovani
					{
						myDetectedNetworks[lanNum].cipType = CIP_WPA2;
					}
					else if (strcomp((unsigned char*)tmpBuff,(unsigned char*)"WPA"))
					{
						myDetectedNetworks[lanNum].cipType = CIP_WPA;
					}
					else if (strcomp((unsigned char*)tmpBuff,(unsigned char*)"WEP"))
					{
						myDetectedNetworks[lanNum].cipType = CIP_WEP;
					}
					pos = NS_CHAN;
					offset = 0;
				}
				else
				{
					tmpBuff[offset++] = wifiBuffer[i];		//kopie znaku
				}

				break;
			case NS_CHAN:					
					if (wifiBuffer[i] == ',')				//Stale stejny system - konec carkou
					{
						pos = NS_STR;						//presun na dalsi pozici
						tmpBuff[offset] = 0;
						offset = 0;
															//zjisteni kanalu
						myDetectedNetworks[lanNum].channel = (char)char2int(tmpBuff);
					}
					else
					{
						tmpBuff[offset++] = wifiBuffer[i];
					}			
				break;
			case NS_STR:									
				if (wifiBuffer[i] == '\n')					//Sila signalu
				{
					pos = NS_NAME;							//Prechod opet na zacatek automatu
					
					tmpBuff[offset] = 0;
					offset = 0;
					myDetectedNetworks[lanNum].isValid = YES;
					myDetectedNetworks[lanNum++].strength = (char)char2int(tmpBuff);
					
				}
				else
				{
					tmpBuff[offset++] = wifiBuffer[i];
				}
				break;
			}
			if (lanNum >= NETWORK_BUFFER_LENGTH)	//POkud bylo zdetekovano prilos mnoho siti, ukonci
			{
				break;
			}
		}
		for (int i = 0; i < (lanNum>NETWORK_BUFFER_LENGTH?NETWORK_BUFFER_LENGTH:lanNum); i++)		//Aktualizace zaplnenych policek
		{
			NetworkNamesInternal.Screen->UpdateObject((short)i+NETWORK_NAME_BUTTON_1);
		}
		}
		break;
	case NN_COMPLETED:
        LedOff(LED_4);			//Hotovo
		break;
	default:
		break;

	}
}

/****************************************************************************
	Ridici stavovy automat aplikace

	Vstup: 
		neni

	Vystup
		neni

*****************************************************************************/
void AppNetworkNames(void)
{
	switch (NetworkNamesInternal.State)
	{
	case NETWORK_NAMES_NOT_INITIALIZED:
		break;
	case NETWORK_NAMES_INITIALIZE:
		NetworkNamesInternal.State = NETWORK_NAMES_READY;			//Prechod do dalsiho stavu
		if (networkNamesInitialize() !=PASSED)						//Inicializace
		{
			DebugOut("NetworkNames could not be initialized. Falling to error.\n");
			NetworkNamesInternal.State = NETWORK_NAMES_ERROR;		//Chyba pri inicializaci => selhani
			break;
		}
		DebugOut("NetworkNames initialized.\n");
		break;
	case NETWORK_NAMES_RESTART:
		NetworkNamesInternal.Startup();								//Akce nutne pro spusteni
		ClearNetworkBuffer();										//reset bufferu
		nnAcquireState = NN_CLEAR;									//Reset stavu wifi
		NetworkNamesInternal.State = NETWORK_NAMES_DRAW;			//prechod do dalsiho stavu
		break;
	case NETWORK_NAMES_DRAW:
		NetworkNamesInternal.Screen->Draw();						//Vykresleni aplikace
		NetworkNamesInternal.State = NETWORK_NAMES_ACTIVE;			//Pfrechod do aktivniho stavu
		break;
	case NETWORK_NAMES_ACTIVE:
		NetworkNamesInternal.ProceedClick();						//Zpracovani kliknuti
		NNHandleWifi();												//Zjisteni bezdratovych siti z okoli
		break;
	case NETWORK_NAMES_WAITING:
		switch(NetworkNamesInternal.TryToResume())					//Cekani na navrat spustene aplikace
		{
		case KEYBOARD:
			if (KeyboardBuffer.TextBuffer[0] != 0)					//pokud byl zadan vlastni nazev
			{
				NetworkNamesResult = NETWORK_NAME_BUTTON_OWN;		//Ukonci aplikaci s vysledkem "Vlastni nazev"
				NetworkNamesInternal.State = NETWORK_NAMES_STOP;
			}
			break;
		case DIALOG:
			if ((ANSWER)SendTaskMessage(DIALOG,&DEFAULT_MSG_GET_RESULT)==YES)	//Navrat z dialogu
			{
				NetworkNamesResult = NETWORK_NAME_BUTTON_BACK;					//POkud byla kladna odpoved
				NetworkNamesInternal.State = NETWORK_NAMES_STOP;				//ukonci
			}
			break;
		case NONE:
			break;
		default:
			DebugOut("Returned to NetworkNames from unknown application.\n");
			break;
		}
		break;
	case NETWORK_NAMES_READY:
		break;
	case NETWORK_NAMES_STOP:
		NetworkNamesInternal.Stop();											//Akce pro korektni ukonceni
		break;
	case NETWORK_NAMES_ERROR:
		break;
	default:
		break;
	}
}