#include "StdAfx.h"
#include "LogParser.h"

#include <iostream>

#include "WindowFinder.h"

LogParser::LogParser(_TCHAR* runCommand, unsigned int startupTime, TestResults * results, bool useDefaultDelay) : runCommand(runCommand), startupTime(startupTime), results(results), useDefaultDelay(useDefaultDelay)
{
	workingPath = NULL;
}

LogParser::~LogParser(void)
{
	
}



HRESULT STDMETHODCALLTYPE LogParser::startElement( 
			wchar_t __RPC_FAR *pwchNamespaceUri,
			int cchNamespaceUri,
			wchar_t __RPC_FAR *pwchLocalName,
			int cchLocalName,
			wchar_t __RPC_FAR *pwchRawName,
			int cchRawName,
			MSXML2::ISAXAttributes __RPC_FAR *pAttributes)
{
	std::wstring localName(pwchLocalName);
	if( localName.compare(L"session")==0 ) {
		sessionId = GetAttributeValue(pAttributes, L"id", L"");
		std::wcout << L"================================================" << std::endl;
		std::wcout << L"starting session " << sessionId << std::endl;
		result.sessionPass = true;
		result.errorMessage = L"";
		result.msgNumber = 0;
		currentMessage = 0;
		std::wcout << L"executing " << runCommand << std::endl;
		PROCESS_INFORMATION pi;
		STARTUPINFO si;
		ZeroMemory(&pi, sizeof(pi));
		ZeroMemory(&si, sizeof(si));

		CreateProcess(NULL, runCommand, NULL, NULL, FALSE, 0, NULL, workingPath, &si, &pi);

		hProcess = pi.hProcess;
		CloseHandle(pi.hThread);
		
		std::wcout << L"waiting " << startupTime << L" ms for application under test to intialize" << std::endl;
		Sleep(startupTime);
		std::wcout << L"replay starting..." << std::endl;
	}
	else if( localName.compare(L"msg")==0 ) {
		std::wstring type = GetAttributeValue(pAttributes, L"type", L"");
		msgType = _wtoi(type.c_str());
		std::wstring lParamStr = GetAttributeValue(pAttributes, L"LPARAM", L"");
		lParam = _wtoi(lParamStr.c_str());
		std::wstring wParamStr = GetAttributeValue(pAttributes, L"WPARAM", L"");
		wParam = _wtoi(wParamStr.c_str());
		std::wstring delayStr = GetAttributeValue(pAttributes, L"delay", L"");
		delay = _wtoi(delayStr.c_str());
		currentWindow = NULL;
		currentParent = NULL;
	}
	else if( localName.compare(L"window")==0 ) {
		WindowData * winData = new WindowData;
		winData->name = GetAttributeValue(pAttributes, L"name", L"");
		winData->className = GetAttributeValue(pAttributes, L"class", L"");
		std::wstring resourceIdStr = GetAttributeValue(pAttributes, L"resourceId", L"");
		winData->resourceId = _wtoi(resourceIdStr.c_str());
		std::wstring isModalStr = GetAttributeValue(pAttributes, L"isModal", L"");
		if( isModalStr.compare(L"true")==0 ) {
			winData->isModal = true;
		} else {
			winData->isModal = false;
		}
		winData->child = NULL;
		if( currentWindow==NULL ) {
			currentWindow = winData;
		} else {
			currentParent->child = winData;
		}
		currentParent = winData;
	}
	else if( localName.compare(L"textEquals")==0 ) {
		expectedValue = GetAttributeValue(pAttributes, L"expectedValue", L"");
	}
	else if( localName.compare(L"fileEquals")==0) {
		actualFile = GetAttributeValue(pAttributes, L"actualFile", L"");
		expectedFile = GetAttributeValue(pAttributes, L"expectedFile", L"");
	}

	return S_OK;
}


HRESULT STDMETHODCALLTYPE LogParser::endElement( 
			wchar_t __RPC_FAR *pwchNamespaceUri,
			int cchNamespaceUri,
			wchar_t __RPC_FAR *pwchLocalName,
			int cchLocalName,
			wchar_t __RPC_FAR *pwchRawName,
			int cchRawName)
{
	std::wstring localName(pwchLocalName);
	if( localName.compare(L"session")==0 ) {
		std::wcout << L"session completed" << std::endl;
		results->addResult(sessionId, result);
		BOOL retVal = TerminateProcess(hProcess, 0);
		if( retVal!=0 ) {
			std::wcout << L"application terminated" << std::endl;
		}
		CloseHandle(hProcess);
	} 
	else if( localName.compare(L"msg")==0 ) {
		currentMessage++;
		WindowFinder finder;
		HWND hwnd = finder.find(currentWindow);
		// check if window was found, if not test has failed
		if( result.sessionPass ) {
			result.sessionPass = false;
			result.errorMessage = finder.getErrorMessage();
			result.msgNumber = currentMessage;
		}

		sendMessage(hwnd);
		deleteWindowData(currentWindow);
		currentWindow = NULL;
	} 
	else if( localName.compare(L"LPARAM")==0 ) {
		WindowFinder finder;
		HWND hwnd = finder.find(currentWindow);
		lParam = (LPARAM) hwnd;
		deleteWindowData(currentWindow);
		currentWindow = NULL;
	}
	else if( localName.compare(L"WPARAM")==0 ) {
		WindowFinder finder;
		HWND hwnd = finder.find(currentWindow);
		wParam = (WPARAM) hwnd;
		deleteWindowData(currentWindow);
		currentWindow = NULL;
	}
	else if( localName.compare(L"textEquals")==0 ) {
		WindowFinder finder;
		HWND hwnd = finder.find(currentWindow);

		wchar_t* wstr = new wchar_t[256];
		wstr[0] = '\0';
		
		//GetWindowText is not working properly for EditControls in other applications -> send WM_GETTEXT instead
		SendMessage(hwnd, WM_GETTEXT, 255, (LPARAM)wstr);
		std::wstring windowText = wstr;

		if(expectedValue == windowText) std::wcout << std::endl << L"textEquals passed (expected value: " << expectedValue.c_str() << ")" << std::endl << std::endl;
		else std::wcout << std::endl << L"textEquals failed (expected value: " << expectedValue.c_str() << ", windowText: " << windowText << ")" << std::endl << std::endl;
		deleteWindowData(currentWindow);
		currentWindow = NULL;
	}
	else if( localName.compare(L"fileEquals")==0) {
		std::ifstream f1(actualFile.c_str(), std::ios_base::in | std::ios_base::binary);
		if(f1 == NULL) {

			std::wcout << std::endl << L"fileEquals failed because " << actualFile << " is not available!"<< std::endl << std::endl;
			return S_OK;
		}

		std::ifstream f2(expectedFile.c_str(), std::ios_base::in | std::ios_base::binary);
		if(f2 == NULL) {
			std::wcout << std::endl << L"fileEquals failed because " << expectedFile << " is not available!" << std::endl << std::endl;
			return S_OK;
		}

		f1.seekg(0, std::ios_base::end);
		f2.seekg(0, std::ios_base::end);
		std::streamsize length1 = f1.tellg();
		std::streamsize length2 = f2.tellg();

		bool passed = true;

		if (length1 != length2) { // Non equal length -> files differ
			passed = false;
		}
		else {
			f1.seekg(0);
			f2.seekg(0);

			const std::streamsize blocksize = 4096;
			char block1[blocksize], block2[blocksize];

			for (std::streamsize counter=length1; counter > 0; counter -= blocksize)
			{				
				f1.read(block1, blocksize);
				f2.read(block2, blocksize);

				if(memcmp(block1, block2, blocksize) != 0) { //block are non equal -> files differ
					passed = false;
					break;
				}
			}
		}

		if(passed) {
			std::wcout << std::endl << L"FileEquals passed" << std::endl << std::endl;
		}
		else {
			std::wcout << std::endl << L"fileEquals failed (expectedFile: " << expectedFile.c_str() << ", actualFile: " << actualFile << ")" << std::endl << std::endl;
		}
		
		f1.close();
		f2.close();
	}

	return S_OK;
}

std::wstring LogParser::GetAttributeValue(MSXML2::ISAXAttributes __RPC_FAR *pAttributes,
							   std::wstring name, std::wstring defvalue)
{
	// get the number of attributes
	int length = 0;
	pAttributes->getLength(&length);

	// enumerate over all attributes
	for ( int i=0; i<length; i++ ) 
	{
		wchar_t *attrname = NULL, * attrvalue = NULL;
		int namelen = 0, valuelen = 0;

		// get the local name of the current attribute
		pAttributes->getLocalName(i,&attrname,&namelen);
		// get the value of the current attribute
		pAttributes->getValue(i,&attrvalue,&valuelen);
		// if current attribute is the one needed return its value
		if(name.compare(std::wstring(attrname,namelen)) == 0)
			return std::wstring(attrvalue, valuelen);
	}

	// attribute not found; return the default value
	return defvalue;
}

void LogParser::sendMessage(HWND hwnd) {
	std::wcout << L"  Sending " << msgType << L" to " << hwnd << "L - LPARAM: " << lParam << L" - WPARAM: " << wParam << std::endl;
	PostMessage(hwnd, msgType, wParam, lParam);
	if( useDefaultDelay ) {
		Sleep(defaultMsgDelay);
	} else {
		Sleep(delay);
	}
}

void LogParser::setWorkingPath(TCHAR * workingPath) {
	this->workingPath = workingPath;
}