#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 INT_PTR CALLBACK ProgressProc(HWND hDlg, UINT Msg, WPARAM /*wParam*/, LPARAM /*lParam*/)
{
	if (Msg == WM_INITDIALOG) {
		TCHAR szText[MAX_PATH] = _T("");

		hProgressDlg = hDlg;

		// ==> load custom resource, added by regret
		HBITMAP hBitmap = (HBITMAP)LoadImage(hAppInst, _T("res\\misc.bmp"), IMAGE_BITMAP, 80, 60, LR_LOADFROMFILE);
		if (!hBitmap) {
			hBitmap = LoadBitmap(hAppInst, MAKEINTRESOURCE(BMP_MISC));
		}
		SendDlgItemMessage(hDlg, IDC_MISC, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap);
		// <== load custom resource

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

		_stprintf(szText, FBALoadStringEx(IDS_PROGRESS_LOADING), 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 FALSE;
}

static DWORD WINAPI DoProgress(LPVOID)
{
	MSG msg;
	BOOL bRet;

	// 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(IDD_WAIT, NULL, (DLGPROC)ProgressProc);

	if (hEvent) {
		SetEvent(hEvent);
	}

	while (1) {
		bRet = GetMessage(&msg, NULL, 0, 0);

		if (bRet != 0 && bRet != -1) {
			// 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) {
		return 0;
	}

	// 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;
}
