#include "StdAfx.h"

#include "../../UI/NSIS/ExtractCallbackConsole.h"
#include "Common/StringConvert.h"

#pragma warning(disable: 4100)

#define IDC_PROGRESS                    1004
#define IDC_INTROTEXT                   1006

#define EXTRACTFUNC(funcname) extern "C" { \
void  __declspec(dllexport) __cdecl funcname(HWND hwndParent, int string_size, \
                                      TCHAR *variables, stack_t **stacktop, \
                                      extra_parameters *extra) \
{ \
	EXDLL_INIT();\
	g_lastVal = -1; \
	g_hwndParent=hwndParent; \
	HWND hwndDlg = FindWindowEx(g_hwndParent, NULL, TEXT("#32770"), NULL); \
	g_hwndProgress = GetDlgItem(hwndDlg, IDC_PROGRESS); \
	g_hwndText = GetDlgItem(hwndDlg, IDC_INTROTEXT); \
	TCHAR sArchive[1024], *outDir = getuservariable(INST_OUTDIR); \
	popstring(sArchive); \
	g_pluginExtra = extra; \

#define EXTRACTFUNCEND } }

HINSTANCE g_hInstance2;
HWND g_hwndParent;
HWND g_hwndProgress;
HWND g_hwndText;
extra_parameters *g_pluginExtra;

void DoInitialize();
int DoExtract(LPTSTR archive, LPTSTR dir, bool overwrite, bool expath, ExtractProgressHandler epc, const UStringVector& skipPatterns);
int DoExtractWithFile(LPTSTR archive, LPTSTR dir, bool overwrite, bool expath, ExtractProgressWithFileHandler epc, const UStringVector& skipPatterns);

int g_progressCallback = -1;
int g_lastVal = -1;
TCHAR* g_sDetails;

static void PopSkipPatterns(UStringVector& skipPatterns)
{
	TCHAR *buf = new TCHAR[g_stringsize];
	while (popstring(buf) == 0 && lstrlen(buf) > 0)
	{
#ifdef UNICODE
		skipPatterns.Add(UString(buf));
#else
		skipPatterns.Add(MultiByteToUnicodeString(AString(buf)));
#endif
	}
	delete[] buf;
}

int GetPercentComplete(UInt64 completedSize, UInt64 totalSize)
{
	if (totalSize == 0) return 0;
	const int nsisProgressMax = 30000;
	int val = (int)((completedSize*nsisProgressMax)/totalSize);
	if (val < 0) return 0;
	if (val > nsisProgressMax) return nsisProgressMax;
	return val;
}

void SimpleProgressHandler(UInt64 completedSize, UInt64 totalSize)
{
	int val = GetPercentComplete(completedSize, totalSize);
	if (g_lastVal != val)
		SendMessage(g_hwndProgress, PBM_SETPOS, g_lastVal = val, 0);
}

void CallbackProgressHandler(UInt64 completedSize, UInt64 totalSize)
{
	int val = 0;

	if (totalSize > 0)
	{
		val = GetPercentComplete(completedSize, totalSize);

		static TCHAR buf[32];
		wsprintf(buf, TEXT("%lu"), totalSize);
		pushstring(buf);
		wsprintf(buf, TEXT("%lu"), completedSize);
		pushstring(buf);
		g_pluginExtra->ExecuteCodeSegment(g_progressCallback-1, 0);
	}

	if (g_lastVal != val)
		SendMessage(g_hwndProgress, PBM_SETPOS, g_lastVal = val, 0);
}

void CallbackFileProgressHandler(UInt64 completedSize, UInt64 totalSize, const wchar_t *fileName)
{
	int val = 0;

	if (totalSize > 0)
		val = GetPercentComplete(completedSize, totalSize);

	// Notify NSIS only when a new file starts (filename changes)
	if (fileName && fileName[0])
	{
		static TCHAR fileNameBuf[MAX_PATH];
		static TCHAR prevFileNameBuf[MAX_PATH];
		static TCHAR buf[32];

		lstrcpyn(fileNameBuf, (const TCHAR*)fileName, MAX_PATH);
		if (lstrcmp(fileNameBuf, prevFileNameBuf) != 0)
		{
			lstrcpy(prevFileNameBuf, fileNameBuf);
			pushstring(fileNameBuf);
			wsprintf(buf, TEXT("%lu"), (DWORD)totalSize);
			pushstring(buf);
			wsprintf(buf, TEXT("%lu"), (DWORD)completedSize);
			pushstring(buf);
			g_pluginExtra->ExecuteCodeSegment(g_progressCallback-1, 0);
		}
	}

	if (g_lastVal != val)
		SendMessage(g_hwndProgress, PBM_SETPOS, g_lastVal = val, 0);
}

void DetailsProgressHandler(UInt64 completedSize, UInt64 totalSize)
{
	int val = 0;

	if (totalSize > 0)
	{
		val = GetPercentComplete(completedSize, totalSize);

		TCHAR* buf = new TCHAR[g_stringsize];
		TCHAR* buf2 = new TCHAR[g_stringsize];
		wsprintf(buf, TEXT("%d%% (%d / %d MB)"), (int)(val?val/300:0), (int)(completedSize?completedSize/(1024*1024):0), (int)(totalSize/(1024*1024)));
		wsprintf(buf2, g_sDetails, buf);
		SetWindowText(g_hwndText, buf2);
		delete[] buf;
		delete[] buf2;
	}

	if (g_lastVal != val)
		SendMessage(g_hwndProgress, PBM_SETPOS, g_lastVal = val, 0);
}

EXTRACTFUNC(Extract)
{
	UStringVector skipPatterns;
	PopSkipPatterns(skipPatterns);
	DoExtract(sArchive, outDir, true, true, (ExtractProgressHandler)SimpleProgressHandler, skipPatterns);
}
EXTRACTFUNCEND

EXTRACTFUNC(ExtractWithDetails)
{
	g_sDetails = new TCHAR[string_size];
	popstring(g_sDetails);
	UStringVector skipPatterns;
	PopSkipPatterns(skipPatterns);
	DoExtract(sArchive, outDir, true, true, (ExtractProgressHandler)DetailsProgressHandler, skipPatterns);
	delete[] g_sDetails;
}
EXTRACTFUNCEND

EXTRACTFUNC(ExtractWithCallback)
{
	g_progressCallback = popint();
	UStringVector skipPatterns;
	PopSkipPatterns(skipPatterns);
	DoExtract(sArchive, outDir, true, true, (ExtractProgressHandler)CallbackProgressHandler, skipPatterns);
}
EXTRACTFUNCEND

EXTRACTFUNC(ExtractWithFileCallback)
{
	g_progressCallback = popint();
	UStringVector skipPatterns;
	PopSkipPatterns(skipPatterns);
	DoExtractWithFile(sArchive, outDir, true, true, CallbackFileProgressHandler, skipPatterns);
}
EXTRACTFUNCEND

extern "C" BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
	(void)lpReserved;
	g_hInstance2=(HINSTANCE)hInst;
	if (ul_reason_for_call == DLL_PROCESS_ATTACH)
	{
		DoInitialize();
	}
	return TRUE;
}
