/*
 * SHLWAPI message box functions
 *
 * Copyright 2004 Jon Griffiths
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"
#include "wine/port.h"

#include <stdarg.h>
#include <string.h>

#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "winreg.h"
#include "shlwapi.h"
#include "wine/unicode.h"
#include "wine/debug.h"
#include "resource.h"


WINE_DEFAULT_DEBUG_CHANNEL(shell);

extern HINSTANCE shlwapi_hInstance; /* in shlwapi_main.c */

static const WCHAR szDontShowKey[] = { 'S','o','f','t','w','a','r','e','\\',
  'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
  'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
  'E','x','p','l','o','r','e','r','\\','D','o','n','t','S','h','o','w',
  'M','e','T','h','i','s','D','i','a','l','o','g','A','g','a','i','n','\0'
};

INT_PTR WINAPI SHMessageBoxCheckExW(HWND,HINSTANCE,LPCWSTR,DLGPROC,LPARAM,INT_PTR,LPCWSTR);
INT_PTR WINAPI SHMessageBoxCheckW(HWND,LPCWSTR,LPCWSTR,DWORD,INT_PTR,LPCWSTR);

/* Data held by each general message boxes */
typedef struct tagDLGDATAEX
{
  DLGPROC dlgProc;   /* User supplied DlgProc */
  LPARAM  lParam;    /* User supplied LPARAM for dlgProc */
  LPCWSTR lpszId;    /* Name of reg key holding whether to skip */
} DLGDATAEX;

/* Dialogue procedure for general message boxes */
static INT_PTR CALLBACK SHDlgProcEx(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  DLGDATAEX *d = (DLGDATAEX *)GetWindowLongPtrW(hDlg, DWLP_USER);

  TRACE("(%p,%u,%ld,%ld) data %p\n", hDlg, uMsg, wParam, lParam, d);

  switch (uMsg)
  {
  case WM_INITDIALOG:
  {
    /* FIXME: Not sure where native stores its lParam */
    SetWindowLongPtrW(hDlg, DWLP_USER, lParam);
    d = (DLGDATAEX *)lParam;
    TRACE("WM_INITDIALOG: %p, %s,%p,%p\n", hDlg, debugstr_w(d->lpszId),
          d->dlgProc, (void*)d->lParam);
    if (d->dlgProc)
      return d->dlgProc(hDlg, uMsg, wParam, d->lParam);
    return TRUE;
  }

  case WM_COMMAND:
    switch (LOWORD(wParam))
    {
      case IDYES:
        wParam = MAKELONG(IDOK, HIWORD(wParam));
        /* Fall through ... */
      case IDNO:
        if (LOWORD(wParam) == IDNO)
          wParam = MAKELONG(IDCANCEL, HIWORD(wParam));
        /* Fall through ... */
      case IDOK:
      case IDCANCEL:

        TRACE("WM_COMMAND: id=%s data=%p\n",
              LOWORD(wParam) == IDOK ? "IDOK" : "IDCANCEL", d);

        if (SendMessageW(GetDlgItem(hDlg, IDC_ERR_DONT_SHOW), BM_GETCHECK, 0L, 0L))
        {
          DWORD dwZero = 0;

          /* The user clicked 'don't show again', so set the key */
          SHRegSetUSValueW(szDontShowKey, d->lpszId, REG_DWORD, &dwZero,
                           sizeof(dwZero), SHREGSET_DEFAULT);
        }
        if (!d->dlgProc || !d->dlgProc(hDlg, uMsg, wParam, lParam))
          EndDialog(hDlg, wParam);
        return TRUE;
    }
    break;

  default:
    break;
  }

  if (d && d->dlgProc)
    return d->dlgProc(hDlg, uMsg, wParam, lParam);
  return FALSE;
}

/*************************************************************************
 * @ [SHLWAPI.291]
 *
 * Pop up a 'Don't show this message again' dialogue box.
 *
 * PARAMS
 *  hWnd     [I] Window to be the dialogues' parent
 *  hInst    [I] Instance of the module holding the dialogue resource
 *  lpszName [I] Resource Id of the dialogue resource
 *  dlgProc  [I] Dialog procedure, or NULL for default handling
 *  lParam   [I] LPARAM to pass to dlgProc
 *  iRet     [I] Value to return if dialogue is not shown
 *  lpszId   [I] Name of registry subkey which determines whether to show the dialog
 *
 * RETURNS
 *  Success: The value returned from the dialogue procedure.
 *  Failure: iRet, if the dialogue resource could not be loaded or the dialogue
 *           should not be shown.
 *
 * NOTES
 *  Both lpszName and lpszId must be less than MAX_PATH in length.
 */
INT_PTR WINAPI SHMessageBoxCheckExA(HWND hWnd, HINSTANCE hInst, LPCSTR lpszName,
                                    DLGPROC dlgProc, LPARAM lParam, INT_PTR iRet,
                                    LPCSTR lpszId)
{
  WCHAR szNameBuff[MAX_PATH], szIdBuff[MAX_PATH];
  LPCWSTR szName = szNameBuff;

  if (HIWORD(lpszName))
    MultiByteToWideChar(CP_ACP, 0, lpszName, -1, szNameBuff, MAX_PATH);
  else
    szName = (LPCWSTR)lpszName; /* Resource Id or NULL */

  MultiByteToWideChar(CP_ACP, 0, lpszId, -1, szIdBuff, MAX_PATH);

  return SHMessageBoxCheckExW(hWnd, hInst, szName, dlgProc, lParam, iRet, szIdBuff);
}

/*************************************************************************
 * @ [SHLWAPI.292]
 *
 * Unicode version of SHMessageBoxCheckExW.
 */
INT_PTR WINAPI SHMessageBoxCheckExW(HWND hWnd, HINSTANCE hInst, LPCWSTR lpszName,
                                    DLGPROC dlgProc, LPARAM lParam, INT_PTR iRet, LPCWSTR lpszId)
{
  DLGDATAEX d;

  if (!SHRegGetBoolUSValueW(szDontShowKey, lpszId, FALSE, TRUE))
    return iRet;

  d.dlgProc = dlgProc;
  d.lParam = lParam;
  d.lpszId = lpszId;
  return DialogBoxParamW(hInst, (LPCWSTR)lpszName, hWnd, SHDlgProcEx, (LPARAM)&d);
}

/* Data held by each shlwapi message box */
typedef struct tagDLGDATA
{
  LPCWSTR lpszTitle; /* User supplied message title */
  LPCWSTR lpszText;  /* User supplied message text */
  DWORD   dwType;    /* Message box type */
} DLGDATA;

/* Dialogue procedure for shlwapi message boxes */
static INT_PTR CALLBACK SHDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  TRACE("(%p,%u,%ld,%ld)\n", hDlg, uMsg, wParam, lParam);

  switch (uMsg)
  {
  case WM_INITDIALOG:
  {
    DLGDATA *d = (DLGDATA *)lParam;
    TRACE("WM_INITDIALOG: %p, %s,%s,%d\n", hDlg, debugstr_w(d->lpszTitle),
          debugstr_w(d->lpszText), d->dwType);

    SetWindowTextW(hDlg, d->lpszTitle);
    SetWindowTextW(GetDlgItem(hDlg, IDS_ERR_USER_MSG), d->lpszText);

    /* Set buttons according to dwType */
    switch (d->dwType)
    {
    case 0:
      ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_HIDE);
      /* FIXME: Move OK button to position of the Cancel button (cosmetic) */
    case 1:
      ShowWindow(GetDlgItem(hDlg, IDYES), SW_HIDE);
      ShowWindow(GetDlgItem(hDlg, IDNO), SW_HIDE);
      break;
    default:
      ShowWindow(GetDlgItem(hDlg, IDOK), SW_HIDE);
      ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_HIDE);
      break;
    }
    return TRUE;
  }
  default:
    break;
  }
  return FALSE;
}

/*************************************************************************
 * @ [SHLWAPI.185]
 *
 * Pop up a 'Don't show this message again' dialogue box.
 *
 * PARAMS
 *  hWnd      [I] Window to be the dialogues' parent
 *  lpszText  [I] Text of the message to show
 *  lpszTitle [I] Title of the dialogue box
 *  dwType    [I] Type of dialogue buttons (See below)
 *  iRet      [I] Value to return if dialogue is not shown
 *  lpszId    [I] Name of registry subkey which determines whether to show the dialog
 *
 * RETURNS
 *  Success: The value returned from the dialogue procedure (e.g. IDOK).
 *  Failure: iRet, if the default dialogue resource could not be loaded or the
 *           dialogue should not be shown.
 *
 * NOTES
 *  - Both lpszTitle and lpszId must be less than MAX_PATH in length.
 *  - Possible values for dwType are:
 *| Value     Buttons
 *| -----     -------
 *|   0       OK
 *|   1       OK/Cancel
 *|   2       Yes/No
 */
INT_PTR WINAPI SHMessageBoxCheckA(HWND hWnd, LPCSTR lpszText, LPCSTR lpszTitle,
                                  DWORD dwType, INT_PTR iRet, LPCSTR lpszId)
{
  WCHAR szTitleBuff[MAX_PATH], szIdBuff[MAX_PATH];
  WCHAR *szTextBuff = NULL;
  int iLen;
  INT_PTR iRetVal;

  if (lpszTitle)
    MultiByteToWideChar(CP_ACP, 0, lpszTitle, -1, szTitleBuff, MAX_PATH);

  if (lpszText)
  {
    iLen = MultiByteToWideChar(CP_ACP, 0, lpszText, -1, NULL, 0);
    szTextBuff = HeapAlloc(GetProcessHeap(), 0, iLen * sizeof(WCHAR));
    MultiByteToWideChar(CP_ACP, 0, lpszText, -1, szTextBuff, iLen);
  }

  MultiByteToWideChar(CP_ACP, 0, lpszId, -1, szIdBuff, MAX_PATH);

  iRetVal = SHMessageBoxCheckW(hWnd, szTextBuff, lpszTitle ? szTitleBuff : NULL,
                               dwType, iRet, szIdBuff);
  HeapFree(GetProcessHeap(), 0, szTextBuff);
  return iRetVal;
}

/*************************************************************************
 * @ [SHLWAPI.191]
 *
 * Unicode version of SHMessageBoxCheckA.
 */
INT_PTR WINAPI SHMessageBoxCheckW(HWND hWnd, LPCWSTR lpszText, LPCWSTR lpszTitle,
                                  DWORD dwType, INT_PTR iRet, LPCWSTR lpszId)
{
  DLGDATA d;

  d.lpszTitle = lpszTitle;
  d.lpszText = lpszText;
  d.dwType = dwType;

  return SHMessageBoxCheckExW(hWnd, shlwapi_hInstance, (LPCWSTR)IDD_ERR_DIALOG,
                               SHDlgProc, (LPARAM)&d, iRet, lpszId);
}
