/*
 * SetupAPI dialog functions
 *
 * Copyright 2009 Ricardo Filipe
 *
 * 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 <stdarg.h>

#include "wine/debug.h"
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winreg.h"
#include "commdlg.h"
#include "setupapi.h"
#include "winnls.h"
#include "setupapi_private.h"

#include "wine/unicode.h"

WINE_DEFAULT_DEBUG_CHANNEL(setupapi);

struct promptdisk_params {
    PCWSTR DialogTitle;
    PCWSTR DiskName;
    PCWSTR PathToSource;
    PCWSTR FileSought;
    PCWSTR TagFile;
    DWORD DiskPromptStyle;
    PWSTR PathBuffer;
    DWORD PathBufferSize;
    PDWORD PathRequiredSize;
};

/* initiates the fields of the SetupPromptForDisk dialog according to the parameters
*/
static void promptdisk_init(HWND hwnd, struct promptdisk_params *params)
{
    SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)params);

    if(params->DialogTitle)
        SetWindowTextW(hwnd, params->DialogTitle);
    if(params->PathToSource)
        SetDlgItemTextW(hwnd, IDC_PATH, params->PathToSource);

    if(!(params->DiskPromptStyle & IDF_OEMDISK))
    {
        WCHAR message[256+2*MAX_PATH];
        WCHAR format[256];
        WCHAR unknown[256];
        DWORD_PTR args[2];
        LoadStringW(SETUPAPI_hInstance, IDS_PROMPTDISK, format,
            sizeof(format)/sizeof(format[0]));

        args[0] = (DWORD_PTR)params->FileSought;
        if(params->DiskName)
            args[1] = (DWORD_PTR)params->DiskName;
        else
        {
            LoadStringW(SETUPAPI_hInstance, IDS_UNKNOWN, unknown,
                sizeof(unknown)/sizeof(unknown[0]));
            args[1] = (DWORD_PTR)unknown;
        }
        FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
                       format, 0, 0, message, sizeof(message)/sizeof(*message),
                       (__ms_va_list*)args);
        SetDlgItemTextW(hwnd, IDC_FILENEEDED, message);

        LoadStringW(SETUPAPI_hInstance, IDS_INFO, message,
            sizeof(message)/sizeof(message[0]));
        SetDlgItemTextW(hwnd, IDC_INFO, message);
        LoadStringW(SETUPAPI_hInstance, IDS_COPYFROM, message,
            sizeof(message)/sizeof(message[0]));
        SetDlgItemTextW(hwnd, IDC_COPYFROM, message);
    }
    if(params->DiskPromptStyle & IDF_NOBROWSE)
        ShowWindow(GetDlgItem(hwnd, IDC_RUNDLG_BROWSE), SW_HIDE);
}

/* When the user clicks in the Ok button in SetupPromptForDisk dialog
 * if the parameters are good it copies the path from the dialog to the output buffer
 * saves the required size for the buffer if PathRequiredSize is given
 * returns NO_ERROR if there is no PathBuffer to copy too
 * returns DPROMPT_BUFFERTOOSMALL if the path is too big to fit in PathBuffer
 */
static void promptdisk_ok(HWND hwnd, struct promptdisk_params *params)
{
    int requiredSize;
    WCHAR aux[MAX_PATH];
    GetWindowTextW(GetDlgItem(hwnd, IDC_PATH), aux, MAX_PATH);
    requiredSize = strlenW(aux)+1;

    if(params->PathRequiredSize)
    {
        *params->PathRequiredSize = requiredSize;
        TRACE("returning PathRequiredSize=%d\n",*params->PathRequiredSize);
    }
    if(!params->PathBuffer)
    {
        EndDialog(hwnd, NO_ERROR);
        return;
    }
    if(requiredSize > params->PathBufferSize)
    {
        EndDialog(hwnd, DPROMPT_BUFFERTOOSMALL);
        return;
    }
    strcpyW(params->PathBuffer, aux);
    TRACE("returning PathBuffer=%s\n", debugstr_w(params->PathBuffer));
    EndDialog(hwnd, DPROMPT_SUCCESS);
}

/* When the user clicks the browse button in SetupPromptForDisk dialog
 * it copies the path of the selected file to the dialog path field
 */
static void promptdisk_browse(HWND hwnd, struct promptdisk_params *params)
{
    OPENFILENAMEW ofn;
    ZeroMemory(&ofn, sizeof(ofn));

    ofn.lStructSize = sizeof(ofn);
    ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
    ofn.hwndOwner = hwnd;
    ofn.nMaxFile = MAX_PATH;
    ofn.lpstrFile = HeapAlloc(GetProcessHeap(), 0, MAX_PATH*sizeof(WCHAR));
    strcpyW(ofn.lpstrFile, params->FileSought);

    if(GetOpenFileNameW(&ofn))
    {
        WCHAR* last_slash = strrchrW(ofn.lpstrFile, '\\');
        if (last_slash) *last_slash = 0;
        SetDlgItemTextW(hwnd, IDC_PATH, ofn.lpstrFile);
    }
    HeapFree(GetProcessHeap(), 0, ofn.lpstrFile);
}

/* Handles the messages sent to the SetupPromptForDisk dialog
*/
static INT_PTR CALLBACK promptdisk_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_INITDIALOG:
            promptdisk_init(hwnd, (struct promptdisk_params *)lParam);
            return TRUE;
        case WM_COMMAND:
            switch(wParam)
            {
                case IDOK:
                {
                    struct promptdisk_params *params =
                        (struct promptdisk_params *)GetWindowLongPtrW(hwnd, DWLP_USER);
                    promptdisk_ok(hwnd, params);
                    return TRUE;
                }
                case IDCANCEL:
                    EndDialog(hwnd, DPROMPT_CANCEL);
                    return TRUE;
                case IDC_RUNDLG_BROWSE:
                {
                    struct promptdisk_params *params =
                        (struct promptdisk_params *)GetWindowLongPtrW(hwnd, DWLP_USER);
                    promptdisk_browse(hwnd, params);
                    return TRUE;
                }
            }
    }
    return FALSE;
}

/***********************************************************************
 *      SetupPromptForDiskA (SETUPAPI.@)
 */
UINT WINAPI SetupPromptForDiskA(HWND hwndParent, PCSTR DialogTitle, PCSTR DiskName,
        PCSTR PathToSource, PCSTR FileSought, PCSTR TagFile, DWORD DiskPromptStyle,
        PSTR PathBuffer, DWORD PathBufferSize, PDWORD PathRequiredSize)
{
    WCHAR *DialogTitleW, *DiskNameW, *PathToSourceW;
    WCHAR *FileSoughtW, *TagFileW, PathBufferW[MAX_PATH];
    UINT ret, length;

    TRACE("%p, %s, %s, %s, %s, %s, 0x%08x, %p, %d, %p\n", hwndParent, debugstr_a(DialogTitle),
          debugstr_a(DiskName), debugstr_a(PathToSource), debugstr_a(FileSought),
          debugstr_a(TagFile), DiskPromptStyle, PathBuffer, PathBufferSize,
          PathRequiredSize);

    DialogTitleW = strdupAtoW(DialogTitle);
    DiskNameW = strdupAtoW(DiskName);
    PathToSourceW = strdupAtoW(PathToSource);
    FileSoughtW = strdupAtoW(FileSought);
    TagFileW = strdupAtoW(TagFile);

    ret = SetupPromptForDiskW(hwndParent, DialogTitleW, DiskNameW, PathToSourceW,
            FileSoughtW, TagFileW, DiskPromptStyle, PathBufferW, MAX_PATH, PathRequiredSize);

    HeapFree(GetProcessHeap(), 0, DialogTitleW);
    HeapFree(GetProcessHeap(), 0, DiskNameW);
    HeapFree(GetProcessHeap(), 0, PathToSourceW);
    HeapFree(GetProcessHeap(), 0, FileSoughtW);
    HeapFree(GetProcessHeap(), 0, TagFileW);

    if(ret == DPROMPT_SUCCESS)
    {
        length = WideCharToMultiByte(CP_ACP, 0, PathBufferW, -1, NULL, 0, NULL, NULL);
        if(PathRequiredSize) *PathRequiredSize = length;
        if(PathBuffer)
        {
            if(length > PathBufferSize)
                return DPROMPT_BUFFERTOOSMALL;
            WideCharToMultiByte(CP_ACP, 0, PathBufferW, -1, PathBuffer, length, NULL, NULL);
        }
    }
    return ret;
}

/***********************************************************************
 *      SetupPromptForDiskW (SETUPAPI.@)
 */
UINT WINAPI SetupPromptForDiskW(HWND hwndParent, PCWSTR DialogTitle, PCWSTR DiskName,
        PCWSTR PathToSource, PCWSTR FileSought, PCWSTR TagFile, DWORD DiskPromptStyle,
        PWSTR PathBuffer, DWORD PathBufferSize, PDWORD PathRequiredSize)
{
    struct promptdisk_params params;
    UINT ret;

    TRACE("%p, %s, %s, %s, %s, %s, 0x%08x, %p, %d, %p\n", hwndParent, debugstr_w(DialogTitle),
          debugstr_w(DiskName), debugstr_w(PathToSource), debugstr_w(FileSought),
          debugstr_w(TagFile), DiskPromptStyle, PathBuffer, PathBufferSize,
          PathRequiredSize);

    if(!FileSought)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return DPROMPT_CANCEL;
    }
    params.DialogTitle = DialogTitle;
    params.DiskName = DiskName;
    params.PathToSource = PathToSource;
    params.FileSought = FileSought;
    params.TagFile = TagFile;
    params.DiskPromptStyle = DiskPromptStyle;
    params.PathBuffer = PathBuffer;
    params.PathBufferSize = PathBufferSize;
    params.PathRequiredSize = PathRequiredSize;

    ret = DialogBoxParamW(SETUPAPI_hInstance, MAKEINTRESOURCEW(IDPROMPTFORDISK),
        hwndParent, promptdisk_proc, (LPARAM)&params);

    if(ret == DPROMPT_CANCEL)
        SetLastError(ERROR_CANCELLED);
    return ret;
}