#include "burner.h"

static HWND hProgressDlg = NULL;
static HANDLE hProgressThread = NULL;
static DWORD dwProgressThreadID = 0;

int nProgressPosBurn, nProgressPosBurner;
int nProgressMin, nProgressMax;

static HANDLE hEvent = NULL;

// ----------------------------------------------------------------------------
// Callbacks for burn library

static int __cdecl ProgressSetRangeBurn(double dProgressRange)
{
	if (hProgressThread == NULL || hProgressDlg == NULL) {
		return 1;
	}

	nProgressMin = -(int)((double)nProgressMax * dProgressRange);
	SendDlgItemMessage(hProgressDlg, IDC_WAIT_PROG, PBM_SETRANGE32, nProgressMin, nProgressMax);

	return 0;
}

static int __cdecl ProgressUpdateBurn(double dProgress, const TCHAR* pszText, bool bAbs)
{
	if (hProgressThread == NULL || hProgressDlg == NULL) {
		return 1;
	}

	if (pszText) {
		SendDlgItemMessage(hProgressDlg, IDC_WAIT_LABEL_B2, WM_SETTEXT, 0, (LPARAM)pszText);
		SetWindowText(hScrnWnd, pszText);
	}

	if (bAbs) {
		nProgressPosBurn = (int)((double)(-nProgressMin) * dProgress);
		if (nProgressPosBurn > -nProgressMin) {
			nProgressPosBurn = -nProgressMin;
		}
		SendDlgItemMessage(hProgressDlg, IDC_WAIT_PROG, PBM_SETPOS, (WPARAM)(nProgressMin + nProgressPosBurn + nProgressPosBurner), 0);
	} else {
		if (dProgress) {
			nProgressPosBurn += (int)((double)(-nProgressMin) * dProgress);
			if (nProgressPosBurn > -nProgressMin) {
				nProgressPosBurn = -nProgressMin;
			}
			SendDlgItemMessage(hProgressDlg, IDC_WAIT_PROG, PBM_SETPOS, (WPARAM)(nProgressMin + nProgressPosBurn + nProgressPosBurner), 0);
		}
	}

	return 0;
}

// ----------------------------------------------------------------------------
// Callback for burner

int ProgressUpdateBurner(double dProgress, const TCHAR* pszText, bool bAbs)
{
	if (hProgressThread == NULL || hProgressDlg == NULL) {
		return 1;
	}

	if (pszText) {
		SendDlgItemMessage(hProgressDlg, IDC_WAIT_LABEL_B2, WM_SETTEXT, 0, (LPARAM)pszText);
		SetWindowText(hScrnWnd, pszText);
	}

	if (bAbs) {
		nProgressPosBurner = (int)((double)nProgressMax * dProgress);
		if (nProgressPosBurner > nProgressMax) {
			nProgressPosBurner = nProgressMax;
		}
		SendDlgItemMessage(hProgressDlg, IDC_WAIT_PROG, PBM_SETPOS, (WPARAM)(nProgressMin + nProgressPosBurn + nProgressPosBurner), 0);
	} else {
		if (dProgress) {
			if (nProgressPosBurner > nProgressMax) {
				nProgressPosBurner = nProgressMax;
			}
			nProgressPosBurner += (int)((double)nProgressMax * dProgress);
			SendDlgItemMessage(hProgressDlg, IDC_WAIT_PROG, PBM_SETPOS, (WPARAM)(nProgressMin + nProgressPosBurn + nProgressPosBurner), 0);
		}
	}

	return 0;
}

// ----------------------------------------------------------------------------
// Code that runs in a seperate thread

static BOOL CALLBACK ProgressProc(HWND hDlg, UINT Msg, WPARAM /*wParam*/, LPARAM /*lParam*/)
{
	if (Msg == WM_INITDIALOG) {
		TCHAR szText[128] = _T("");

		hProgressDlg = hDlg;

		SendDlgItemMessage(hDlg, IDC_WAIT_PROG, PBM_SETRANGE32, nProgressMin, nProgressMax);

		_stprintf(szText, FBALoadStringEx(hAppInst, IDS_PROGRESS_LOADING, true), BurnDrvGetText(DRV_NAME));
		SendDlgItemMessage(hDlg, IDC_WAIT_LABEL_B1, WM_SETTEXT, 0, (LPARAM)szText);

		ShowWindow(GetDlgItem(hDlg, IDC_WAIT_LABEL_B1), TRUE);
		ShowWindow(GetDlgItem(hDlg, IDC_WAIT_LABEL_B2), TRUE);

		WndInMid(hDlg, hScrnWnd);
		SetForegroundWindow(hDlg);
		SetWindowPos(hDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);

		return TRUE;
	}

	return 0;
}

static DWORD WINAPI DoProgress(LPVOID)
{
	MSG msg;

	// Raise the thread priority for this thread
	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

	nProgressMin = 0;
	nProgressMax = 1 << 30;

	nProgressPosBurn = 0;
	nProgressPosBurner = 0;

	BurnExtProgressRangeCallback = ProgressSetRangeBurn;
	BurnExtProgressUpdateCallback = ProgressUpdateBurn;

	FBACreateDialog(hAppInst, MAKEINTRESOURCE(IDD_WAIT), NULL, ProgressProc);

	if (hEvent) {
		SetEvent(hEvent);
	}

	while (GetMessage(&msg, NULL, 0, 0)) {

		// See if we need to end the thread
		if (msg.message == (WM_APP + 0)) {
			break;
		}

		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	DestroyWindow(hProgressDlg);

	return 0;
}

// ----------------------------------------------------------------------------

int ProgressCreate()
{
	if (hProgressDlg || hProgressThread) {
		return 1;
	}

	hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

	hProgressThread = CreateThread(NULL, 0, DoProgress, NULL, THREAD_TERMINATE, &dwProgressThreadID);

	WaitForSingleObject(hEvent, 15000);

	CloseHandle(hEvent);
	hEvent = NULL;

	return 0;
}

int ProgressDestroy()
{
	if (hProgressThread) {

		// Signal the prgress thread to end
		PostThreadMessage(dwProgressThreadID, WM_APP + 0, 0, 0);

		// Wait for the thread to finish
		if (WaitForSingleObject(hProgressThread, 15000) != WAIT_OBJECT_0) {
			// If the thread doesn't finish within 15 seconds, forcibly kill it
			TerminateThread(hProgressThread, 1);
		}

		BurnExtProgressRangeCallback = NULL;
		BurnExtProgressUpdateCallback = NULL;

		hProgressDlg = NULL;

		CloseHandle(hProgressThread);

		hProgressThread = NULL;
		dwProgressThreadID = 0;
	}

	return 0;
}
