#include "wxinc.h"

#include "oswin32.h"

#define WIN32_LEAN_AND_MEAN
#define WIN32_EXTRA_LEAN
#define STRICT
#include <windows.h>
#include <winspool.h>

bool OsWin32_CheckPrinterInfo(const void* data, unsigned int size)
{
	bool ok = data != NULL;
	if (ok)
	{
	  LPDEVMODE pdm = (LPDEVMODE)data;
	  const unsigned int s = pdm->dmSize + pdm->dmDriverExtra;
		ok = s > 0 && s == size;
	}
	return ok;
}

void* OsWin32_ConvertFromNativePrinterInfo(void* hGlobal, unsigned int& nDataSize)
{
	void* ptr = ::GlobalLock(hGlobal);
	PDEVMODE dm = (PDEVMODE)ptr;
	nDataSize = dm->dmSize+dm->dmDriverExtra;
	void* buff = new char[nDataSize]; 
	memcpy(buff, ptr, nDataSize);
	::GlobalUnlock(hGlobal);
	return buff;
}

void* OsWin32_ConvertToNativePrinterInfo(void* data, unsigned int nDataSize)
{
	HGLOBAL hGlobal = ::GlobalAlloc(GHND, nDataSize);
	void* ptr = ::GlobalLock(hGlobal);
	memcpy(ptr, data, nDataSize);
	::GlobalUnlock(hGlobal);
	return hGlobal;
}

struct XvtData
{
	char** families;
	long* sizes;
  short* scalable;
	long max_count;
	long cur_count;

	XvtData() {	memset(this, 0, sizeof(XvtData));	}
};

int CALLBACK FamilyEnumerator(
  const LOGFONT *plf,        // pointer to logical-font data
  const TEXTMETRIC *lpntme,  // pointer to physical-font data
  unsigned long FontType,    // type of font
  LPARAM lParam              // application-defined data
)
{
	XvtData* d = (XvtData*)lParam;
	d->families[d->cur_count++] = strdup(plf->lfFaceName);
	return d->cur_count < d->max_count;
}

int CALLBACK SizeEnumerator(
  const LOGFONT *plf,        // pointer to logical-font data
  const TEXTMETRIC *lpntme,  // pointer to physical-font data
  unsigned long FontType,    // type of font
  LPARAM lParam              // application-defined data
)
{
	XvtData* d = (XvtData*)lParam;
  long& i = d->cur_count; 
	const int size = (plf->lfHeight+5) / 10;
	if (i == 0 || size > d->sizes[i-1])
	{
	  d->sizes[i] = size;
		if (lpntme->tmPitchAndFamily & TMPF_TRUETYPE)
			*d->scalable = TRUE;
	  i++;
	}
	return i < d->max_count;
}

int FamilySorter(const void* p1,const void* p2)
{
	const char* s1 = *(const char**)p1;
	const char* s2 = *(const char**)p2;
	return strcmp(s1, s2);
}

int OsWin32_EnumerateFamilies(unsigned int hDC, char** families, int max_count)
{
	XvtData data;
	data.families = families;
	data.max_count = max_count;
	LOGFONT lf; memset(&lf, 0, sizeof(lf));
  ::EnumFontFamiliesEx((HDC)hDC, &lf, FamilyEnumerator, (LPARAM)&data, 0);
	qsort(families, data.cur_count, sizeof(char*), FamilySorter);
	return data.cur_count;
}

int OsWin32_EnumerateSizes(unsigned int hDC, const char* name, long* sizes, short* scalable, int max_count)
{
	XvtData data;
	data.sizes = sizes;
	data.scalable = scalable;
	data.max_count = max_count;
	LOGFONT lf; memset(&lf, 0, sizeof(lf));
	strcpy(lf.lfFaceName, name);
  ::EnumFontFamiliesEx((HDC)hDC, &lf, SizeEnumerator, (LPARAM)&data, 0);

	return data.cur_count;
}

void* OsWin32_GetPrinterInfo(int& size, const char* printer)
{
	char name[256];

	size = 0;
	if (printer == NULL || *printer == '\0')
	{
		if (::GetProfileString("windows", "device", ",,,", name, sizeof(name)) == 0)
			return NULL;
		char* comma = strchr(name, ',');
		if (comma) *comma = '\0';
	}
	else
		strcpy(name, printer);

	LPDEVMODE pdm = (LPDEVMODE) new char[16*1024];
	HANDLE hPrinter;
	if (::OpenPrinter(name, &hPrinter, NULL) == 0)
		return NULL;

	::DocumentProperties(0, hPrinter, name, pdm, NULL, DM_OUT_BUFFER);
	::ClosePrinter(hPrinter);

	size =  pdm->dmSize + pdm->dmDriverExtra;
	return pdm;
}

void OsWin32_SetCaptionStyle(unsigned int handle, bool set)
{
	HWND hwnd = (HWND)handle;
	DWORD s = ::GetWindowLong(hwnd, GWL_STYLE);
	if (set)
	  s |= WS_CAPTION;
	else
	  s &= ~WS_CAPTION;
#if !wxCHECK_VERSION(2,3,2)
  s |= WS_CLIPSIBLINGS;
#endif
	::SetWindowLong(hwnd, GWL_STYLE, s);
}

void OsWin32_SetClippingRect(unsigned int hDC, int x, int y, int w, int h)
{
	HDC hdc = (HDC)hDC;
	if (w > 0 && h > 0)
	{
  	HRGN hrgn = ::CreateRectRgn(x, y, x+w, y+h);
		::SelectClipRgn(hdc, hrgn);
		::DeleteObject(hrgn);
	}
	else
		::SelectClipRgn(hdc, NULL);
}

void OsWin32_UpdateWindow(unsigned int handle)
{
	HWND hwnd = (HWND)handle;
	::UpdateWindow(hwnd);
}

void OsWin32_StretchBlt(unsigned int hDst, int xd, int yd, int wd, int hd,
												unsigned int hSrc, int xs, int ys, int ws, int hs)
{
	const int nColors = ::GetDeviceCaps((HDC)hDst, NUMCOLORS);
	::SetStretchBltMode((HDC)hDst, nColors == 2 ? STRETCH_HALFTONE : STRETCH_DELETESCANS);
	::StretchBlt((HDC)hDst, xd, yd, wd, hd, (HDC)hSrc, xs, ys, ws, hs, SRCCOPY);
}

///////////////////////////////////////////////////////////
// Drawing bitmaps
///////////////////////////////////////////////////////////

void OsWin32_DrawBitmap(unsigned int hBitmap, unsigned int hDC, 
				                int xd, int yd, int wd, int hd,
  											int xs, int ys, int ws, int hs)
{
	const int nTechno = ::GetDeviceCaps((HDC)hDC, TECHNOLOGY);
  if (nTechno == DT_RASPRINTER) // Sto stampando!
	{
		BITMAPINFO bi; memset(&bi, 0, sizeof(bi));
		BITMAPINFOHEADER& bih = bi.bmiHeader;
		bih.biSize = sizeof(bih);
		HDC hMemDC = ::CreateCompatibleDC(NULL);
		GetDIBits(hMemDC, (HBITMAP)hBitmap, 0, 0, NULL, &bi, DIB_RGB_COLORS);
		if (bih.biSizeImage > 0)
		{
  		LPBYTE bits = new BYTE[bih.biSizeImage];
			::GetDIBits(hMemDC, (HBITMAP)hBitmap, 0, bih.biHeight, 
			  					bits, &bi, DIB_RGB_COLORS);
			::StretchDIBits((HDC)hDC, xd, yd, wd, hd, xs, ys, ws, hs, 
											bits, &bi, DIB_RGB_COLORS, SRCCOPY);
			delete bits;
		}
		::DeleteDC(hMemDC);
	}
	else
	{
		HDC hMemDC = ::CreateCompatibleDC((HDC)hDC);
		HGDIOBJ hOldBitmap = ::SelectObject(hMemDC, (HBITMAP)hBitmap);
  	::SetStretchBltMode((HDC)hDC, STRETCH_DELETESCANS);
    ::StretchBlt((HDC)hDC, xd, yd, wd, hd, hMemDC, xs, ys, ws, hs, SRCCOPY);
		::SelectObject(hMemDC, hOldBitmap);
		::DeleteDC(hMemDC);
	}
}

void OsWin32_Beep(int severity)
{
  switch (severity)
  {
  case  0: MessageBeep(MB_OK); break;
  case  1: MessageBeep(MB_ICONEXCLAMATION); break;
  default: MessageBeep(MB_ICONSTOP); break;
  }
}