
#include <windows.h>
#include <cstdio>

#include "Destiny3D.hpp"

#include "deGlobalTypes.hpp"
#include "deDriver.hpp"
#include "ResPicker.hpp"
#include "resource.h"

deResLister::deResLister()
{
	m_NumAdapters = 0;
}

deResLister::~deResLister()
{
}

deBoolean deResLister::EnumDriver(const IdeDriver * pDriver)
{
	if (!pDriver)
		DEBUGRETURN(deFALSE, "EnumDriver: pDriver was NULL");

	m_ResList.Resize(0);

	IdeDriver::deDisplay Display;
	ResInfo* pCurrentRes;
	int ModeNum;
	int j, k, x;

	ModeNum = 0;
	m_NumAdapters = pDriver->GetAdapterCount();
	for (j = 0; j < m_NumAdapters; ++j)
	{
		ModeNum += pDriver->GetModeCount(j);
	}
	m_ResList.ResizeQuick(ModeNum, deTRUE);

	x = 0;
	for (j = 0; j < m_NumAdapters; ++j)
	{
		ModeNum = pDriver->GetModeCount(j);
		for (k = 0; k < ModeNum; ++k)
		{
			pDriver->GetDisplayMode(j, k, &Display);
			pCurrentRes = &m_ResList[x];
			// gotta have at least 60 Hz at a nice resolution
			if (/*Display.RefreshRate >= 60 &&*/ Display.Width >= 500)
			{
				pCurrentRes->AdapterNum = j;
				pCurrentRes->ModeNum = k;
				pCurrentRes->Width = Display.Width;
				pCurrentRes->Height = Display.Height;
				pCurrentRes->Refresh = Display.RefreshRate;
				pCurrentRes->BitDepth = IdeDriver_BPPBitDepth(Display.FrontBpp);
				x++;
			}
		}
	}
	m_ResList.ResizeQuick(x, deTRUE);
	return deTRUE;
}

long deResLister::GetNumAdapters() const
{
	return m_NumAdapters;
}

long deResLister::GetNumResolutions() const
{
	return m_ResList.Length();
}

deResLister::ResInfo* deResLister::GetResList() const
{
	return m_ResList.GetCArray();
}


// class that does win32 interface for picking a resolution
struct ResListData
{
	int						NumAdapters;
	int						ResNum;
	deResLister::ResInfo*	ResList;
	const IdeDriver*		pDriver;
	IdeDriver::deDisplay*	pDisplay;
};
BOOL CALLBACK ResPickerDlgProc(HWND hwndDlg,  // handle to dialog box
							   UINT uMsg,     // message
							   WPARAM wParam, // first message parameter
							   LPARAM lParam  // second message parameter
							   );

deResPicker::deResPicker()
{
}

deResPicker::~deResPicker()
{
}

deBoolean deResPicker::PickResolution(HINSTANCE hInstance, 
									  HWND ParentWindow, 
									  const IdeDriver* pDriver, 
									  IdeDriver::deDisplay* pDisplay)
{
	if (!pDriver)
		DEBUGRETURN(deFALSE, "PickResolution: pDriver was NULL");
	if (!pDisplay)
		DEBUGRETURN(deFALSE, "PickResolution: pDisplay was NULL");
	
	int RetVal;
	ResListData Data;

	if (!m_Lister.EnumDriver(pDriver))
		return deFALSE;
	Data.NumAdapters = m_Lister.GetNumAdapters();
	Data.ResNum = m_Lister.GetNumResolutions();
	Data.ResList = m_Lister.GetResList();
	Data.pDriver = pDriver;
	Data.pDisplay = pDisplay;
	memset(pDisplay, 0, sizeof(IdeDriver::deDisplay));

	RetVal = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_RESPICK), 
							ParentWindow, ResPickerDlgProc, (LPARAM)(&Data));
	if (RetVal < 0)
		return deFALSE;

	return deTRUE;
}

BOOL CALLBACK ResPickerDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	BOOL result = FALSE;
	switch (uMsg)
	{
	default:
		break;
	case WM_INITDIALOG:
		{
			ResListData* pData = (ResListData*)(lParam);
			SetWindowLong(hwnd, GWL_USERDATA, (LONG)(pData));
			int k, count;
			char buffer[256];
			const IdeDriver::deDriverCaps* pCaps;
			if (pData->NumAdapters == 0)
			{
				_snprintf(buffer, 255, "%s", "No valid devices");
				buffer[255] = '\0';
				SendDlgItemMessage(hwnd, IDC_ADAPTERLIST, LB_ADDSTRING, 0, (LPARAM)((LPCTSTR)buffer));
				SendDlgItemMessage(hwnd, IDC_ADAPTERLIST, LB_SETITEMDATA, 0, -1);
			}
			count = 0;
			for (k = 0; k < pData->NumAdapters; ++k)
			{
				int j;
				for (j = 0; j < pData->ResNum; ++j)
				{
					if (pData->ResList[j].AdapterNum == k)
					{
						pCaps = pData->pDriver->GetCaps(k);
						_snprintf(buffer, 255, "%s", pCaps->AdapterDescription);
						buffer[255] = '\0';
						SendDlgItemMessage(hwnd, IDC_ADAPTERLIST, LB_ADDSTRING, 0, (LPARAM)((LPCTSTR)buffer));
						SendDlgItemMessage(hwnd, IDC_ADAPTERLIST, LB_SETITEMDATA, count, k);
						if (count == 0)
						{
							SendDlgItemMessage(hwnd, IDC_ADAPTERLIST, LB_SELECTSTRING, 0, (LPARAM)((LPCTSTR)buffer));
						}
						break;
					}
				}
				count++;
			}
			// HAAACK!!!
			ResPickerDlgProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_ADAPTERLIST, LBN_SELCHANGE), 0);
			result = TRUE;
		}
		break;
	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		default:
			{
				if (HIWORD(wParam) == LBN_DBLCLK)
				{
					return ResPickerDlgProc(hwnd, WM_COMMAND, IDOK, 0);
				}
				ResListData* pData;
				LONG LResult;
				int k, count;
				int Width, Height, Adapter;
				char buffer[255];
				pData = (ResListData*)GetWindowLong(hwnd, GWL_USERDATA);
				if (LOWORD(wParam) == IDC_ADAPTERLIST)
				{
					if (HIWORD(wParam) == LBN_SELCHANGE)
					{
						SendDlgItemMessage(hwnd, IDC_RESLIST, LB_RESETCONTENT, 0, 0);
						SendDlgItemMessage(hwnd, IDC_BPPLIST, LB_RESETCONTENT, 0, 0);
						SendDlgItemMessage(hwnd, IDC_REFRESHLIST, LB_RESETCONTENT, 0, 0);
						LResult = SendDlgItemMessage(hwnd, IDC_ADAPTERLIST, LB_GETCURSEL, 0, 0);
						Adapter = SendDlgItemMessage(hwnd, IDC_ADAPTERLIST, LB_GETITEMDATA, LResult, 0);
						sprintf(buffer, "%s", "Windowed");
						SendDlgItemMessage(hwnd, IDC_RESLIST, LB_ADDSTRING, 0, (LPARAM)((LPCTSTR)buffer));
						SendDlgItemMessage(hwnd, IDC_RESLIST, LB_SETITEMDATA, 0, 0);
						SendDlgItemMessage(hwnd, IDC_RESLIST, LB_SELECTSTRING, 0, (LPARAM)((LPCTSTR)buffer));
						count = 1;
						for (k = 0; k < pData->ResNum; ++k)
						{
							if (pData->ResList[k].AdapterNum == Adapter)
							{
								sprintf(buffer, "%d x %d", 
									pData->ResList[k].Width,
									pData->ResList[k].Height);
								LResult = SendDlgItemMessage(hwnd, IDC_RESLIST, LB_FINDSTRING, 0, (LPARAM)((LPCTSTR)buffer));
								if (LResult == LB_ERR)
								{
									SendDlgItemMessage(hwnd, IDC_RESLIST, LB_ADDSTRING, 0, (LPARAM)((LPCTSTR)buffer));
									SendDlgItemMessage(hwnd, IDC_RESLIST, LB_SETITEMDATA, count, ((pData->ResList[k].Width<<16)|pData->ResList[k].Height));
									count++;
								}
							}
						}
					}
				}
				if (LOWORD(wParam) == IDC_RESLIST)
				{
					if (HIWORD(wParam) == LBN_SELCHANGE)
					{
						SendDlgItemMessage(hwnd, IDC_BPPLIST, LB_RESETCONTENT, 0, 0);
						SendDlgItemMessage(hwnd, IDC_REFRESHLIST, LB_RESETCONTENT, 0, 0);
						LResult = SendDlgItemMessage(hwnd, IDC_RESLIST, LB_GETCURSEL, 0, 0);
						LResult = SendDlgItemMessage(hwnd, IDC_RESLIST, LB_GETITEMDATA, LResult, 0);
						Width = (LResult >> 16) & 0xffff;
						Height = LResult & 0xffff;
						LResult = SendDlgItemMessage(hwnd, IDC_ADAPTERLIST, LB_GETCURSEL, 0, 0);
						Adapter = SendDlgItemMessage(hwnd, IDC_ADAPTERLIST, LB_GETITEMDATA, LResult, 0);
						count = 0;
						for (k = 0; k < pData->ResNum; ++k)
						{
							if (pData->ResList[k].Width == Width && 
								pData->ResList[k].Height == Height &&
								pData->ResList[k].AdapterNum == Adapter)
							{
								sprintf(buffer, "%d bit", 
									pData->ResList[k].BitDepth);
								LResult = SendDlgItemMessage(hwnd, IDC_BPPLIST, LB_FINDSTRING, 0, (LPARAM)((LPCTSTR)buffer));
								if (LResult == LB_ERR)
								{
									SendDlgItemMessage(hwnd, IDC_BPPLIST, LB_ADDSTRING, 0, (LPARAM)((LPCTSTR)buffer));
									SendDlgItemMessage(hwnd, IDC_BPPLIST, LB_SETITEMDATA, count, pData->ResList[k].BitDepth);
									if (count == 0)
										SendDlgItemMessage(hwnd, IDC_BPPLIST, LB_SELECTSTRING, 0, (LPARAM)((LPCTSTR)buffer));
									count++;
								}
							}
						}
						sprintf(buffer, "%d bit", 16);
						SendDlgItemMessage(hwnd, IDC_BPPLIST, LB_SELECTSTRING, 0, (LPARAM)((LPCTSTR)buffer));
						sprintf(buffer, "%d bit", 32);
						SendDlgItemMessage(hwnd, IDC_BPPLIST, LB_SELECTSTRING, 0, (LPARAM)((LPCTSTR)buffer));
						count = 0;
						for (k = 0; k < pData->ResNum; ++k)
						{
							if (pData->ResList[k].Width == Width && 
								pData->ResList[k].Height == Height &&
								pData->ResList[k].AdapterNum == Adapter)
							{
								sprintf(buffer, "%d Hz", 
									pData->ResList[k].Refresh);
								LResult = SendDlgItemMessage(hwnd, IDC_REFRESHLIST, LB_FINDSTRING, 0, (LPARAM)((LPCTSTR)buffer));
								if (LResult == LB_ERR)
								{
									SendDlgItemMessage(hwnd, IDC_REFRESHLIST, LB_ADDSTRING, 0, (LPARAM)((LPCTSTR)buffer));
									SendDlgItemMessage(hwnd, IDC_REFRESHLIST, LB_SETITEMDATA, count, pData->ResList[k].Refresh);
									if (count == 0)
										SendDlgItemMessage(hwnd, IDC_REFRESHLIST, LB_SELECTSTRING, 0, (LPARAM)((LPCTSTR)buffer));
									count++;
								}
							}
						}
						sprintf(buffer, "%d Hz", 60);
						SendDlgItemMessage(hwnd, IDC_REFRESHLIST, LB_SELECTSTRING, 0, (LPARAM)((LPCTSTR)buffer));
						sprintf(buffer, "%d Hz", 75);
						SendDlgItemMessage(hwnd, IDC_REFRESHLIST, LB_SELECTSTRING, 0, (LPARAM)((LPCTSTR)buffer));
					}
				}
			}
			break;
		case IDOK:
			{
				ResListData* pData;
				LONG LResult;
				int BitDepth;
				pData = (ResListData*)GetWindowLong(hwnd, GWL_USERDATA);
				LResult = SendDlgItemMessage(hwnd, IDC_ADAPTERLIST, LB_GETCURSEL, 0, 0);
				pData->pDisplay->Adapter = SendDlgItemMessage(hwnd, IDC_ADAPTERLIST, LB_GETITEMDATA, LResult, 0);
				if (pData->pDisplay->Adapter < 0)
				{
					EndDialog(hwnd, -1);
					break;
				}
				if (SendDlgItemMessage(hwnd, IDC_CHECKFSAA, BM_GETCHECK, 0, 0))
					pData->pDisplay->FSAASamples = 4;
				else
					pData->pDisplay->FSAASamples = 1;
				if (SendDlgItemMessage(hwnd, IDC_CHECKVSYNC, BM_GETCHECK, 0, 0))
					pData->pDisplay->IgnoreVSync = deFALSE;
				else
					pData->pDisplay->IgnoreVSync = deTRUE;
				LResult = SendDlgItemMessage(hwnd, IDC_RESLIST, LB_GETCURSEL, 0, 0);
				LResult = SendDlgItemMessage(hwnd, IDC_RESLIST, LB_GETITEMDATA, LResult, 0);
				if (LResult == 0)
				{
					pData->pDisplay->ZDepth = 24;
					pData->pDisplay->Windowed = deTRUE;
					EndDialog(hwnd, 0);
				}
				else
				{
					pData->pDisplay->Windowed = deFALSE;
					pData->pDisplay->Width = (LResult >> 16) & 0xffff;
					pData->pDisplay->Height = LResult & 0xffff;
					LResult = SendDlgItemMessage(hwnd, IDC_REFRESHLIST, LB_GETCURSEL, 0, 0);
					pData->pDisplay->RefreshRate = SendDlgItemMessage(hwnd, IDC_REFRESHLIST, LB_GETITEMDATA, LResult, 0);
					LResult = SendDlgItemMessage(hwnd, IDC_BPPLIST, LB_GETCURSEL, 0, 0);
					BitDepth = SendDlgItemMessage(hwnd, IDC_BPPLIST, LB_GETITEMDATA, LResult, 0);
					switch (BitDepth)
					{
					default:
					case 16:
						pData->pDisplay->BackBpp = IdeDriver::BPP_R5G6B5;
						pData->pDisplay->FrontBpp = IdeDriver::BPP_R5G6B5;
						pData->pDisplay->ZDepth = 16;
						break;
					case 24:
						pData->pDisplay->BackBpp = IdeDriver::BPP_R8G8B8;
						pData->pDisplay->FrontBpp = IdeDriver::BPP_R8G8B8;
						pData->pDisplay->ZDepth = 24;
						break;
					case 32:
						pData->pDisplay->BackBpp = IdeDriver::BPP_A8R8G8B8;
						pData->pDisplay->FrontBpp = IdeDriver::BPP_X8R8G8B8;
						pData->pDisplay->ZDepth = 24;
						break;
					}
					EndDialog(hwnd, 1);
				}
			}
			break;
		case IDCANCEL:
			EndDialog(hwnd, -1);
			break;
		}
		break;
	}
	return result;
}

