//
// This Channel OC code was copied from browseui.  Once it is reenabled in
// browseui it can be removed from shdocvw.
//

#include "priv.h"
#include "cobjsafe.h"


STDAPI ChannelOC_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi);
HRESULT IUnknown_SetBandInfoSFB(IUnknown *punkBand, BANDINFOSFB *pbi);
IDeskBand * ChannelBand_Create(LPCITEMIDLIST pidlDefault);
//LPITEMIDLIST Channel_GetFolderPidl();
void Channels_SetBandInfoSFB(IUnknown* punkBand);

//////////////////////////////////////////////////
//
// ChannelOC
//
// This is an OC that wraps the above band for
// inclusion in the Active Desktop.
//
// TODO:
//  - listen to ISFBand for resizes and request them
//    from our container
//
// Do we need to register as a drop target?

#undef  SUPERCLASS
#define SUPERCLASS CShellEmbedding
#undef  THISCLASS
#define THISCLASS ChannelOC

class ChannelOC : public SUPERCLASS
    , public IServiceProvider
    , public IPersistPropertyBag
    , public CObjectSafety
{
public:
    // *** IUnknown ***
    virtual STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj) { return SUPERCLASS::QueryInterface(riid, ppvObj); };
    virtual STDMETHODIMP_(ULONG) AddRef(void) { return SUPERCLASS::AddRef(); }
    virtual STDMETHODIMP_(ULONG) Release(void) { return SUPERCLASS::Release(); }

    // *** CAggregatedUnknown ***
    virtual HRESULT v_InternalQueryInterface(REFIID riid, LPVOID * ppvObj);

    // *** IServiceProvider ***
    virtual STDMETHODIMP QueryService(REFGUID guidService,
        REFIID riid, void **ppvObj);

    // *** IPersistPropertyBag ***
    virtual STDMETHODIMP Load(IPropertyBag *pPropBag, IErrorLog *pErrorLog);
    virtual STDMETHODIMP Save(IPropertyBag *pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties);
    virtual STDMETHODIMP InitNew();
    virtual STDMETHODIMP GetClassID(CLSID *pClassID) { return SUPERCLASS::GetClassID(pClassID); };

    // *** IOleInPlaceObject ***
    virtual STDMETHODIMP SetObjectRects(LPCRECT lprcPosRect, LPCRECT lprcClipRect);

protected:
    ChannelOC(IUnknown* punkOuter, LPCOBJECTINFO poi);
    ~ChannelOC();
    friend HRESULT ChannelOC_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi);

    virtual LRESULT v_WndProc(HWND, UINT, WPARAM, LPARAM);

    LPITEMIDLIST _GetInitialPidl(void);
    virtual void _OnInPlaceActivate(void);      // called when we actually go in-place-active
    virtual void _OnInPlaceDeactivate(void);    // called when we actually deactivate

    IDeskBand*          _pBand;
    IWinEventHandler*   _pweh;
    COLORREF            _crBkgndOC;
    COLORREF            _crBorder;

    // for GetBandInfo, not currently used
    DESKBANDINFO _dbi;
};

ChannelOC::ChannelOC(IUnknown* punkOuter, LPCOBJECTINFO poi) : 
    CShellEmbedding(punkOuter, poi, NULL)
{
    TraceMsg(TF_SHDLIFE, "ctor ChannelOC %x", this);
    _crBkgndOC = CLR_DEFAULT;
    _crBorder = CLR_DEFAULT;
}


ChannelOC::~ChannelOC() 
{
    TraceMsg(TF_SHDLIFE, "dtor ChannelOC %x", this);
}

HRESULT ChannelOC::v_InternalQueryInterface(REFIID riid, void **ppvObj)
{
    static const QITAB qit[] = {
        QITABENT(THISCLASS, IServiceProvider),
        QITABENT(THISCLASS, IPersistPropertyBag),
        QITABENT(THISCLASS, IObjectSafety),
        { 0 },
    };

    HRESULT hres = QISearch(this, qit, riid, ppvObj);

    if (FAILED(hres))
        hres = SUPERCLASS::v_InternalQueryInterface(riid, ppvObj);

    return hres;
}

HRESULT ChannelOC::QueryService(REFGUID guidService, REFIID riid, void **ppvObj)
{
    return IUnknown_QueryService(_pcli, guidService, riid, ppvObj);
}

//***   ChannelOC::IPersistPropertyBag::* {
//
HRESULT THISCLASS::Load(IPropertyBag *pPropBag, IErrorLog *pErrorLog)
{
    ASSERT(_crBkgndOC == CLR_DEFAULT);  // paranoia
    _crBkgndOC = PropBag_ReadInt4(pPropBag, L"BGColor", CLR_DEFAULT);
    TraceMsg(TF_WARNING, "coc.l: BGColor=%x", _crBkgndOC);
    
    _crBorder = PropBag_ReadInt4(pPropBag, L"BorderColor", CLR_DEFAULT);
    
    return S_OK;
}

HRESULT THISCLASS::Save(IPropertyBag *pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties)
{
    ASSERT(0);
    return E_NOTIMPL;
}

HRESULT THISCLASS::InitNew()
{
    ASSERT(_crBkgndOC == CLR_DEFAULT);
    ASSERT(_crBorder == CLR_DEFAULT);
    return E_NOTIMPL;
}

// }

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

    switch(uMsg)
    {
    case WM_CONTEXTMENU:
        return 1;

    case WM_WINDOWPOSCHANGED:
        if (_hwndChild)
        {
            LPWINDOWPOS lpwp = (LPWINDOWPOS)lParam;
    
            if (!(lpwp->flags & SWP_NOSIZE))
            {
                SetWindowPos(_hwndChild, NULL,
                    0,0,
                    lpwp->cx, lpwp->cy,
                    SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE|
                    (lpwp->flags&(SWP_NOREDRAW|SWP_NOCOPYBITS)));
            }
    
        }
        return 0;

    case WM_ERASEBKGND:
        if ( _crBorder == CLR_DEFAULT )
        {
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
        else
        {
            HDC hdc = (HDC) wParam;
            RECT rcClient;
            GetClientRect( hwnd, &rcClient );
            COLORREF crSave = SetBkColor(hdc, _crBorder);
            
            ExtTextOut(hdc,0,0,ETO_OPAQUE,&rcClient,NULL,0,NULL);
            SetBkColor(hdc, crSave);
        }

    case WM_COMMAND:
    case WM_NOTIFY:
        if (_pweh)
        {
            LRESULT lres;
            if (SUCCEEDED(_pweh->OnWinEvent(hwnd, uMsg, wParam, lParam, &lres)))
                return lres;
        }
        // fall through

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


LPITEMIDLIST ChannelOC::_GetInitialPidl()
{
    LPITEMIDLIST pidl = NULL;

    // Figure out what directory this ChannelOC is looking at.
    // If we're looking at a specific channel category, use it,
    // otherwise use the top-level Channels folder.
    //
    if (EVAL(_pcli))
    {
        IOleContainer *pContainer;

        if (SUCCEEDED(_pcli->GetContainer(&pContainer)))
        {
            IHTMLDocument2 *pDoc;

            if (SUCCEEDED(pContainer->QueryInterface(IID_IHTMLDocument2, (LPVOID*)&pDoc)))
            {
                IHTMLLocation *pLocation;

                if (SUCCEEDED(pDoc->get_location(&pLocation)))
                {
                    BSTR bstrURL;

                    if (SUCCEEDED(pLocation->get_href(&bstrURL)) && bstrURL)
                    {
                        TCHAR szPath[MAX_PATH];
                        DWORD cchT = ARRAYSIZE(szPath);
                        if (SUCCEEDED(PathCreateFromUrl(bstrURL, szPath, &cchT, 0)))
                        {
                            PathRemoveFileSpec(szPath);

                            // if we're not under the channels folder, then
                            // ignore this location
                            //
                            if (SUCCEEDED(IECreateFromPath(szPath, &pidl)))
                            {
                                LPITEMIDLIST pidlChannels = Channel_GetFolderPidl();

                                if (!pidlChannels || !ILIsParent(pidlChannels, pidl, FALSE))
                                {
                                    ILFree(pidl);
                                    pidl = NULL;
                                }

                                ILFree(pidlChannels);
                            }
                            TraceMsg(TF_BAND, "ChannelOC::_OnInPlaceActivate [%s] (%x)", szPath, pidl);
                        }

                        SysFreeString(bstrURL);
                    }

                    pLocation->Release();
                }

                pDoc->Release();
            }

            pContainer->Release();
        }
    }

    return pidl;
}

void ChannelOC::_OnInPlaceActivate()
{
    SUPERCLASS::_OnInPlaceActivate();

    // we should never get called twice, but might as well be safe
    //
    if (EVAL(!_pBand))
    {
        LPITEMIDLIST pidl = _GetInitialPidl();

        // Now create the band and initialize it properly
        //
        _pBand = ChannelBand_Create(pidl);
        if (_pBand)
        {
            IDropTarget* pdt;

            Channels_SetBandInfoSFB(_pBand);
            _pBand->QueryInterface(IID_IWinEventHandler, (LPVOID*)&_pweh);
        
            IUnknown_SetSite(_pBand, SAFECAST(this, IOleObject*));

            // now that band is sited and init'ed, we can override defaults
            if (_crBkgndOC != CLR_DEFAULT) {
                BANDINFOSFB bi;

                TraceMsg(TF_WARNING, "coc.oipa: BGColor=%x _pBand=%x", _crBkgndOC, _pBand);
                bi.dwMask = ISFB_MASK_BKCOLOR;
                bi.crBkgnd = _crBkgndOC;
                IUnknown_SetBandInfoSFB(_pBand, &bi);
            }

            _dbi.dwMask = DBIM_MINSIZE|DBIM_MAXSIZE|DBIM_INTEGRAL|DBIM_ACTUAL|DBIM_TITLE|DBIM_MODEFLAGS|DBIM_BKCOLOR;
            _pBand->GetBandInfo(0, DBIF_VIEWMODE_VERTICAL, &_dbi);

            _pBand->GetWindow(&_hwndChild);

            SetWindowPos(_hwndChild, NULL, 0, 0,
                         _rcPos.right - _rcPos.left,
                         _rcPos.bottom - _rcPos.top,
                         SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_SHOWWINDOW);

            _pBand->ShowDW(TRUE);

            // Register the band as a drop target
            if (SUCCEEDED(_pBand->QueryInterface(IID_IDropTarget, (LPVOID*)&pdt)))
            {
                THR(RegisterDragDrop(_hwnd, pdt));
                pdt->Release();
            }

            TraceMsg(TF_BAND, "ISFBandOC::_OnInPlaceActivate()d to cx=%d cy=%d", _size.cx, _size.cy);
        }

        ILFree(pidl);
    }
}

void ChannelOC::_OnInPlaceDeactivate()
{
    IDeskBand * pBand = _pBand;

    // set to NULL to avoid re-entrancy...
    _pBand = NULL;
    
    if (pBand)
    {
        _hwndChild = NULL;

        RevokeDragDrop(_hwnd);

        pBand->ShowDW(FALSE);
        IUnknown_SetSite(pBand, NULL);
        pBand->CloseDW(0);

        TraceMsg(TF_BAND, "ISFBandOC::_OnInPlaceDeactivate()d");
    }

    // we need to keep _pweh because we need the notifies during destruction to 
    // free everything properly
    // the _pBand = NULL above is sufficient to keep from reentrancy
    ATOMICRELEASE(_pweh);
    ATOMICRELEASE(pBand);

    SUPERCLASS::_OnInPlaceDeactivate();
}

HRESULT ChannelOC::SetObjectRects(LPCRECT lprcPosRect, LPCRECT lprcClipRect)
{
    HRESULT hres = SUPERCLASS::SetObjectRects(lprcPosRect, lprcClipRect);

    if (_hwndChild)
    {
        SetWindowPos(_hwndChild, NULL, 0,0,
            _rcPos.right - _rcPos.left,
            _rcPos.bottom - _rcPos.top, SWP_NOZORDER);
    }

    return hres;
}

STDAPI ChannelOC_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
{
    HRESULT hres = E_OUTOFMEMORY;

    ChannelOC* pBand = new ChannelOC(punkOuter, poi);
    if (pBand)
    {
        *ppunk = pBand->_GetInner();
        hres = S_OK;
    }

    return hres;
}

