#include <windows.h>

#include <fstream>
#include <iostream>
#include <conio.h>
#include <string>
#include <time.h>
#include <shellapi.h>
#include <direct.h>

using namespace std;

#ifndef CDROM
#include "libsearch4j.h"
#endif

//globals
HBITMAP g_splash = NULL; //the main splash bitmap
HFONT g_hfFont = NULL; //the main splash bitmap
char step[10] = "TMP"; //the current step
char progress[4] = "0"; //progress in the current step
HWND splashWnd;
HINSTANCE mainInstance;
int mainCmdShow;
bool window_loaded = false;

void set_splash_step( char* new_step ) {
	strcpy( step, new_step );
	InvalidateRect(splashWnd, NULL, FALSE);
	UpdateWindow(splashWnd);
}

void set_splash_progress( int percent_progress ) {
	percent_progress = ( percent_progress / 10 ) * 10;
	char p[4] = {0};
	strcpy( progress, itoa( percent_progress, p, 10 ) );
	InvalidateRect(splashWnd, NULL, FALSE);
	//RedrawWindow(splashWnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ERASENOW | RDW_UPDATENOW | RDW_INTERNALPAINT);
	UpdateWindow(splashWnd);
}

void spoof_progress( int interval ) {
	int tenPercentWait = interval / 11;
	for ( int i=0; i <= 100; i+=10 ) {
		set_splash_progress( i );
		Sleep( tenPercentWait );
	}
}

//extracts a resource in chunks
int extractResource( const char * basename, const char * type, char * file ) {

	HMODULE hModule = GetModuleHandle(NULL);
	set_splash_progress( 0 );

	int no_chunks;
	bool chunk_available = true;
	for ( no_chunks = 0; chunk_available; no_chunks++ ) {

		//construct the chunk name
		char chunkname[127] = {0};
		strcpy( chunkname, basename );
		strcat( chunkname, "_" );
		char chunknum[5] = {0};
		itoa( no_chunks+1, chunknum, 10 );
		strcat( chunkname, chunknum );
		
		if ( FindResource(hModule, chunkname, type) == NULL ) {
			chunk_available = false;
			break;
		}
	}
	
	if ( no_chunks == 0 ) {
		return 1;
	}
	
	for ( int i=0; i<no_chunks; i++ ) {
	
		//construct the chunk name
		char chunkname[127] = {0};
		strcpy( chunkname, basename );
		strcat( chunkname, "_" );
		char chunknum[5] = {0};
		itoa( i+1, chunknum, 10 );
		strcat( chunkname, chunknum );
		
		//find it
		HRSRC hRsrc = FindResource(hModule, chunkname, type);
		if (hRsrc == NULL ) return 1; //couldn't find the chunk
		
		//load it
		HGLOBAL hGlobal = LoadResource(hModule, hRsrc);
		if (hGlobal == NULL) return 1; //couldn't lock
		
		//lock it
		BYTE* pBytes = (BYTE*) LockResource(hGlobal);
		if (pBytes == NULL) return 1; //couldn't lock
		
		//put it on disk
		DWORD dwLength = SizeofResource(hModule, hRsrc);
		ofstream fStream(file, ios::binary | ios::out | ios::app );
		fStream.write((char*) pBytes, dwLength);
		fStream.close();
		
		//unload
		UnlockResource(hGlobal);
		FreeResource(hGlobal);
		
		//update the progress
		set_splash_progress( i * 100 / no_chunks );
		
	}

	return 0;

}

//the splash window procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
	switch(msg) {

		case WM_CREATE: {
			//load in the reusable resources
			g_splash = LoadBitmap(GetModuleHandle(NULL),"SPLASH");
			g_hfFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
			if(g_splash == NULL) {
				MessageBox(hwnd, "Could not load splash bitmap!", "Error", MB_OK | MB_ICONEXCLAMATION);
			}
			
			//center the window automatically
			RECT rect;
			GetWindowRect(hwnd, &rect);
			int x = (GetSystemMetrics(SM_CXSCREEN) - (rect.right - rect.left)) / 2;
			int y = (GetSystemMetrics(SM_CYSCREEN) - (rect.bottom - rect.top)) / 2;
			SetWindowPos(hwnd, HWND_TOP, x, y, 0, 0, SWP_NOSIZE);
		}
		
		case WM_PAINT: {
			// Don't use MessageBox from inside WM_PAINT
		
			//load in the correct resources
			HBITMAP g_progress = NULL;
			HBITMAP g_step = NULL;
			char progress_res[20] = "PROGRESS_";
			strcat( progress_res, progress );
			char step_res[20] = "STEP_";
			strcat( step_res, step );
			g_progress = LoadBitmap(GetModuleHandle(NULL), progress_res );
			g_step = LoadBitmap(GetModuleHandle(NULL), step_res );
			if( g_progress == NULL || g_step == NULL ) {
				//MessageBox(hwnd, "Could not load an image!", "Error", MB_OK | MB_ICONEXCLAMATION);
			}
		
			BITMAP bm; //holds info about the width and height
			PAINTSTRUCT ps;
			
			HDC hdc = BeginPaint(hwnd, &ps);
			HDC hdcMem = CreateCompatibleDC(hdc);
			
			//paint the main splash screen
			GetObject(g_splash, sizeof(bm), &bm);
			HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, g_splash);
			BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
						
			//paint in the progress
			GetObject(g_progress, sizeof(bm), &bm);
			SelectObject(hdcMem, g_progress);
			BitBlt(hdc, 0, 270, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
			
			//paint in the step with transparency
			GetObject(g_step, sizeof(bm), &bm);
			SelectObject(hdcMem, g_step);
			BitBlt(hdc, 0, 276, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCPAINT);
			
			//print the step with text
			RECT rcClient;
			GetClientRect(hwnd, &rcClient);
			//DrawText(hdc, "This is the step", -1, &rcClient, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
			
			//restore the hdc
			SelectObject(hdcMem, hbmOld);
			
			//release resources
			DeleteDC(hdcMem);
			EndPaint(hwnd, &ps);
			DeleteObject(g_progress);
			DeleteObject(g_step);
			
		}
		break;
		
		/*
		case WM_LBUTTONDOWN: {
			DeleteObject(g_splash);
			PostQuitMessage(0);
		}
		break;
		*/
		
		case WM_CLOSE:
			DestroyWindow(hwnd);
			break;
		
		case WM_DESTROY:
			DeleteObject(g_splash);
			PostQuitMessage(0);
			break;
		
		default:
			return DefWindowProc(hwnd, msg, wParam, lParam);
	}

	return 0;

}

//function to process the window
DWORD WINAPI ProcessWindow( LPVOID lpParam ) {
	
	const char g_szClassName[] = "splash";
	WNDCLASSEX wc;
	MSG Msg;

	//Step 1: Registering the Window Class
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = 0;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = mainInstance;
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = g_szClassName;
	wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
	if(!RegisterClassEx(&wc)) {
		MessageBox(NULL, "Window Registration Failed!", "Error!",
		MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}

	// Step 2: Creating the Window
	splashWnd = CreateWindowEx( WS_EX_TOOLWINDOW, g_szClassName, "", WS_POPUP | SS_BITMAP, 0, 0, 420, 300, NULL, NULL, mainInstance, NULL);
	if( splashWnd == NULL ) {
		MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}
	ShowWindow(splashWnd, mainCmdShow);
	UpdateWindow(splashWnd);

	window_loaded = true;
	
	// Step 3: The Message Loop
	while(GetMessage(&Msg, NULL, 0, 0) > 0) {
		TranslateMessage(&Msg);
		DispatchMessage(&Msg);
	}
	return 0;
} 

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
	
	const int SPOOF_TIME = 1000;
	string command = lpCmdLine;

	if((command.compare("-extract") == 0) || (command.compare("--extract") == 0) || (command.compare("-x") == 0)){
		printf("----------------------------\n");
		printf("Extracting java installer...\n");
		printf("----------------------------\n");
		int result = extractResource( "JAR", "JAR", "greenstone.jar" );
		
		if(result == 0){
			printf("\n");
			printf("Extraction complete\n");
			printf("You can now type java -jar greenstone.jar to run the installer from the command line\n");
			return 0;	
		}
		else{
			return 1;
		}
	}
	else if(command.compare("-text") == 0 || command.compare("text") == 0 )
	{
		cerr << "-text is depricated, please use -extract and then run \"java -jar greenstone.jar text\"" << endl;
	}
	
	srand( time(0) );
	mainInstance = hInstance;
	mainCmdShow = nCmdShow;
	
	//start the window
	HANDLE hThread;
	DWORD dwThreadId, dwThrdParam = 1;
	hThread = CreateThread( 
       NULL,                        // no security attributes 
       0,                           // use default stack size  
       ProcessWindow,               // thread function 
       &dwThrdParam,	            // argument to thread function 
       0,                           // use default creation flags 
       &dwThreadId);                // returns the thread identifier 
	if ( hThread == NULL ) {
		return -1;
		//ErrorExit( "CreateThread failed." );
	}
	CloseHandle( hThread );
	
	while ( !window_loaded ) {
		Sleep( 100 );
	}
	
	//create the temp folder and change to it
	set_splash_step( "TMP" );
	spoof_progress( SPOOF_TIME );
	
	//get the original working directory
	char* owdChar = _getcwd(NULL, 1024);
	string owd = "";
	owd.append( owdChar );
	free(owdChar);
	
	//strip trailing slash
	if ( owd.substr(owd.length()-1,1).compare( "\\" ) == 0 ) {
		owd = owd.substr(0, owd.length()-1);
	}
		
	//using the users TMP directory for temp files rather than the current directory so
	//installation from cdrom works
	char* tmp = getenv( "TMP" );
	char id[6]; for( int i=0; i<5; i++ ) id[i] = (char)((rand()%26) + 97); id[5] = '\0'; //id for this instance
	string tempdir = ""; tempdir.append( tmp ); tempdir.append( "\\Greenstone-" ); tempdir.append( id );
	const char* tmpdir = tempdir.c_str();
	if ( _mkdir( tmpdir ) != 0 ) {
		string title = "Failed to create the temp folder.";
		string message = "Failed to create the temp folder.\nPlease set the environment variable TMP to a folder you have permission to write to.";
		//ShowWindow(splashWnd, SW_HIDE); //hide splash screen
		MessageBox(splashWnd, message.c_str(), title.c_str(), MB_OK);
		return -1;
	}
	//SetFileAttributes(tmpdir, FILE_ATTRIBUTE_HIDDEN);
	SetCurrentDirectory(tmpdir);
	// delete tmp; //  should be tmpdir? ??? (deallocate memory)
	
	#ifdef CDROM

	//ask if they want to install, then do it if so
	int choice = MessageBox(splashWnd, "Install Greenstone?", "Greenstone", MB_OKCANCEL | MB_ICONQUESTION );
	if ( choice == IDOK ) {

		//prepare to launch the jar
		set_splash_step( "LAUNCHING" );
		spoof_progress( SPOOF_TIME );
	
		string cmd = "\"";
		cmd.append( owd );
		cmd.append( "\\Java\\Windows\\jdk\\bin\\java.exe\" \"-Dorig.dir=" );
		cmd.append( owd );
		cmd.append( "\" -jar \"" );
		cmd.append( owd );
		cmd.append( "\\Java\\Jars\\windows.jar\"" );
		char cmdLpstr[1024];
		strcpy( cmdLpstr, cmd.c_str() );
		
		
		STARTUPINFO si;
		PROCESS_INFORMATION pi;
		memset(&pi, 0, sizeof(pi));
		memset(&si, 0, sizeof(si));
		si.cb = sizeof(si);
		DWORD dwExitCode = -1;

		//hide splash screen
		ShowWindow(splashWnd, SW_HIDE);

		//launch the jar
		bool result;
		result = CreateProcess(NULL, cmdLpstr, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);

		WaitForSingleObject(pi.hProcess, INFINITE);
		GetExitCodeProcess(pi.hProcess, &dwExitCode);
		CloseHandle(pi.hThread);
		CloseHandle(pi.hProcess);

	}	
	#else

	//rip out java archive if it is bundled
	set_splash_step( "XJAVA" );
	int javaIsBundled = (extractResource( "JAVA", "EXE", "@java.installer@" ) == 0);
	if ( javaIsBundled ) {
		process( "@java.installer@", false );
	}

	//find java
	set_splash_step( "SEARCHING" );
	spoof_progress( SPOOF_TIME );
	bool use_minimum = true;
	Jvm minimum;
	minimum.setVersionFromString( "@java.min.version@" );
	string phint = "@java.extracted@";
	string hint = "";
	bool verbose = true;
	Jvm foundJvm;
	bool jvmFound = find( foundJvm, use_minimum, minimum, false, false, phint, hint, verbose );
	
	//if the jvm was not found, try to fix it and find it
	if ( !jvmFound ) {

		//did not find a good java
		string message = "This version of Greenstone requires java @java.min.version@ or greater, but ";
		
		//tell them if java is absent or just too old
		if ( find( foundJvm, false, minimum, false, false, "", "", false ) ) {
			message.append( "your java is too old.");
		} else {
			message.append( "java could not be found on your computer." );
		}
		message.append( "\n" );
		
		string title = "Installation Failed: Couldn't find java";
		//ShowWindow(splashWnd, SW_HIDE); //hide splash screen
		MessageBox(splashWnd, message.c_str(), title.c_str(), MB_OK);

	//if java was found, carry on
	} else {

		//extract the jar
		string jarLocation = "greenstone.jar";
		
		set_splash_step( "XJAR" );
		extractResource( "JAR", "JAR", (char*) jarLocation.c_str() );

		//launch the jar
		set_splash_step( "LAUNCHING" );
		spoof_progress( SPOOF_TIME );
		string cmd = "\"";
		cmd.append( foundJvm.getWinExecutable() );
		cmd.append( "\" \"-Dorig.dir=" );
		cmd.append( owd );
		cmd.append( "\" -jar greenstone.jar" );
		
		
		//hide splash screen
		ShowWindow(splashWnd, SW_HIDE);
		
		//run the jar
		int launch_exit_code = process( cmd, true );
		
		//report how it went
		if ( launch_exit_code != 0 ) {
			string title = "Installation Error";
			string message = "The installation exited with an error. Java may not be installed properly, or the installation may have been interrupted partway through. Please try again.";
			//ShowWindow(splashWnd, SW_HIDE); //hide splash screen
			MessageBox(NULL, message.c_str(), title.c_str(), MB_OK);
		}
	}
	#endif
	
	//clean up the temp directory
	_unlink( "ant.install.log" );
	#ifndef CDROM
	_unlink( "greenstone.jar" );
	_unlink( "@java.installer@" );
	process( "cmd.exe /c rd /s /q @java.extracted@", false );
	#endif

	SetCurrentDirectory( ".." );
	_rmdir( tempdir.c_str() );
	
	return 0;
	
}
