#include "priv.h"
#include "apithk.h"
#include "mshtmhst.h"
#include "basebar.h"

#ifdef MAINWIN
#include <mainwin.h>
EXTERN_C MwPaintSpecialEOBorder( HWND hWnd, HDC hDC );
#endif

#define DBM_ONPOSRECTCHANGE  (WM_USER)


//*** CBaseBar::IDeskBar::* {
//


/*----------------------------------------------------------
Purpose: IDeskBar::SetClient

         Usually the function that composes a bar/bandsite/band
         union is responsible for calling this method to inform
         the bar what the client (bandsite) is.

*/
HRESULT CBaseBar::SetClient(IUnknown *punkChild)
{
    if (_punkChild != NULL)
    {
        // 4, 3, 2, 1 Release
        _hwndChild = NULL;

        if (_pDBC)
        {
            // This must happen first, before _pWEH becomes NULL so cleanup
            // notifications can still go thru
            _pDBC->SetDeskBarSite(NULL);
        }

        ATOMICRELEASE(_pDBC);

        ATOMICRELEASE(_pWEH);

        ATOMICRELEASE(_punkChild);
    }

    _punkChild = punkChild;

    HRESULT hr = S_OK;
    if (_punkChild != NULL)
    {
        // 1, 2, 3, 4 QI/AddRef/etc.
        _punkChild->AddRef();
        if (!_hwnd)
        {
            _RegisterDeskBarClass();
            _CreateDeskBarWindow();
            if (!_hwnd)
            {
                return E_OUTOFMEMORY;
            }

            // can't do CBaseBar::_Initialize yet (haven't done SetSite yet)
        }

        hr = _punkChild->QueryInterface(IID_PPV_ARG(IWinEventHandler, &_pWEH));
        if (SUCCEEDED(hr))
        {
            hr = _punkChild->QueryInterface(IID_PPV_ARG(IDeskBarClient, &_pDBC));
            if (SUCCEEDED(hr))
            {
                // nothing to cache yet due to lazy CreateWindow
                hr = _pDBC->SetDeskBarSite(SAFECAST(this, IDeskBar*));

                IUnknown_GetWindow(_punkChild, &_hwndChild);
            }
        }
    }

    return hr;
}

HRESULT CBaseBar::GetClient(IUnknown **ppunk)
{
    *ppunk = _punkChild;
    if (_punkChild)
        _punkChild->AddRef();
    return _punkChild ? S_OK : E_FAIL;
}

HRESULT CBaseBar::OnPosRectChangeDB(LPRECT prc)
{
    _szChild.cx = RECTWIDTH(*prc);
    _szChild.cy = RECTHEIGHT(*prc);

    // We can't change our size right away because we haven't returned from processing
    // this WM_SIZE message. If we resize right now, USER gets confused...
    //
    // We cannot use PeekMessage to determine if there is already a pending
    // DBM_ONPOSRECTCHANGE because that allows incoming SendMessage's to
    // arrive, and then we can get into a bad recursive situation when there
    // are a lot of SHChangeNotify's arriving in rapid succession.
    //
    if (!_fPosRectChangePending)
    {
        _fPosRectChangePending = TRUE;
        PostMessage(_hwnd, DBM_ONPOSRECTCHANGE, 0, 0);
    }

    return S_OK;
}

//  Derived classes are expected to implement this method and do something
//  interesting...
void CBaseBar::_OnPostedPosRectChange()
{
}

// }

HRESULT CBaseBar::ShowDW(BOOL fShow)
{
    fShow = BOOLIFY(fShow);

    if (BOOLIFY(_fShow) == fShow)
        return S_OK;

    _fShow = fShow;

    if (_pDBC)
        return _pDBC->UIActivateDBC(fShow ? DBC_SHOW : DBC_HIDE);
    else
        return E_UNEXPECTED;
}

void CBaseBar::_OnCreate()
{
    SendMessage(_hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, 0), 0);
}

LRESULT CBaseBar::_OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LRESULT lres = 0;

    _CheckForwardWinEvent(uMsg, wParam, lParam, &lres);

    return lres;
}


/***
 */
LRESULT CBaseBar::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LRESULT lres = 0;

    switch (uMsg) {
    case WM_CREATE:
        _OnCreate();
        break;

    case WM_COMMAND:
        return _OnCommand(uMsg, wParam, lParam);
        

    case WM_SIZE:     
        _OnSize();    
        break;

    case WM_NOTIFY:
        return _OnNotify(uMsg, wParam, lParam);

#if 0
    // we'd like to set focus to the 1st band when somebody clicks in
    // 'dead space' on the bar (i.e. make it look like they TABed in).
    // however for some reason the below code has the bad effect of
    // de-selecting text in (e.g.) the addr edit control (it's as if
    // the control thinks we've clicked there 2x rather than 1x).
    case WM_SETFOCUS:
        if (IUnknown_HasFocusIO(_pDBC) == S_FALSE)
            IUnknown_UIActivateIO(_pDBC, TRUE, NULL);
        break;
#endif
        
    case WM_SYSCOLORCHANGE:
    case WM_WININICHANGE:
    case WM_CONTEXTMENU:
    case WM_INITMENUPOPUP:
    case WM_MEASUREITEM:
    case WM_DRAWITEM:
    case WM_MENUCHAR:
    case WM_PALETTECHANGED:
        _CheckForwardWinEvent(uMsg, wParam, lParam, &lres);
        break;

    case DBM_ONPOSRECTCHANGE:
        _fPosRectChangePending = FALSE;
        _OnPostedPosRectChange();
        break;

#ifdef MAINWIN
    case WM_NCPAINTSPECIALFRAME:
        // In  case  of  motif look  the  MwPaintBorder paints a Etched In
        // border if WM_NCPAINTSPECIALFRAME returns FALSE. We are handling
        // this message here and drawing the Etched Out frame explicitly.
        // wParam - HDC
        if (MwCurrentLook() == LOOK_MOTIF)
        {
            MwPaintSpecialEOBorder( hwnd, (HDC)wParam );
            return TRUE;
        }
#endif

    default:
        return DefWindowProcWrap(hwnd, uMsg, wParam, lParam);
    }

    return lres;
}

/***
 */
CBaseBar::CBaseBar() : _cRef(1)
{
    DllAddRef();
}

/***
 */
CBaseBar::~CBaseBar()
{
    // see Release, where we call virtuals (which can't be called from dtor)
    DllRelease();
}

/***
 */
void CBaseBar::_RegisterDeskBarClass()
{
    WNDCLASS  wc = {0};
    wc.style            = _GetClassStyle();
    wc.lpfnWndProc      = s_WndProc;
    //wc.cbClsExtra       = 0;
    wc.cbWndExtra       = SIZEOF(CBaseBar*);
    wc.hInstance        = HINST_THISDLL;
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground    = (HBRUSH) (COLOR_3DFACE+1);
    //wc.lpszMenuName     =  NULL;
    wc.lpszClassName    = TEXT("BaseBar");
    //wc.hIcon            = NULL;

    SHRegisterClass(&wc);
}

DWORD CBaseBar::_GetExStyle()
{
    return WS_EX_TOOLWINDOW;
}

DWORD CBaseBar::_GetClassStyle()
{
    return CS_HREDRAW | CS_VREDRAW;
}

void CBaseBar::_CreateDeskBarWindow()
{
#ifndef UNIX
    // _hwnd is set in s_WndProc
    DWORD dwExStyle = _GetExStyle();    
    dwExStyle |= IS_BIDI_LOCALIZED_SYSTEM() ? dwExStyleRTLMirrorWnd : 0L;
    HWND hwndDummy = CreateWindowEx(
                                    dwExStyle,
                                    TEXT("BaseBar"), NULL,
                                    _hwndSite ? WS_CHILD | WS_CLIPCHILDREN : WS_POPUP | WS_CLIPCHILDREN,
                                    0,0,100,100,
                                    _hwndSite, NULL, HINST_THISDLL,
                                    (LPVOID)SAFECAST(this, CImpWndProc*));
#else
    // This change removes a flash at the corner of the
    // screen. We create a small 1,1 window.

    HWND hwndDummy = CreateWindowEx(
                                    _GetExStyle(),
                                    TEXT("BaseBar"), NULL,
                                    _hwndSite ? WS_CHILD | WS_CLIPCHILDREN : WS_POPUP | WS_CLIPCHILDREN,
                                    -100,-100,1,1,
                                    _hwndSite, NULL, HINST_THISDLL,
                                    (LPVOID)SAFECAST(this, CImpWndProc*));
#endif
}


void CBaseBar::_OnSize(void)
{
    RECT rc;

    if (!_hwndChild)
        return;

    GetClientRect(_hwnd, &rc);
    SetWindowPos(_hwndChild, 0,
            rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc),
            SWP_NOACTIVATE|SWP_NOZORDER);
}

void CBaseBar::_NotifyModeChange(DWORD dwMode)
{
    if (_pDBC) {
        _dwMode = dwMode;
        // FEATURE: should we add an STBBIF_VIEWMODE_FLOAT?
        _pDBC->SetModeDBC(_dwMode);
    }
}

BOOL CBaseBar::_CheckForwardWinEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plres)
{
    HWND hwnd = NULL;

    *plres = 0;
    switch (uMsg)
    {
    case WM_CONTEXTMENU:
    case WM_INITMENUPOPUP:
    case WM_MEASUREITEM:
    case WM_DRAWITEM:
    case WM_MENUCHAR:
        hwnd = _hwndChild;
        break;

    case WM_NOTIFY:
        hwnd = ((LPNMHDR)lParam)->hwndFrom;
        break;
        
    case WM_COMMAND:
        hwnd = GET_WM_COMMAND_HWND(wParam, lParam);
        break;
        
    case WM_SYSCOLORCHANGE:
    case WM_WININICHANGE:
    case WM_PALETTECHANGED:
        hwnd = _hwndChild;
        break;
    }
    
    if (hwnd && _pWEH && _pWEH->IsWindowOwner(hwnd) == S_OK)
    {
        _pWEH->OnWinEvent(_hwnd, uMsg, wParam, lParam, plres);
        return TRUE;
    }
    return FALSE;
}

/***
 */
LRESULT CBaseBar::_OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LRESULT lres = 0;

    _CheckForwardWinEvent(uMsg, wParam, lParam, &lres);

    return lres;
}

HRESULT CBaseBar::CloseDW(DWORD dwReserved)
{
    SetClient(NULL);
    if (_hwnd) {
        DestroyWindow(_hwnd);
        _hwnd = NULL;
    }
    return S_OK;
}


HRESULT CBaseBar::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
    static const QITAB qit[] = {
        QITABENT(CBaseBar, IOleWindow),
        QITABENT(CBaseBar, IDeskBar),
        QITABENT(CBaseBar, IInputObject),
        QITABENT(CBaseBar, IInputObjectSite),
        QITABENT(CBaseBar, IServiceProvider),
        QITABENT(CBaseBar, IOleCommandTarget),
        { 0 },
    };

    return QISearch(this, (LPCQITAB)qit, riid, ppvObj);
}


ULONG CBaseBar::AddRef()
{
    _cRef++;
    return _cRef;
}

ULONG CBaseBar::Release()
{
    ASSERT(_cRef > 0);
    _cRef--;

    if (_cRef > 0)
        return _cRef;

    // 'virtual dtor'
    // gotta do virtual stuff here (not in dtor) because can't call
    // any virtuals in the dtor
    // CBaseBar::Destroy() {
    CloseDW(0);
    // }

    delete this;
    return 0;
}

//*** CBaseBar::IOleWindow::* {
//

HRESULT CBaseBar::GetWindow(HWND * lphwnd)
{
    *lphwnd = _hwnd;
    return (_hwnd) ? S_OK : E_FAIL;
}

HRESULT CBaseBar::ContextSensitiveHelp(BOOL fEnterMode)
{
    // FEATURE: Visit here later.
    return E_NOTIMPL;
}
// }


// }
// some helpers... {

// What's the point of having
// these empty implementations in the base class?
//

//*** CBaseBar::IServiceProvider::*
//
HRESULT CBaseBar::QueryService(REFGUID guidService,
                                REFIID riid, void **ppvObj)
{
    HRESULT hres = E_FAIL;
    *ppvObj = NULL;

    return hres;
}

//*** CBaseBar::IOleCommandTarget::*
//
HRESULT CBaseBar::QueryStatus(const GUID *pguidCmdGroup,
    ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
{
    return MayQSForward(_pDBC, OCTD_DOWN, pguidCmdGroup, cCmds, rgCmds, pcmdtext);
}

HRESULT CBaseBar::Exec(const GUID *pguidCmdGroup,
    DWORD nCmdID, DWORD nCmdexecopt,
    VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
    return MayExecForward(_pDBC, OCTD_DOWN, pguidCmdGroup, nCmdID, nCmdexecopt,
        pvarargIn, pvarargOut);
}

// }


//*** CDeskBar::IInputObject::* {

HRESULT CBaseBar::HasFocusIO()
{
    HRESULT hres;

    hres = IUnknown_HasFocusIO(_pDBC);
    return hres;
}

HRESULT CBaseBar::TranslateAcceleratorIO(LPMSG lpMsg)
{
    HRESULT hres;

    hres = IUnknown_TranslateAcceleratorIO(_pDBC, lpMsg);
    return hres;
}

HRESULT CBaseBar::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
{
    HRESULT hres;

    hres = IUnknown_UIActivateIO(_pDBC, fActivate, lpMsg);
    return hres;
}

// }

//***   CDeskBar::IInputObjectSite::* {

HRESULT CBaseBar::OnFocusChangeIS(IUnknown *punk, BOOL fSetFocus)
{
    return NOERROR;
}

// }

