#include "wxinc.h"

#include "oswin32.h"

#include "xvt_menu.h"
#include "xvt_help.h"

#define WIN32_LEAN_AND_MEAN
#define WIN32_EXTRA_LEAN
#define STRICT
#include <windows.h>
#include <winspool.h>
#include <shellapi.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; 
	int size = (plf->lfHeight+5) / 10;
  if (size <= 0)
  {
    for (const char* n = plf->lfFaceName; *n; n++) 
      if (*n >= '1' && *n <= '9')
      {
        size = int(10.0 / atoi(n) * 10.0 + 0.5);
        break;
      }
  }
	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 = NULL;
	HANDLE hPrinter;
	if (::OpenPrinter(name, &hPrinter, NULL) != 0)
	{
		size = ::DocumentProperties(0, hPrinter, name, NULL, NULL, 0); // Determina dimensione DEVMODE
		if (size > 0)
		{
			pdm = (LPDEVMODE) new char[size]; // Alloca un DEVMODE sufficientemente capiente
			::DocumentProperties(0, hPrinter, name, pdm, NULL, DM_OUT_BUFFER); // Legge DEVMODE
		}
		else
			size = 0;
		::ClosePrinter(hPrinter);
	}
	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_DrawDottedRect(unsigned int hDC, int left, int top, int right, int bottom)
{
  LOGBRUSH lBrush; 
  lBrush.lbHatch = 0; lBrush.lbStyle = BS_SOLID;
  lBrush.lbColor = ::GetTextColor((HDC)hDC); 
  HPEN hPen = ::ExtCreatePen(PS_COSMETIC|PS_ALTERNATE, 1, &lBrush, 0, NULL);
  HGDIOBJ hOldPen = ::SelectObject((HDC)hDC, hPen);
  HGDIOBJ hBrush = ::GetStockObject(HOLLOW_BRUSH);
  HGDIOBJ hOldBrush = ::SelectObject((HDC)hDC, hBrush);
  ::Rectangle((HDC)hDC, left, top, right, bottom);
  ::SelectObject((HDC)hDC, hOldBrush);
  ::SelectObject((HDC)hDC, hOldPen);
  ::DeleteObject(hPen);
}

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

int OsWin32_Help(unsigned int handle, const char* hlp, unsigned int cmd, const char* topic)
{
	HWND hwnd = (HWND)handle;

	switch(cmd)
	{                     
	case M_HELP_CONTENTS: 
		::WinHelp(hwnd, hlp, HELP_CONTENTS, 0); 
		break;
	case M_HELP_SEARCH: 
		::WinHelp(hwnd, hlp, HELP_PARTIALKEY, (DWORD)""); 
		break;
	case M_HELP_HELPONHELP: 
		::WinHelp(hwnd, hlp, HELP_HELPONHELP, 0); 
		break;
	case M_HELP_ONCONTEXT:
		if (topic != NULL)
		{
			struct MULTIGUY
			{
				DWORD mkSize;
				TCHAR mkKeylist;
				TCHAR mkKeyphrase[16];
			} mk;

			mk.mkSize = sizeof(MULTIGUY);
			mk.mkKeylist = 'M';
			strncpy(mk.mkKeyphrase, topic, sizeof(mk.mkKeyphrase));
			::WinHelp(hwnd, hlp, HELP_MULTIKEY, (DWORD)&mk);
		}
		break;
	default:
  	::WinHelp(hwnd, hlp, HELP_QUIT, 0);
		break;
	}
	return TRUE;
}

bool OsWin32_TestNetworkVersion()
{
  // Win95 only!
  char* VREDIRNAME = "vredir.vxd";
  DWORD handle;
  
  DWORD dwSize = ::GetFileVersionInfoSize(VREDIRNAME,&handle);
  if (dwSize)
  {
    BYTE infoBuffer[512];
    GetFileVersionInfo(VREDIRNAME,handle,dwSize,infoBuffer);
    long *language;
    void * lpBuffer;
    UINT Size;
    if (VerQueryValue(infoBuffer, "\\VarFileInfo\\Translation", (void **)&language, &Size) && Size!=0) 
    {
      char szName[128];
      sprintf(szName, "\\StringFileInfo\\%04x%04x\\FileVersion",LOWORD(*language), HIWORD(*language));
      if (VerQueryValue(infoBuffer, szName, &lpBuffer, &Size) && Size!=0) 
      {
        const char* s = (const char *)lpBuffer;
        for (int i = 0; i < 2; i++)
          s = strchr(s, '.')+1;
        int subver=atoi(s);
        if (subver >= 1111 && subver <= 1115)
          return false;
      }
    }
  }
  return true;
}

///////////////////////////////////////////////////////////
// Execute in window support
///////////////////////////////////////////////////////////

struct TFindWindowInfo
{ 
  HINSTANCE _instance;
  wxString _file;
  HWND _hwnd;
  
  TFindWindowInfo() : _instance(NULL), _hwnd(NULL) { }
};

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
  TFindWindowInfo* w = (TFindWindowInfo*)lParam;

  HINSTANCE inst = (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE);
  if (inst == w->_instance)
  {         
    const LONG style = GetWindowLong(hwnd, GWL_STYLE);
    if ((style & WS_CAPTION) != 0)  // Ha la caption?
    {
      w->_hwnd = hwnd;
      return FALSE;
    }
    return TRUE;  
  }  

  char str[256];
  GetWindowText(hwnd, str, sizeof(str));
  wxString title = str;
  title.MakeUpper();
  if (title.find(w->_file) >= 0)
  {
    w->_hwnd = hwnd;
    return FALSE;
  }

  return TRUE;
}

void OsWin32_PlaceProcessInWindow(unsigned int instance, const char* name, unsigned int parent)
{
  TFindWindowInfo w;
  w._instance = (HINSTANCE)instance;
  w._file = name;
  w._file.MakeUpper();

  for (int i = 0; w._hwnd == NULL && i < 20; i++)
  { 
    wxThread::Sleep(500);
    ::EnumWindows(EnumWindowsProc, LPARAM(&w));
  }  

  if (w._hwnd != NULL)  // L'ho trovata!
  {                                                          
    RECT rct; ::GetClientRect((HWND)parent, &rct);
    ::SetParent(w._hwnd, (HWND)parent);
    const int fx = ::GetSystemMetrics(SM_CXFRAME);
    const int fy = ::GetSystemMetrics(SM_CYFRAME);
    int cy = ::GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYBORDER);
    if (::GetMenu(w._hwnd) != NULL) 
      cy += ::GetSystemMetrics(SM_CYMENU);
    ::SetWindowPos(w._hwnd, (HWND)parent, -fx, -fy-cy, rct.right+2*fx, rct.bottom+cy+2*fy, SWP_NOZORDER);
  }
}

///////////////////////////////////////////////////////////
// Hardlock Support
///////////////////////////////////////////////////////////

#include "hlapi_c.h"

bool OsWin32_HL_Login(unsigned short address, const unsigned char* label, const unsigned char* password)
{
  int err = HL_LOGIN(address, LOCAL_DEVICE, (unsigned char*)label, (unsigned char*)password);
  return err == STATUS_OK;
}

bool OsWin32_HL_Logout() 
{
  HL_LOGOUT();
  return TRUE;
}

bool OsWin32_HL_Read(unsigned short reg, unsigned short* data) 
{
  int err = HL_READ(reg, data);
  return err == STATUS_OK;
}

bool OsWin32_HL_ReadBlock(unsigned char* data) 
{
  int err = HL_READBL(data);
  return err == STATUS_OK;
}

bool OsWin32_HL_Write(unsigned short reg, unsigned short data) 
{
  int err = HL_WRITE(reg, data);
  return err == STATUS_OK;
}

bool OsWin32_HL_Crypt(unsigned short* data) // Array di 4 words (8 bytes)
{
  int err = HL_CODE(data, 1);
  return err == STATUS_OK;
}

///////////////////////////////////////////////////////////
// Eutron Smartlink Support
///////////////////////////////////////////////////////////

#include "skeylink.h"

static KEY_NET _eutron_key;

bool OsWin32_SL_Crypt(unsigned short* data)
{
  _eutron_key.net_command = NET_KEY_ACCESS;
  _eutron_key.command = SCRAMBLING_MODE;
  memcpy(_eutron_key.data, data, 8);
  smartlink(&_eutron_key);
  if (_eutron_key.status == ST_OK)
    memcpy(data, _eutron_key.data, 8);
  return _eutron_key.status == ST_OK;
}

bool OsWin32_SL_Login(const unsigned char* label, const unsigned char* password)
{
  memset(&_eutron_key, 0, sizeof(KEY_NET));
  _eutron_key.net_command = NET_KEY_OPEN;
	_eutron_key.status = ST_HW_FAILURE; // Don't leave ST_OK = 0 here!
  memcpy(_eutron_key.label, label, strlen((const char*)label));  
  memcpy(_eutron_key.password, password, strlen((const char*)password));  

  smartlink(&_eutron_key);
  return _eutron_key.status == ST_OK;
}

bool OsWin32_SL_Logout()
{
  _eutron_key.net_command = NET_KEY_CLOSE;
  _eutron_key.command = 0;
  smartlink(&_eutron_key);
  return true;
}

bool OsWin32_SL_ReadBlock(unsigned short reg, unsigned short size, unsigned short* data)
{
  _eutron_key.net_command = NET_KEY_ACCESS;
  _eutron_key.command = BLOCK_READING_MODE;
  unsigned short* pointer = (unsigned short*)(&_eutron_key.data[0]);
  unsigned short* number  = (unsigned short*)(&_eutron_key.data[2]);
  *pointer = reg;
  *number = size;
  smartlink(&_eutron_key);
  bool ok = _eutron_key.status == ST_OK;
  if (ok)
    memcpy(data, &_eutron_key.data[4], size*sizeof(unsigned short));
  return ok;
}

bool OsWin32_SL_WriteBlock(unsigned short reg, unsigned short size, const unsigned short* data)
{
  _eutron_key.net_command = NET_KEY_ACCESS;
  _eutron_key.command = BLOCK_WRITING_MODE;
  unsigned short* pointer = (unsigned short*)(&_eutron_key.data[0]);
  unsigned short* number  = (unsigned short*)(&_eutron_key.data[2]);
  *pointer = reg;
  *number = size;
  memcpy(&_eutron_key.data[4], data, size*sizeof(unsigned short));
  smartlink(&_eutron_key);
  return _eutron_key.status == ST_OK;
}

///////////////////////////////////////////////////////////
// Ex-Golem utilities
///////////////////////////////////////////////////////////

static long GetRegistryString(HKEY key, const char* subkey, wxString& retstr)
{                                            
  HKEY hkey;
  long retval = RegOpenKey(key, subkey, &hkey);
  if (retval == ERROR_SUCCESS)
  {
    char retdata[_MAX_PATH];
    long datasize = sizeof(retdata);
    RegQueryValue(hkey, NULL, retdata, &datasize);
    RegCloseKey(hkey);
    retstr = retdata;
  }
  return retval;
}

wxString OsWin32_File2App(const char* filename)
{                               
  wxString app;

  if (*filename != '.')
  {
    char retdata[_MAX_PATH];
    HINSTANCE hinst = ::FindExecutable(filename, ".", retdata);
    DWORD* pinst = (DWORD*)hinst;
    UINT err = LOWORD(pinst);
    if (err > 32)
      app = retdata;
  }
    
  if (app.IsEmpty())
  {                         
    char ext[_MAX_EXT];
    if (*filename == '.')
      strcpy(ext, filename);
    else  
      _splitpath(filename, NULL, NULL, NULL, ext);
    _strlwr(ext);  

    wxString key;
    if (GetRegistryString(HKEY_CLASSES_ROOT, ext, key) == ERROR_SUCCESS)
    {
      key << "\\shell\\open\\command";
      if (GetRegistryString(HKEY_CLASSES_ROOT, key, key) == ERROR_SUCCESS)
      {
        key.Replace("\"", " ");
        int pos = key.Find("%1");
        if (pos > 0)
          key.Truncate(pos);
        key.Trim(false); key.Trim(true);
        app = key;  
      }
    }      
  }  
  
  return app;
}

static bool IsInternetAddress(const char* filename)
{           
  const wxString url(filename);
  if (url.StartsWith("http:") || url.StartsWith("ftp:"))
    return TRUE;
  if (url.Find("www.") >= 0)
    return TRUE;                                
  
  char ext[_MAX_EXT];
  _splitpath(filename, NULL, NULL, NULL, ext);
  _strlwr(ext);
  const char* const extensions[] = { "com","edu","gov","it","mil","net","org", NULL };
  for (int e = 0; extensions[e]; e++)
    if (stricmp(ext, extensions[e]) == 0)
      return true;
  
  return false;
}

unsigned int OsWin32_LoadIcon(const char* filename)
{
  unsigned int icon = 0;
  int icon_number = 0;

  char ext[_MAX_EXT];
  if (*filename == '.' && strlen(filename) < _MAX_EXT)
    strcpy(ext, filename);
  else                   
  {         
    if (IsInternetAddress(filename))
      strcpy(ext, ".htm");  
    else                          
      _splitpath(filename, NULL, NULL, NULL, ext);
  }  
  _strlwr(ext);

  wxString key;
  if (ext != ".exe")
  {              
    if (GetRegistryString(HKEY_CLASSES_ROOT, ext, key) == ERROR_SUCCESS)
    {                     
      key << "\\DefaultIcon";                                     
      if (GetRegistryString(HKEY_CLASSES_ROOT, key, key) == ERROR_SUCCESS)  // Windows 95 only
      {
        const int comma = key.find(',');
        if (comma > 0)
        {
          icon_number = atoi(key.Mid(comma+1));
          key.Truncate(comma);
        }
      }
      else
      {
        key = OsWin32_File2App(filename);
        if (key.IsEmpty())
          key = OsWin32_File2App(".htm");
      }  
    }
  }  
  else
    key = filename;
  
  // Toglie eventuali parametri sulla riga si comando
  const int ext_pos = key.Find(".exe");
  if (ext_pos > 0) 
    key.Truncate(ext_pos+4);
    
  HINSTANCE hInst = NULL;
  HICON hicon = ::ExtractIcon(hInst, key, icon_number);
  if (hicon == NULL && icon_number != 0) 
    hicon = ::ExtractIcon(hInst, key, 0);
  if (hicon != NULL)
  {
    DWORD dwicon = DWORD((DWORD*)hicon);
    icon = LOWORD(dwicon);
  }  
  
  return icon;  
}

bool OsWin32_GotoUrl(const char* url, const char* action)
{
  // action = [ open, edit, print ];
  HINSTANCE hinst = ShellExecute(NULL, action, url, NULL, NULL, SW_SHOWNORMAL);
  DWORD winst = DWORD((DWORD*)hinst);
  UINT error = UINT(winst);  // Tutto 'sto giro per evitare un warning
  return error > 32;
}