/*
 *    Folder Options
 *
 * Copyright 2007 Johannes Anderwald <johannes.anderwald@reactos.org>
 * Copyright 2016-2018 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
 *
 * 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 "precomp.h"

WINE_DEFAULT_DEBUG_CHANNEL (fprop);

// Folder Options:
// CLASSKEY = HKEY_CLASSES_ROOT\CLSID\{6DFD7C5C-2451-11d3-A299-00C04F8EF6AF}

/////////////////////////////////////////////////////////////////////////////
// strings

// path to shell32
LPCWSTR g_pszShell32 = L"%SystemRoot%\\system32\\shell32.dll";

// the space characters
LPCWSTR g_pszSpace = L" \t\n\r\f\v";

/////////////////////////////////////////////////////////////////////////////
// utility functions

HBITMAP Create24BppBitmap(HDC hDC, INT cx, INT cy)
{
    BITMAPINFO bi;
    LPVOID pvBits;

    ZeroMemory(&bi, sizeof(bi));
    bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bi.bmiHeader.biWidth = cx;
    bi.bmiHeader.biHeight = cy;
    bi.bmiHeader.biPlanes = 1;
    bi.bmiHeader.biBitCount = 24;
    bi.bmiHeader.biCompression = BI_RGB;

    HBITMAP hbm = CreateDIBSection(hDC, &bi, DIB_RGB_COLORS, &pvBits, NULL, 0);
    return hbm;
}

HBITMAP BitmapFromIcon(HICON hIcon, INT cx, INT cy)
{
    HDC hDC = CreateCompatibleDC(NULL);
    if (!hDC)
        return NULL;

    HBITMAP hbm = Create24BppBitmap(hDC, cx, cy);
    if (!hbm)
    {
        DeleteDC(hDC);
        return NULL;
    }

    HGDIOBJ hbmOld = SelectObject(hDC, hbm);
    {
        RECT rc = { 0, 0, cx, cy };
        FillRect(hDC, &rc, HBRUSH(COLOR_3DFACE + 1));
        if (hIcon)
        {
            DrawIconEx(hDC, 0, 0, hIcon, cx, cy, 0, NULL, DI_NORMAL);
        }
    }
    SelectObject(hDC, hbmOld);
    DeleteDC(hDC);

    return hbm;
}

HBITMAP CreateCheckImage(HDC hDC, BOOL bCheck, BOOL bEnabled)
{
    INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON);
    INT cySmallIcon = GetSystemMetrics(SM_CYSMICON);

    HBITMAP hbm = Create24BppBitmap(hDC, cxSmallIcon, cySmallIcon);
    if (hbm == NULL)
        return NULL;    // failure

    RECT Rect, BoxRect;
    SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon);
    BoxRect = Rect;
    InflateRect(&BoxRect, -1, -1);

    HGDIOBJ hbmOld = SelectObject(hDC, hbm);
    {
        UINT uState = DFCS_BUTTONCHECK | DFCS_FLAT | DFCS_MONO;
        if (bCheck)
            uState |= DFCS_CHECKED;
        if (!bEnabled)
            uState |= DFCS_INACTIVE;
        DrawFrameControl(hDC, &BoxRect, DFC_BUTTON, uState);
    }
    SelectObject(hDC, hbmOld);

    return hbm;     // success
}

HBITMAP CreateCheckMask(HDC hDC)
{
    INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON);
    INT cySmallIcon = GetSystemMetrics(SM_CYSMICON);

    HBITMAP hbm = CreateBitmap(cxSmallIcon, cySmallIcon, 1, 1, NULL);
    if (hbm == NULL)
        return NULL;    // failure

    RECT Rect, BoxRect;
    SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon);
    BoxRect = Rect;
    InflateRect(&BoxRect, -1, -1);

    HGDIOBJ hbmOld = SelectObject(hDC, hbm);
    {
        FillRect(hDC, &Rect, HBRUSH(GetStockObject(WHITE_BRUSH)));
        FillRect(hDC, &BoxRect, HBRUSH(GetStockObject(BLACK_BRUSH)));
    }
    SelectObject(hDC, hbmOld);

    return hbm;     // success
}

HBITMAP CreateRadioImage(HDC hDC, BOOL bCheck, BOOL bEnabled)
{
    INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON);
    INT cySmallIcon = GetSystemMetrics(SM_CYSMICON);

    HBITMAP hbm = Create24BppBitmap(hDC, cxSmallIcon, cySmallIcon);
    if (hbm == NULL)
        return NULL;    // failure

    RECT Rect, BoxRect;
    SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon);
    BoxRect = Rect;
    InflateRect(&BoxRect, -1, -1);

    HGDIOBJ hbmOld = SelectObject(hDC, hbm);
    {
        UINT uState = DFCS_BUTTONRADIOIMAGE | DFCS_FLAT | DFCS_MONO;
        if (bCheck)
            uState |= DFCS_CHECKED;
        if (!bEnabled)
            uState |= DFCS_INACTIVE;
        DrawFrameControl(hDC, &BoxRect, DFC_BUTTON, uState);
    }
    SelectObject(hDC, hbmOld);

    return hbm;     // success
}

HBITMAP CreateRadioMask(HDC hDC)
{
    INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON);
    INT cySmallIcon = GetSystemMetrics(SM_CYSMICON);

    HBITMAP hbm = CreateBitmap(cxSmallIcon, cySmallIcon, 1, 1, NULL);
    if (hbm == NULL)
        return NULL;    // failure

    RECT Rect, BoxRect;
    SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon);
    BoxRect = Rect;
    InflateRect(&BoxRect, -1, -1);

    HGDIOBJ hbmOld = SelectObject(hDC, hbm);
    {
        FillRect(hDC, &Rect, HBRUSH(GetStockObject(WHITE_BRUSH)));
        UINT uState = DFCS_BUTTONRADIOMASK | DFCS_FLAT | DFCS_MONO;
        DrawFrameControl(hDC, &BoxRect, DFC_BUTTON, uState);
    }
    SelectObject(hDC, hbmOld);

    return hbm;     // success
}

/////////////////////////////////////////////////////////////////////////////

// CMSGlobalFolderOptionsStub --- The owner window of Folder Options.
// This window hides taskbar button of Folder Options.

#define GlobalFolderOptionsClassName _T("MSGlobalFolderOptionsStub")

class CMSGlobalFolderOptionsStub : public CWindowImpl<CMSGlobalFolderOptionsStub>
{
public:
    DECLARE_WND_CLASS_EX(GlobalFolderOptionsClassName, 0, COLOR_WINDOWTEXT)

    BEGIN_MSG_MAP(CMSGlobalFolderOptionsStub)
    END_MSG_MAP()
};

/////////////////////////////////////////////////////////////////////////////

EXTERN_C HPSXA WINAPI SHCreatePropSheetExtArrayEx(HKEY hKey, LPCWSTR pszSubKey, UINT max_iface, IDataObject *pDataObj);

static int CALLBACK
PropSheetProc(HWND hwndDlg, UINT uMsg, LPARAM lParam)
{
    // NOTE: This callback is needed to set large icon correctly.
    HICON hIcon;
    switch (uMsg)
    {
        case PSCB_INITIALIZED:
        {
            hIcon = LoadIconW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_FOLDER_OPTIONS));
            SendMessageW(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
            break;
        }
    }
    return 0;
}

enum {
    PAGE_GENERAL,
    PAGE_VIEW,
    PAGE_FILETYPES
};

static DWORD CALLBACK
ShowFolderOptionsDialogThreadProc(LPVOID param)
{
    PROPSHEETHEADERW pinfo;
    HPROPSHEETPAGE hppages[3];
    HPROPSHEETPAGE hpage;
    UINT num_pages = 0;

    hpage = SH_CreatePropertySheetPage(IDD_FOLDER_OPTIONS_GENERAL, FolderOptionsGeneralDlg, 0, NULL);
    if (hpage)
        hppages[num_pages++] = hpage;

    hpage = SH_CreatePropertySheetPage(IDD_FOLDER_OPTIONS_VIEW, FolderOptionsViewDlg, 0, NULL);
    if (hpage)
        hppages[num_pages++] = hpage;

    hpage = SH_CreatePropertySheetPage(IDD_FOLDER_OPTIONS_FILETYPES, FolderOptionsFileTypesDlg, 0, NULL);
    if (hpage)
        hppages[num_pages++] = hpage;

    // the stub window to hide taskbar button
    DWORD style = WS_DISABLED | WS_CLIPSIBLINGS | WS_CAPTION;
    DWORD exstyle = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW;
    CMSGlobalFolderOptionsStub stub;
    if (!stub.Create(NULL, NULL, NULL, style, exstyle))
    {
        ERR("stub.Create failed\n");
        return 0;
    }

    memset(&pinfo, 0x0, sizeof(PROPSHEETHEADERW));
    pinfo.dwSize = sizeof(PROPSHEETHEADERW);
    pinfo.dwFlags = PSH_NOCONTEXTHELP | PSH_USEICONID | PSH_USECALLBACK;
    pinfo.hwndParent = stub;
    pinfo.nPages = num_pages;
    pinfo.phpage = hppages;
    pinfo.hInstance = shell32_hInstance;
    pinfo.pszIcon = MAKEINTRESOURCEW(IDI_SHELL_FOLDER_OPTIONS);
    pinfo.pszCaption = MAKEINTRESOURCEW(IDS_FOLDER_OPTIONS);
    pinfo.pfnCallback = PropSheetProc;
    pinfo.nStartPage = PtrToUlong(param);

    PropertySheetW(&pinfo);

    stub.DestroyWindow();
    return 0;
}

VOID WINAPI 
ShowFolderOptionsDialog(UINT Page, BOOL Async = FALSE)
{
    HWND hWnd = FindWindow(GlobalFolderOptionsClassName, NULL);
    if (hWnd)
    {
        HWND hPop = GetLastActivePopup(hWnd);
        if (hWnd == GetParent(hPop))
        {
            PostMessage(hPop, PSM_SETCURSEL, Page, 0);
        }
        SetForegroundWindow(hPop);
        return;
    }
    
    LPVOID param = UlongToPtr(Page);
    if (Async)
        SHCreateThread(ShowFolderOptionsDialogThreadProc, param, 0, 0);
    else
        ShowFolderOptionsDialogThreadProc(param); // Rundll32 caller cannot be async!
}

static VOID
Options_RunDLLCommon(HWND hWnd, HINSTANCE hInst, int fOptions, DWORD nCmdShow)
{
    switch(fOptions)
    {
        case 0:
            ShowFolderOptionsDialog(PAGE_GENERAL);
            break;

        case 1: // Taskbar settings
#if (NTDDI_VERSION >= NTDDI_VISTA)
        case 3: // Start menu settings
        case 4: // Tray icon settings
        case 6: // Taskbar toolbars
#endif
            PostMessage(GetShellWindow(), WM_PROGMAN_OPENSHELLSETTINGS, fOptions, 0);
            break;

        case 7: // Windows 8, 10
            ShowFolderOptionsDialog(PAGE_VIEW);
            break;

        default:
            FIXME("unrecognized options id %d\n", fOptions);
    }
}

/*************************************************************************
 *              Options_RunDLL (SHELL32.@)
 */
EXTERN_C VOID WINAPI
Options_RunDLL(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow)
{
    Options_RunDLLCommon(hWnd, hInst, StrToIntA(cmd), nCmdShow);
}

/*************************************************************************
 *              Options_RunDLLA (SHELL32.@)
 */
EXTERN_C VOID WINAPI
Options_RunDLLA(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow)
{
    Options_RunDLLCommon(hWnd, hInst, StrToIntA(cmd), nCmdShow);
}

/*************************************************************************
 *              Options_RunDLLW (SHELL32.@)
 */
EXTERN_C VOID WINAPI
Options_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow)
{
    Options_RunDLLCommon(hWnd, hInst, StrToIntW(cmd), nCmdShow);
}
