#include "Win32InputBox.h"

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)

#include <stdio.h>

#pragma warning (disable: 4312)

typedef struct _MSDN_DLGTEMPLATEEX 
{
  WORD dlgVer;
  WORD signature;
  DWORD helpID;
  DWORD exStyle;
  DWORD style;
  WORD cDlgItems;
  short x;
  short y;
  short cx;
  short cy;
  BYTE  _rest[1]; // rest of structure
} MSDN_DLGTEMPLATEEX;

static bool IsDlgTemplateExtended(DLGTEMPLATE *dlgTemplate)
{
  MSDN_DLGTEMPLATEEX *dgExTemplate = (MSDN_DLGTEMPLATEEX *) dlgTemplate;

  // MSDN excerpt:
  //* dlgVer
  //  Specifies the version number of the extended dialog box template. This member must be 1. 
  //* signature
  //  Indicates whether a template is an extended dialog box template. 
  // If signature is 0xFFFF, this is an extended dialog box template. 
  // In this case, the dlgVer member specifies the template version number. 
  // If signature is any value other than 0xFFFF, this is a standard dialog box template that uses the DLGTEMPLATE and DLGITEMTEMPLATE structures. 

  return (dgExTemplate->dlgVer == 1) && (dgExTemplate->signature == 0xFFFF);
}

// Use alignment if supported by the compiler
#ifdef _MSC_VER
  #if _MSC_VER > 1200
    __declspec(align(4)) 
  #endif
#endif

// per the MSDN, the DLGTEMPLATE must be DWORD aligned
// this was generated by the DlgResToDlgTemplate tool
static unsigned char definputbox_dlg[] =
{
  0x01,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc8,0x00,0xc8,0x00,0x06,
    0x00,0x16,0x00,0x11,0x00,0xe7,0x00,0x6d,0x00,0x00,0x00,0x00,0x00,0x57,0x00,0x69,
    0x00,0x6e,0x00,0x33,0x00,0x32,0x00,0x49,0x00,0x6e,0x00,0x70,0x00,0x75,0x00,0x74,
    0x00,0x42,0x00,0x6f,0x00,0x78,0x00,0x00,0x00,0x08,0x00,0xbc,0x02,0x00,0x00,0x4d,
    0x00,0x53,0x00,0x20,0x00,0x53,0x00,0x68,0x00,0x65,0x00,0x6c,0x00,0x6c,0x00,0x20,
    0x00,0x44,0x00,0x6c,0x00,0x67,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x80,0x00,0x02,0x50,0x06,0x00,0x04,0x00,0x9d,0x00,0x21,0x00,0xe8,
    0x03,0x00,0x00,0xff,0xff,0x82,0x00,0x50,0x00,0x72,0x00,0x6f,0x00,0x6d,0x00,0x70,
    0x00,0x74,0x00,0x3a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x80,0x00,0x81,0x50,0x06,0x00,0x25,0x00,0xd8,0x00,0x0e,0x00,0xe9,
    0x03,0x00,0x00,0xff,0xff,0x81,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x84,0x10,0xa1,0x50,0x06,0x00,0x37,0x00,0xd8,0x00,0x31,0x00,0xea,
    0x03,0x00,0x00,0xff,0xff,0x81,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x01,0x00,0x03,0x50,0xab,0x00,0x04,0x00,0x33,0x00,0x0e,0x00,0x01,
    0x00,0x00,0x00,0xff,0xff,0x80,0x00,0x4f,0x00,0x4b,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x50,0xab,0x00,0x15,0x00,0x33,
    0x00,0x0e,0x00,0x02,0x00,0x00,0x00,0xff,0xff,0x80,0x00,0x43,0x00,0x41,0x00,0x4e,
    0x00,0x43,0x00,0x45,0x00,0x4c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x02,0x40,0x00,0x00,0x27,0x00,0x08,0x00,0x08,0x00,0xff,
    0xff,0xff,0xff,0xff,0xff,0x82,0x00,0x00,0x00,0x00,0x00
};

static LPCTSTR definputbox_buttonnames[] = { _T("OK"), _T("CANCEL") };
static const INT_PTR definputbox_buttonids[] = { IDOK, IDCANCEL };

static const INT 
  definputbox_id_prompt = 1000,
  definputbox_id_edit1 = 1001,
  definputbox_id_edit2 = 1002;

WIN32INPUTBOX_PARAM::WIN32INPUTBOX_PARAM()
{
  bMultiline = false;
  hwndOwner = 0;
  DlgTemplateName = 0;
  hInstance = (HINSTANCE) ::GetModuleHandle(0);
  DlgTemplateData = definputbox_dlg;

  bCenter = true;

  dwStylesPlus = 0;
  dwExStylesPlus = 0;
  dwStylesMinus = 0xFFFFFFFF;
  dwExStylesMinus = 0xFFFFFFFF;

  xPos = yPos = -1;

  szResult = 0;
  nResultSize = 0;
}

CWin32InputBox::CWin32InputBox(WIN32INPUTBOX_PARAM *param)
{
  _param = param;
}

CWin32InputBox::~CWin32InputBox()
{

}

void CWin32InputBox::SetParam(WIN32INPUTBOX_PARAM *param)
{
  _param = param;
}

WIN32INPUTBOX_PARAM *CWin32InputBox::GetParam()
{
  return _param;
}

INT_PTR CWin32InputBox::InputBoxEx(WIN32INPUTBOX_PARAM *param)
{
  // Check mandatory parameters
  if (param->szResult == 0)
  {
    ::SetLastError(ERROR_INVALID_PARAMETER);
    return 0;
  }

  LPDLGTEMPLATE dlgTemplate;

  if (param->DlgTemplateName != 0)
  {
    HMODULE hModule = (HMODULE)param->hInstance;
    HRSRC rcDlg = ::FindResource(hModule, MAKEINTRESOURCE(param->DlgTemplateName), RT_DIALOG);
    if (rcDlg == NULL)
      return 0;

    HGLOBAL hglobalDlg = ::LoadResource(hModule, rcDlg);
    if (hglobalDlg == NULL)
      return 0;

    dlgTemplate = (LPDLGTEMPLATE) hglobalDlg;
  }
  else if (param->DlgTemplateData != 0)
  {
    dlgTemplate = (LPDLGTEMPLATE) param->DlgTemplateData;
  }

  MSDN_DLGTEMPLATEEX *dlgTemplateEx = 
    IsDlgTemplateExtended((LPDLGTEMPLATE) dlgTemplate) ? (MSDN_DLGTEMPLATEEX *) dlgTemplate : 0;

  if (dlgTemplateEx != 0)
  {
    dlgTemplateEx->exStyle |= param->dwExStylesPlus;
    dlgTemplateEx->style   |= param->dwStylesPlus;
    dlgTemplateEx->exStyle &= param->dwExStylesMinus;
    dlgTemplateEx->style   &= param->dwStylesMinus;

    if (param->bCenter)
      dlgTemplateEx->style |= DS_CENTER;

    if (param->xPos != -1)
      dlgTemplateEx->x = param->xPos;
    if (param->yPos != -1)
      dlgTemplateEx->y = param->yPos;
  }
  else
  {
    dlgTemplate->dwExtendedStyle  |= param->dwExStylesPlus;
    dlgTemplate->style            |= param->dwStylesPlus;
    dlgTemplate->dwExtendedStyle  &= param->dwExStylesMinus;
    dlgTemplate->style            &= param->dwStylesMinus;

    if (param->bCenter)
      dlgTemplate->style |= DS_CENTER;

    if (param->xPos != -1)
      dlgTemplate->x = param->xPos;

    if (param->yPos != -1)
      dlgTemplate->y = param->yPos;
  }

  CWin32InputBox inputbox(param);

  // Resize dialog and SHOW or HIDE multiline
  INT_PTR r = ::DialogBoxIndirectParam(param->hInstance, dlgTemplate, param->hwndOwner, (DLGPROC)DlgProc, (LPARAM)&inputbox);

  return r;
}

INT_PTR CWin32InputBox::InputBox(
  LPCSTR szTitle, 
  LPCSTR szPrompt, 
  LPSTR szResult, 
  DWORD nResultSize,
  bool bMultiLine,
  HWND hwndParent)
{
  WIN32INPUTBOX_PARAM param;

  param.szTitle = szTitle;
  param.szPrompt = szPrompt;
  param.szResult = szResult;
  param.nResultSize = nResultSize;
  param.bMultiline = bMultiLine;

  return InputBoxEx(&param);
}

void CWin32InputBox::InitDialog()
{
  // Set the button captions
  for (size_t i=0;i<sizeof(definputbox_buttonids)/sizeof(definputbox_buttonids[0]);i++)
    ::SetDlgItemText(_param->hDlg, (int) definputbox_buttonids[i], definputbox_buttonnames[i]);

  // Set other controls
  ::SetWindowTextA(_param->hDlg, Utf8ToAnsi(_param->szTitle).c_str());
  ::SetDlgItemTextA(_param->hDlg, definputbox_id_prompt, Utf8ToAnsi(_param->szPrompt).c_str());

  HWND hwndEdit1 = ::GetDlgItem(_param->hDlg, definputbox_id_edit1);
  HWND hwndEdit2 = ::GetDlgItem(_param->hDlg, definputbox_id_edit2);

  if (_param->bMultiline)
    _hwndEditCtrl = hwndEdit2;
  else
    _hwndEditCtrl = hwndEdit1;

  ::SetWindowTextA(_hwndEditCtrl, Utf8ToAnsi(_param->szResult).c_str());

  RECT rectDlg, rectEdit1, rectEdit2;

  ::GetWindowRect(_param->hDlg, &rectDlg);
  ::GetWindowRect(hwndEdit1, &rectEdit1);
  ::GetWindowRect(hwndEdit2, &rectEdit2);

  if (_param->bMultiline)
  {
    ::ShowWindow(hwndEdit1, SW_HIDE);
    ::SetWindowPos(
      hwndEdit2, 
      HWND_NOTOPMOST, 
      rectEdit1.left - rectDlg.left, 
      (rectEdit1.top - rectDlg.top) - (rectEdit1.bottom - rectEdit1.top), 
      0, 
      0, 
      SWP_NOSIZE | SWP_NOZORDER);

    ::SetWindowPos(
      _param->hDlg, 
      HWND_NOTOPMOST, 
      0, 
      0, 
      rectDlg.right - rectDlg.left, 
      rectDlg.bottom - rectDlg.top - (rectEdit1.bottom - rectEdit1.top), 
      SWP_NOMOVE);

  }
  else
  {
    ::SetWindowPos(
      _param->hDlg, 
      HWND_NOTOPMOST, 
      0, 
      0, 
      rectDlg.right - rectDlg.left, 
      rectEdit1.bottom - rectDlg.top + 5,
      SWP_NOMOVE);

    ::ShowWindow(hwndEdit2, SW_HIDE);
  }
}

// Message handler for about box.
LRESULT CALLBACK CWin32InputBox::DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
  CWin32InputBox *_this = (CWin32InputBox *) ::GetWindowLong(hDlg, GWL_USERDATA);
  WIN32INPUTBOX_PARAM *param = _this ? _this->GetParam() : 0;

  switch (message)
  {
    case WM_INITDIALOG:
    {
      ::SetWindowLong(hDlg, GWL_USERDATA, (LONG) lParam);
      
      _this = (CWin32InputBox *)  lParam;
      _this->_param->hDlg = hDlg;
      _this->InitDialog();
      return TRUE;
    }

    case WM_COMMAND:
    {
#ifdef _MY_DEBUG
      CHAR buf[1024];
      static int i=0;
      sprintf(buf, "WM_COMMAND: %09d wParam=%08X lParam=%08X\n", i++, wParam, lParam);
      OutputDebugString(buf);
#endif
      INT_PTR buttonId = LOWORD(wParam);
      for (size_t i=0;
           i<sizeof(definputbox_buttonids)/sizeof(definputbox_buttonids[0]);
           i++)
      {
        if (buttonId == definputbox_buttonids[i]) 
        {
          ::GetWindowTextA(
            _this->_hwndEditCtrl, 
            _this->_param->szResult, 
            _this->_param->nResultSize);

		  std::string strUtf8 = AnsiToUtf8(_this->_param->szResult);

		  memset(_this->_param->szResult, 0, _this->_param->nResultSize);
		  strncpy(_this->_param->szResult, strUtf8.c_str(), _this->_param->nResultSize-1);

          ::EndDialog(hDlg, buttonId);
          return TRUE;
        }
      }
    }
    break;
  }
  return FALSE;
}


std::string CWin32InputBox::AnsiToUtf8(std::string strAnsi)
{
	std::string ret;
	if (strAnsi.length() > 0)
	{	
		int nWideStrLength = MultiByteToWideChar(CP_ACP, 0, strAnsi.c_str(), -1, NULL, 0);
		WCHAR* pwszBuf = (WCHAR*)malloc((nWideStrLength+1)*sizeof(WCHAR));
		memset(pwszBuf, 0, (nWideStrLength+1)*sizeof(WCHAR));
		MultiByteToWideChar(CP_ACP, 0, strAnsi.c_str(), -1, pwszBuf, (nWideStrLength+1)*sizeof(WCHAR));

		int nUtf8Length = WideCharToMultiByte( CP_UTF8,0,pwszBuf,-1,NULL,0,NULL,FALSE );
		char* pszUtf8Buf = (char*)malloc((nUtf8Length+1)*sizeof(char));
		memset(pszUtf8Buf, 0, (nUtf8Length+1)*sizeof(char));

		WideCharToMultiByte(CP_UTF8, 0, pwszBuf, -1, pszUtf8Buf, (nUtf8Length+1)*sizeof(char), NULL, FALSE);
		ret = pszUtf8Buf;

		free(pszUtf8Buf);
		free(pwszBuf);
	}
	return ret;
}

std::string CWin32InputBox::Utf8ToAnsi(std::string strUTF8)
{
	std::string ret;
	if (strUTF8.length() > 0)
	{
		int nWideStrLength = MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, NULL, 0);
		WCHAR* pwszBuf = (WCHAR*)malloc((nWideStrLength+1)*sizeof(WCHAR));
		memset(pwszBuf, 0, (nWideStrLength+1)*sizeof(WCHAR));
		MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, pwszBuf, (nWideStrLength+1)*sizeof(WCHAR));

		int nAnsiStrLength = WideCharToMultiByte( CP_ACP,0,pwszBuf,-1,NULL,0,NULL,FALSE );
		char* pszAnsiBuf = (char*)malloc((nAnsiStrLength+1)*sizeof(char));
		memset(pszAnsiBuf, 0, (nAnsiStrLength+1)*sizeof(char));

		WideCharToMultiByte(CP_ACP, 0, pwszBuf, -1, pszAnsiBuf, (nAnsiStrLength+1)*sizeof(char), NULL, FALSE);
		ret = pszAnsiBuf;

		free(pszAnsiBuf);
		free(pwszBuf);
	}

	return ret;
}



#endif /* #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) */