/*
 * ReactOS Explorer
 *
 * Copyright 2009 Andrew Hill <ash77 at domain reactos.org>
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
Implements the toolbar band of a cabinet window
*/

#include "precomp.h"

/* FIXME, I can't include windowsx because it conflicts with some #defines */
#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))

class CToolsBand :
    public CWindowImpl<CToolsBand, CWindow, CControlWinTraits>,
    public CComObjectRootEx<CComMultiThreadModelNoCS>,
    public IDeskBand,
    public IObjectWithSite,
    public IInputObject,
    public IPersistStream
{
private:
    CComPtr<IDockingWindowSite> fDockSite;
    GUID                        fExecCommandCategory;
    CComPtr<IOleCommandTarget>  fExecCommandTarget;
public:
    CToolsBand();
    virtual ~CToolsBand();
public:
    // *** IDeskBand methods ***
    virtual HRESULT STDMETHODCALLTYPE GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO* pdbi);

    // *** IObjectWithSite methods ***
    virtual HRESULT STDMETHODCALLTYPE SetSite(IUnknown* pUnkSite);
    virtual HRESULT STDMETHODCALLTYPE GetSite(REFIID riid, void **ppvSite);

    // *** IOleWindow methods ***
    virtual HRESULT STDMETHODCALLTYPE GetWindow(HWND *lphwnd);
    virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode);

    // *** IDockingWindow methods ***
    virtual HRESULT STDMETHODCALLTYPE CloseDW(DWORD dwReserved);
    virtual HRESULT STDMETHODCALLTYPE ResizeBorderDW(const RECT* prcBorder, IUnknown* punkToolbarSite, BOOL fReserved);
    virtual HRESULT STDMETHODCALLTYPE ShowDW(BOOL fShow);

    // *** IInputObject methods ***
    virtual HRESULT STDMETHODCALLTYPE HasFocusIO();
    virtual HRESULT STDMETHODCALLTYPE TranslateAcceleratorIO(LPMSG lpMsg);
    virtual HRESULT STDMETHODCALLTYPE UIActivateIO(BOOL fActivate, LPMSG lpMsg);

    // *** IPersist methods ***
    virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID *pClassID);

    // *** IPersistStream methods ***
    virtual HRESULT STDMETHODCALLTYPE IsDirty();
    virtual HRESULT STDMETHODCALLTYPE Load(IStream *pStm);
    virtual HRESULT STDMETHODCALLTYPE Save(IStream *pStm, BOOL fClearDirty);
    virtual HRESULT STDMETHODCALLTYPE GetSizeMax(ULARGE_INTEGER *pcbSize);

    // message handlers
    LRESULT OnGetButtonInfo(UINT idControl, NMHDR *pNMHDR, BOOL &bHandled);

BEGIN_MSG_MAP(CToolsBand)
//    MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
//    MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
    NOTIFY_HANDLER(0, TBN_GETBUTTONINFOW, OnGetButtonInfo)
END_MSG_MAP()

BEGIN_COM_MAP(CToolsBand)
    COM_INTERFACE_ENTRY_IID(IID_IDeskBand, IDeskBand)
    COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite)
    COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
    COM_INTERFACE_ENTRY_IID(IID_IDockingWindow, IDockingWindow)
    COM_INTERFACE_ENTRY_IID(IID_IInputObject, IInputObject)
    COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist)
    COM_INTERFACE_ENTRY_IID(IID_IPersistStream, IPersistStream)
END_COM_MAP()
};

CToolsBand::CToolsBand()
    : fDockSite(NULL)
{
}

CToolsBand::~CToolsBand()
{
}

HRESULT STDMETHODCALLTYPE CToolsBand::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO* pdbi)
{
    RECT actualRect;
    POINTL actualSize;
    POINTL idealSize;
    POINTL maxSize;
    POINTL itemSize;

    ::GetWindowRect(m_hWnd, &actualRect);
    actualSize.x = actualRect.right - actualRect.left;
    actualSize.y = actualRect.bottom - actualRect.top;

    /* Obtain the ideal size, to be used as min and max */
    SendMessageW(m_hWnd, TB_AUTOSIZE, 0, 0);
    SendMessageW(m_hWnd, TB_GETMAXSIZE, 0, reinterpret_cast<LPARAM>(&maxSize));

    idealSize = maxSize;
    SendMessageW(m_hWnd, TB_GETIDEALSIZE, FALSE, reinterpret_cast<LPARAM>(&idealSize));

    /* Obtain the button size, to be used as the integral size */
    DWORD size = SendMessageW(m_hWnd, TB_GETBUTTONSIZE, 0, 0);
    itemSize.x = GET_X_LPARAM(size);
    itemSize.y = GET_Y_LPARAM(size);

    if (pdbi->dwMask & DBIM_MINSIZE)
    {
        pdbi->ptMinSize = idealSize;
    }
    if (pdbi->dwMask & DBIM_MAXSIZE)
    {
        pdbi->ptMaxSize = maxSize;
    }
    if (pdbi->dwMask & DBIM_INTEGRAL)
    {
        pdbi->ptIntegral = itemSize;
    }
    if (pdbi->dwMask & DBIM_ACTUAL)
    {
        pdbi->ptActual = actualSize;
    }
    if (pdbi->dwMask & DBIM_TITLE)
        wcscpy(pdbi->wszTitle, L"");
    if (pdbi->dwMask & DBIM_MODEFLAGS)
        pdbi->dwModeFlags = DBIMF_UNDELETEABLE;
    if (pdbi->dwMask & DBIM_BKCOLOR)
        pdbi->crBkgnd = 0;
    return S_OK;
}

static const int backImageIndex = 0;
static const int forwardImageIndex = 1;
static const int favoritesImageIndex = 2;
// 3
// 4
static const int cutImageIndex = 5;
static const int copyImageIndex = 6;
static const int pasteImageIndex = 7;
static const int undoImageIndex = 8;
static const int redoImageIndex = 9;
static const int deleteImageIndex = 10;
// 11
// 12
// 13
// 14
static const int propertiesImageIndex = 15;
// 16
static const int searchImageIndex = 17;
// 18
// 19
// 20
// 21
static const int viewsImageIndex = 22;
// 23
// 24
// 25
// 26
// 27
static const int upImageIndex = 28;
static const int mapDriveImageIndex = 29;
static const int disconnectImageIndex = 30;
// 31
static const int viewsAltImageIndex = 32;       // same image as viewsImageIndex
// 33
// 34
// 35
// 36
// 37
static const int viewsAlt2ImageIndex = 38;      // same image as viewsAltImageIndex & viewsImageIndex
// 39
// 40
// 41
// 42
static const int foldersImageIndex = 43;
static const int moveToImageIndex = 44;
static const int copyToImageIndex = 45;
static const int folderOptionsImageIndex = 46;

enum StandardToolbarButtons {
    BtnIdx_Back = 0,
    BtnIdx_Forward,
    BtnIdx_Up,
    BtnIdx_Search,
    BtnIdx_Folders,
    BtnIdx_MoveTo,
    BtnIdx_CopyTo,
    BtnIdx_Delete,
    BtnIdx_Undo,
    BtnIdx_Views,
    BtnIdx_Stop,
    BtnIdx_Refresh,
    BtnIdx_Home,
    BtnIdx_MapDrive,
    BtnIdx_Disconnect,
    BtnIdx_Favorites,
    BtnIdx_History,
    BtnIdx_FullScreen,
    BtnIdx_Properties,
    BtnIdx_Cut,
    BtnIdx_Copy,
    BtnIdx_Paste,
    BtnIdx_FolderOptions,
};

const int numShownButtons = 13;
const int numHiddenButtons = 13;
TBBUTTON tbButtonsAdd[numShownButtons + numHiddenButtons] =
{
    { backImageIndex, IDM_GOTO_BACK, TBSTATE_ENABLED, BTNS_DROPDOWN | BTNS_SHOWTEXT, { 0 }, 0, BtnIdx_Back },
    { forwardImageIndex, IDM_GOTO_FORWARD, TBSTATE_ENABLED, BTNS_DROPDOWN, { 0 }, 0,          BtnIdx_Forward },
    { upImageIndex, IDM_GOTO_UPONELEVEL, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0,                 BtnIdx_Up },
    { 6, -1, TBSTATE_ENABLED, BTNS_SEP },
    { searchImageIndex, gSearchCommandID, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_SHOWTEXT, { 0 }, 0, BtnIdx_Search },
    { foldersImageIndex, gFoldersCommandID, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_SHOWTEXT, { 0 }, 0, BtnIdx_Folders },
    { 6, -1, TBSTATE_ENABLED, BTNS_SEP },
    { moveToImageIndex, gMoveToCommandID, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_MoveTo },
    { copyToImageIndex, gCopyToCommandID, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_CopyTo },
    { deleteImageIndex, gDeleteCommandID, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_Delete },
    { undoImageIndex, gUndoCommandID, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_Undo },
    { 6, -1, TBSTATE_ENABLED, BTNS_SEP },
    { viewsImageIndex, gViewsCommandID, TBSTATE_ENABLED, BTNS_WHOLEDROPDOWN, { 0 }, 0, BtnIdx_Views },

    { 0, gStopCommandID, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_Stop },
    { 0, IDM_VIEW_REFRESH, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_Refresh },
    { 0, gHomeCommandID, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_Home },
    { mapDriveImageIndex, IDM_TOOLS_MAPNETWORKDRIVE, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_MapDrive },
    { disconnectImageIndex, IDM_TOOLS_DISCONNECTNETWORKDRIVE, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_Disconnect },
    { favoritesImageIndex, gFavoritesCommandID, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_SHOWTEXT, { 0 }, 0, BtnIdx_Favorites },
    { 0, gHistoryCommandID, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_History },
    { 0, gFullScreenCommandID, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_FullScreen },
    { propertiesImageIndex, gPropertiesCommandID, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_Properties },
    { cutImageIndex, gCutCommandID, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_Cut },
    { copyImageIndex, gCopyCommandID, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_Copy },
    { pasteImageIndex, gPasteCommandID, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_Paste },
    { folderOptionsImageIndex, IDM_TOOLS_FOLDEROPTIONS, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0, BtnIdx_FolderOptions },
};

HRESULT STDMETHODCALLTYPE CToolsBand::SetSite(IUnknown* pUnkSite){
    HWND                    parentWindow;
    HWND                    toolbar;
    HRESULT                 hResult;

    if(fDockSite) fDockSite.Release();

    if (pUnkSite == NULL)
        return S_OK;
    hResult = pUnkSite->QueryInterface(IID_PPV_ARG(IDockingWindowSite, &fDockSite));
    if (FAILED_UNEXPECTEDLY(hResult))
        return hResult;
    parentWindow = NULL;
    hResult = IUnknown_GetWindow(pUnkSite, &parentWindow);
    if (FAILED(hResult) || !::IsWindow(parentWindow))
        return E_FAIL;

    toolbar = CreateWindowEx(
                    TBSTYLE_EX_DOUBLEBUFFER,
                    TOOLBARCLASSNAMEW, NULL,
                    WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
                    TBSTYLE_TOOLTIPS | TBSTYLE_TRANSPARENT | TBSTYLE_REGISTERDROP | TBSTYLE_LIST | TBSTYLE_FLAT |
                    CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | CCS_TOP,
                    0, 0, 500, 20, parentWindow, NULL, _AtlBaseModule.GetModuleInstance(), 0);
    if (toolbar == NULL)
        return E_FAIL;
    SubclassWindow(toolbar);
    SendMessage(TB_ADDSTRINGW, (WPARAM) GetModuleHandle(L"browseui.dll"), IDS_STANDARD_TOOLBAR);

    SendMessage(WM_USER + 100, GetSystemMetrics(SM_CXEDGE) / 2, 0);
    SendMessage(TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
    SendMessage(TB_SETMAXTEXTROWS, 1, 0);
    SendMessage(TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_HIDECLIPPEDBUTTONS | TBSTYLE_EX_MIXEDBUTTONS | TBSTYLE_EX_DRAWDDARROWS);

    HINSTANCE shell32Instance = GetModuleHandle(_T("shell32.dll"));
    HBITMAP imgNormal = reinterpret_cast<HBITMAP>(
        LoadImage(shell32Instance, MAKEINTRESOURCE(214),
            IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_CREATEDIBSECTION));

    HBITMAP imgHot = reinterpret_cast<HBITMAP>(
        LoadImage(shell32Instance, MAKEINTRESOURCE(215),
        IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_CREATEDIBSECTION));

    if (imgNormal && imgHot)
    {
        BITMAP bitmapInfo;
        GetObjectW(imgNormal, sizeof(bitmapInfo), &bitmapInfo);
        HIMAGELIST himlNormal = ImageList_Create(bitmapInfo.bmHeight, bitmapInfo.bmHeight, ILC_COLOR32, 4, 4);
        ImageList_Add(himlNormal, imgNormal, NULL);

        GetObjectW(imgHot, sizeof(bitmapInfo), &bitmapInfo);
        HIMAGELIST himlHot = ImageList_Create(bitmapInfo.bmHeight, bitmapInfo.bmHeight, ILC_COLOR32, 4, 4);
        ImageList_Add(himlHot, imgHot, NULL);

        SendMessage(TB_SETIMAGELIST, 0, (LPARAM) himlNormal);
        SendMessage(TB_SETHOTIMAGELIST, 0, (LPARAM) himlHot);
    }

    SendMessage(TB_ADDBUTTONSW, numShownButtons, (LPARAM)&tbButtonsAdd);

    if (imgNormal)
        DeleteObject(imgNormal);
    if (imgHot)
        DeleteObject(imgHot);
    
    return hResult;
}

HRESULT STDMETHODCALLTYPE CToolsBand::GetSite(REFIID riid, void **ppvSite)
{
    if (fDockSite == NULL)
        return E_FAIL;
    return fDockSite->QueryInterface(riid, ppvSite);
}

HRESULT STDMETHODCALLTYPE CToolsBand::GetWindow(HWND *lphwnd)
{
    if (lphwnd == NULL)
        return E_POINTER;
    *lphwnd = m_hWnd;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE CToolsBand::ContextSensitiveHelp(BOOL fEnterMode)
{

    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CToolsBand::CloseDW(DWORD dwReserved)
{
    ShowDW(FALSE);

    if (IsWindow())
        DestroyWindow();

    m_hWnd = NULL;

    if (fDockSite) fDockSite.Release();

    return S_OK;
}

HRESULT STDMETHODCALLTYPE CToolsBand::ResizeBorderDW(const RECT* prcBorder, IUnknown* punkToolbarSite, BOOL fReserved)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CToolsBand::ShowDW(BOOL fShow)
{
    if (m_hWnd)
    {
        if (fShow)
            ShowWindow(SW_SHOW);
        else
            ShowWindow(SW_HIDE);
    }
    return S_OK;
}

HRESULT STDMETHODCALLTYPE CToolsBand::HasFocusIO()
{
    return S_FALSE;
}

HRESULT STDMETHODCALLTYPE CToolsBand::TranslateAcceleratorIO(LPMSG lpMsg)
{
    return S_FALSE;
}

HRESULT STDMETHODCALLTYPE CToolsBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CToolsBand::GetClassID(CLSID *pClassID)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CToolsBand::IsDirty()
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CToolsBand::Load(IStream *pStm)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CToolsBand::Save(IStream *pStm, BOOL fClearDirty)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CToolsBand::GetSizeMax(ULARGE_INTEGER *pcbSize)
{
    return E_NOTIMPL;
}

LRESULT CToolsBand::OnGetButtonInfo(UINT idControl, NMHDR *pNMHDR, BOOL &bHandled)
{
    TBNOTIFYW *pTBntf = reinterpret_cast<TBNOTIFYW *>(pNMHDR);

    if (pTBntf->iItem >= 0 && pTBntf->iItem < (numShownButtons + numHiddenButtons))
    {
        pTBntf->tbButton = tbButtonsAdd[pTBntf->iItem];
        return TRUE;
    }
    else
        return FALSE;
    return 0;
}

HRESULT CreateToolsBar(REFIID riid, void **ppv)
{
    return ShellObjectCreator<CToolsBand>(riid, ppv);
}

