// Copyright (c) 2002
// Sergey Klimov (kidd@ukr.net)
// WTL Docking windows
//
// This code is provided "as is", with absolutely no warranty expressed
// or implied. Any use is at your own risk.
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name is included. If
// the source code in  this file is used in any commercial application
// then a simple email would be nice.

#ifndef WTL_DW_WNDFRMPKG_H_INCLUDED_
#define WTL_DW_WNDFRMPKG_H_INCLUDED_

#pragma once

#ifndef __ATLMISC_H__
#error WndFrmPkg.h requires atlmisc.h to be included first
#endif

#include <memory>

#ifdef USE_BOOST
#include <boost/smart_ptr.hpp>
#endif

#include "ssec.h"
#include "DDTracker.h"

namespace dockwins
{

class CWndFrame
{
public:
    typedef long position;
    typedef long distance;

    class CCmp
    {
    public:
        CCmp(HWND hWnd)    : m_hWnd(hWnd)
        {
        }
        bool operator()(const CWndFrame& frame) const
        {
            return (frame.hwnd() == m_hWnd);
        }
    protected:
        HWND m_hWnd;
    };

    CWndFrame(position pos = 0, HWND hWnd = NULL)
        : m_hWnd(hWnd), m_pos(pos)
    {
    }

    HWND hwnd() const
    {
        return m_hWnd;
    }

    operator position() const
    {
        return m_pos;
    }

    CWndFrame& operator += (position val)
    {
        m_pos += val;
        return *this;
    }
    CWndFrame& operator -= (position val)
    {
        m_pos -= val;
        return *this;
    }

    CWndFrame& operator = (position pos)
    {
        m_pos = pos;
        return *this;
    }

    HDWP DeferFramePos(HDWP hdwp, long x1, long y1, long x2, long y2) const
    {
        return ::DeferWindowPos(hdwp, hwnd(),
                                NULL,
                                x1, y1,
                                x2 - x1, y2 - y1,
                                SWP_NOZORDER | SWP_NOACTIVATE);
    }
    void GetMinMaxInfo(LPMINMAXINFO pMinMaxInfo) const
    {
        ::SendMessage(m_hWnd, WM_GETMINMAXINFO, NULL, reinterpret_cast<LPARAM>(pMinMaxInfo));
    }
    distance MinDistance() const
    {
        DFMHDR dockHdr;
        dockHdr.code = DC_GETMINDIST;
        dockHdr.hWnd = m_hWnd;
        dockHdr.hBar =::GetParent(m_hWnd);
        ATLASSERT(::IsWindow(dockHdr.hBar));
        return static_cast<distance>
               (::SendMessage(dockHdr.hBar, WMDF_DOCK, NULL, reinterpret_cast<LPARAM>(&dockHdr)));
    }
protected:
    position m_pos;
    mutable HWND m_hWnd;
};

//template<class T,const T::distance TMinDist=0>
//struct CWndFrameTraits : ssec::spraits<T, T::position, T::distance/*,TMinDist*/>
//{
//    typedef ssec::spraits<T,position,position/*,TMinDist*/> baseClass;
//    static distance min_distance(const T& x)
//    {
//        const distance dist=TMinDist;
//        return dist+x.MinDistance();
//    }
//
//};

template< class TFrame = CWndFrame , class TTraits = CDockingFrameTraits>
class CWndFramesPackageBase
{
    typedef TFrame CFrame;
    typedef TTraits CTraits;
    typedef typename CTraits::CSplitterBar            CSplitterBar;
    typedef CWndFramesPackageBase<CFrame, TTraits>    thisClass;
protected:
    typedef typename CFrame::position    position;

    template < class T,
#if _MSC_VER<1300
             const T::distance TMinDist = 0 >
#else
             class TTraits = CSimpleSplitterBarTraits>
#endif
    struct CWndFrameTraits
        : ssec::spraits < T, typename T::position, typename T::distance/*,TMinDist*/ >
    {
        typedef ssec::spraits < T, position, position/*,TMinDist*/ > baseClass;
        static distance min_distance(const T& x)
        {
            const distance dist = TTraits::GetWidth();
            return dist + x.MinDistance();
        }

    };
    typedef CWndFrameTraits<CFrame, typename CSplitterBar::Traits> CFrameTraits;
    typedef ssec::ssection<CFrame, CFrameTraits> CFrames;

    typedef typename CFrames::iterator                    iterator;
    typedef typename CFrames::reverse_iterator            reverse_iterator;
    typedef typename CFrames::const_iterator            const_iterator;
    typedef typename CFrames::const_reverse_iterator    const_reverse_iterator;

    typedef ssec::bounds_type<position> CBounds;
protected:
    struct CEmbeddedSplitterBar : public CSplitterBar
    {
        CEmbeddedSplitterBar(bool bHorizontal, position vertex, const CRect& rcClient)
            : CSplitterBar(bHorizontal)
        {
            if (IsHorizontal())
            {
                top = vertex;
                bottom = top + GetThickness();
                left = rcClient.left;
                right = rcClient.right;
            }
            else
            {
                left = vertex;
                right = left + GetThickness();
                top = rcClient.top;
                bottom = rcClient.bottom;
            }
        }
    };
    class CEmbeddedSplitterBarPainter
    {
    public:
        CEmbeddedSplitterBarPainter(CDC& dc, bool bHorizontal, const CRect& rc)
            : m_dc(dc), m_rc(rc), m_bHorizontal(bHorizontal)
        {
        }
        void operator()(const CFrame& ref) const
        {
            CEmbeddedSplitterBar splitter(m_bHorizontal, ref, m_rc);
            splitter.Draw(m_dc);
        }
    protected:
        const    CRect& m_rc;
        bool    m_bHorizontal;
        CDC&    m_dc;
    };

    class CSplitterMoveTrackerBase : public IDDTracker//CDDTrackerBaseT<CSizeTrackerBase>
    {
    protected:
        void SetPosition()
        {
            ATLASSERT(m_bounds.bind(m_pos) == m_pos);
            m_frames.set_position(m_i, m_pos);
            m_owner.Arrange(m_rc);
            m_wnd.RedrawWindow(&m_rc, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
        }
    public:
        CSplitterMoveTrackerBase(HWND hWnd, thisClass& owner, const CPoint& pt, const CRect& rc)
            : m_wnd(hWnd), m_owner(owner), m_frames(owner.m_frames), m_rc(rc)
        {
            position pos = owner.IsHorizontal() ? pt.x : pt.y;
            m_i = m_frames.locate(pos);

            if (m_i != m_frames.end())
            {
                m_pos = (*m_i);
                m_offset = pos - m_pos;
                m_frames.get_effective_bounds(m_i, m_bounds);
            }
        }
        operator iterator() const
        {
            return m_i;
        }
        void OnMove(long x, long y)
        {
            position pos = m_owner.IsHorizontal() ? x : y;
            pos = m_bounds.bind(pos - m_offset);

            if (pos != m_pos)
            {
                m_pos = pos;
                Move();
            }
        }
        virtual void Move() = 0;

    protected:
        thisClass&        m_owner;
        CFrames&        m_frames;
        iterator        m_i;
        CBounds            m_bounds;
        position        m_pos;
        position        m_offset;
        const CRect&    m_rc;
        CWindow            m_wnd;
    };

    friend class CSplitterMoveTrackerBase;

    class CSplitterMoveTrackerFull : public CSplitterMoveTrackerBase
    {
    public:
        CSplitterMoveTrackerFull(HWND hWnd, thisClass& owner, const CPoint& pt, const CRect& rc)
            : CSplitterMoveTrackerBase(hWnd, owner, pt, rc)
        {
        }
        void Move()
        {
            SetPosition();
        }
    };

    class CSplitterMoveTrackerGhost : public CSplitterMoveTrackerBase
    {
        typedef CEmbeddedSplitterBar CSplitterBar;
        typedef CSimpleSplitterBarSlider<CSplitterBar> CSlider;
    public:
        CSplitterMoveTrackerGhost(HWND hWnd, thisClass& owner,
                                  const CPoint& pt, const CRect& rc)
            : CSplitterMoveTrackerBase(hWnd, owner, pt, rc),
              m_dc(::GetWindowDC(NULL)), m_splitter(!owner.IsHorizontal(), m_pos, rc), m_slider(m_splitter)
        {
            CPoint point(rc.TopLeft());
            ::ClientToScreen(hWnd, &point);
            CSize offset(point.x - rc.left, point.y - rc.top);
            m_splitter += offset;
            m_ghOffset = owner.IsHorizontal()
                         ? offset.cx
                         : offset.cy;
        }
        void BeginDrag()
        {
            m_splitter.DrawGhostBar(m_dc);
        }
        void EndDrag(bool bCanceled)
        {
            m_splitter.CleanGhostBar(m_dc);

            if (!bCanceled)
                SetPosition();
        }
        void Move()
        {
            m_splitter.CleanGhostBar(m_dc);
            m_slider = m_pos + m_ghOffset;
            m_splitter.DrawGhostBar(m_dc);
        }

    protected:
        position        m_ghOffset;
        CSplitterBar    m_splitter;
        CSlider            m_slider;
        CDC                m_dc;
    };
    template<class TTraits>
    class CMinMaxInfoAccumulator
    {
        typedef LPMINMAXINFO(*CFunPtr)(MINMAXINFO& mmInfo, LPMINMAXINFO pMinMaxInfo);
    public:
        CMinMaxInfoAccumulator(bool bHorizontal)
        {
            m_pFun = bHorizontal ? &MinMaxInfoH : &MinMaxInfoV;
        }
        LPMINMAXINFO operator()(LPMINMAXINFO pMinMaxInfo, const CFrame& x) const
        {
            MINMAXINFO mmInfo;
            ZeroMemory(&mmInfo, sizeof(MINMAXINFO));
            x.GetMinMaxInfo(&mmInfo);
            return (*m_pFun)(mmInfo, pMinMaxInfo);
        }
    protected:
        static LPMINMAXINFO MinMaxInfoH(MINMAXINFO& mmInfo, LPMINMAXINFO pMinMaxInfo)
        {
            if (pMinMaxInfo->ptMinTrackSize.y < mmInfo.ptMinTrackSize.y)
                pMinMaxInfo->ptMinTrackSize.y = mmInfo.ptMinTrackSize.y;

            pMinMaxInfo->ptMinTrackSize.x += mmInfo.ptMinTrackSize.x +
                TTraits::GetHeight();
            return pMinMaxInfo;
        }
        static LPMINMAXINFO MinMaxInfoV(MINMAXINFO& mmInfo, LPMINMAXINFO pMinMaxInfo)
        {
            if (pMinMaxInfo->ptMinTrackSize.x < mmInfo.ptMinTrackSize.x)
                pMinMaxInfo->ptMinTrackSize.x = mmInfo.ptMinTrackSize.x;

            pMinMaxInfo->ptMinTrackSize.y += mmInfo.ptMinTrackSize.y + TTraits::GetWidth();
            return pMinMaxInfo;
        }
    protected:
        CFunPtr    m_pFun;
    };
protected:
    bool ArrangeH(const CRect& rc)
    {
        HDWP hdwp = reinterpret_cast<HDWP>(TRUE);
        const_iterator begin = m_frames.begin();
        const_iterator end = m_frames.end();

        if (begin != end)
        {
            hdwp = BeginDeferWindowPos(static_cast<int>(m_frames.size()));
            const_iterator next = begin;

            while ((hdwp != NULL) && (++next != end))
            {
                long x = (*begin) + typename CSplitterBar::Traits::GetSize(!IsHorizontal());
                hdwp = begin->DeferFramePos(hdwp,
                                            x,
                                            rc.top,
                                            (*next),
                                            rc.bottom);
                begin = next;
            }

            if (hdwp != NULL)
            {
                long x = (*begin) + typename CSplitterBar::Traits::GetSize(!IsHorizontal());
                hdwp = begin->DeferFramePos(hdwp,
                                            x,
                                            rc.top,
                                            rc.right,
                                            rc.bottom);

                if (hdwp)
                    hdwp = reinterpret_cast<HDWP>(EndDeferWindowPos(hdwp));
            }
        }

        return hdwp != NULL;
    }

    bool ArrangeV(const CRect& rc)
    {
        HDWP hdwp = reinterpret_cast<HDWP>(TRUE);
        const_iterator begin = m_frames.begin();
        const_iterator end = m_frames.end();

        if (begin != end)
        {
            hdwp = BeginDeferWindowPos(static_cast<int>(m_frames.size()));
            const_iterator next = begin;

            while ((hdwp != NULL) && (++next != end))
            {
                long y = (*begin) + typename CSplitterBar::Traits::GetSize(!IsHorizontal());
                hdwp = begin->DeferFramePos(hdwp,
                                            rc.left,
                                            y,
                                            rc.right,
                                            (*next));
                begin = next;
            }

            if (hdwp != NULL)
            {
                long y = (*begin) + typename CSplitterBar::Traits::GetSize(!IsHorizontal());
                hdwp = begin->DeferFramePos(hdwp,
                                            rc.left,
                                            y,
                                            rc.right,
                                            rc.bottom);

                if (hdwp)
                    hdwp = reinterpret_cast<HDWP>(EndDeferWindowPos(hdwp));
            }
        }

        return hdwp != NULL;
    }
    bool Arrange(const CRect& rcClient)
    {
        bool bRes;

        if (IsHorizontal())
            bRes = ArrangeH(rcClient);
        else
            bRes = ArrangeV(rcClient);

        return bRes;
    }

    CWndFramesPackageBase(bool bHorizontal)
        : m_bHorizontal(bHorizontal), m_frames(0, 0)
    {
    }
public:
    bool IsHorizontal() const
    {
        return m_bHorizontal;
    }

    void SetOrientation(bool bHorizontal)
    {
        m_bHorizontal = bHorizontal;
    }

    HCURSOR GetCursor(const CPoint& pt, const CRect& rc) const
    {
        HCURSOR hCursor = NULL;
        position pos = IsHorizontal() ? pt.x : pt.y;
        const_iterator i = m_frames.locate(pos);

        if (i != m_frames.end())
        {
            CEmbeddedSplitterBar splitter(!IsHorizontal(), (*i), rc);
            hCursor = splitter.GetCursor(pt);
        }

        return hCursor;
    }
    /*
        bool StartSliding(HWND hWnd,const CPoint& pt,const CRect& rc,bool bGhostMove)
        {
            std::auto_ptr<CSplitterMoveTrackerBase> pTracker;

            if(bGhostMove)
                pTracker=std::auto_ptr<CSplitterMoveTrackerBase>(
                                    new CSplitterMoveTrackerGhost(hWnd,*this,pt,rc));
            else
                pTracker=std::auto_ptr<CSplitterMoveTrackerBase>(
                                    new CSplitterMoveTrackerFull(*this,pt,rc));

            bool bRes=false;
            if(const_iterator(*pTracker)!=m_frames.end())
                 bRes=TrackDragAndDrop(*pTracker,hWnd);
            return bRes;
        }
    */
    bool StartSliding(HWND hWnd, const CPoint& pt, const CRect& rc, bool bGhostMove)
    {
        bool bRes = false;
        position pos = IsHorizontal() ? pt.x : pt.y;
        const_iterator i = m_frames.locate(pos);

        if (i != m_frames.end())
        {
            CEmbeddedSplitterBar splitter(!IsHorizontal(), (*i), rc);

            if (splitter.IsPtIn(pt))
            {
                std::auto_ptr<CSplitterMoveTrackerBase> pTracker;

                if (bGhostMove)
                    pTracker = std::auto_ptr<CSplitterMoveTrackerBase>(
                                   new CSplitterMoveTrackerGhost(hWnd, *this, pt, rc));
                else
                    pTracker = std::auto_ptr<CSplitterMoveTrackerBase>(
                                   new CSplitterMoveTrackerFull(hWnd, *this, pt, rc));

                if (const_iterator(*pTracker) != m_frames.end())
                    bRes = TrackDragAndDrop(*pTracker, hWnd);
            }
        }

        return bRes;
    }

    bool UpdateLayout(const CRect& rc)
    {
        CBounds bounds;

        if (IsHorizontal())
        {
            bounds.low = rc.left;
            bounds.hi = rc.right;
        }
        else
        {
            bounds.low = rc.top;
            bounds.hi = rc.bottom;
        }

        bounds.low -= typename CSplitterBar::Traits::GetSize(!IsHorizontal());
        CBounds::distance_t limit = m_frames.distance_limit();

        if (bounds.distance() < limit)
            bounds.hi = bounds.low + limit;

        m_frames.set_bounds(bounds);
        return Arrange(rc);
    }

    void GetMinFrameSize(const DFMHDR* pHdr, CSize& sz) const
    {
        MINMAXINFO mmInfo;
        ZeroMemory(&mmInfo, sizeof(MINMAXINFO));
        ::SendMessage(pHdr->hWnd, WM_GETMINMAXINFO, NULL, reinterpret_cast<LPARAM>(&mmInfo));
        sz.cx = mmInfo.ptMinTrackSize.x;
        sz.cy = mmInfo.ptMinTrackSize.y;
    }
    LRESULT GetMinFrameDist(const DFMHDR* pHdr) const
    {
        CSize sz;
        GetMinFrameSize(pHdr, sz);
        return (IsHorizontal()) ? sz.cx : sz.cy;
    }

    void GetMinMaxInfo(LPMINMAXINFO pMinMaxInfo) const
    {
        if (m_frames.size() != 0)
        {
            pMinMaxInfo = std::accumulate(m_frames.begin(), m_frames.end(),
                                          pMinMaxInfo, CMinMaxInfoAccumulator<typename CSplitterBar::Traits>(IsHorizontal()));

            if (IsHorizontal())
                pMinMaxInfo->ptMinTrackSize.x -= typename CSplitterBar::Traits::GetSize(!IsHorizontal());
            else
                pMinMaxInfo->ptMinTrackSize.y -= typename CSplitterBar::Traits::GetSize(!IsHorizontal());
        }
    }
    void Draw(CDC& dc, const CRect& rc) const
    {
        if (m_frames.begin() != m_frames.end())
            std::for_each(++m_frames.begin(), m_frames.end(), CEmbeddedSplitterBarPainter(dc, !IsHorizontal(), rc));
    }

    bool AcceptDock(DFDOCKRECT* pHdr, const CRect& rc)
    {
        bool bRes = false;
        CSize sz;
        GetMinFrameSize(&(pHdr->hdr), sz);

        if (rc.PtInRect(CPoint(pHdr->rect.left, pHdr->rect.top))
                && (sz.cx <= rc.Width())
                && (sz.cy <= rc.Height())
                && ((((IsHorizontal()) ? sz.cx : sz.cy) + m_frames.distance_limit()) < (m_frames.hi() - m_frames.low())))
        {
            if (IsHorizontal())
            {
                pHdr->rect.top = rc.top;
                pHdr->rect.bottom = rc.bottom;
            }
            else
            {
                pHdr->rect.left = rc.left;
                pHdr->rect.right = rc.right;
            }

            bRes = true;
        }

        return bRes;
    }
    bool Dock(CFrame& frame, const DFDOCKRECT* pHdr, const CRect& rc)
    {
        position          pos;
        CFrames::distance len;

        if (IsHorizontal())
        {
            pos = pHdr->rect.left;
            len = pHdr->rect.right - pHdr->rect.left;
        }
        else
        {
            pos = pHdr->rect.top;
            len = pHdr->rect.bottom - pHdr->rect.top;
        }

        iterator i = m_frames.locate(pos);

        if (i != m_frames.end())
        {
            iterator inext = i;
            ++inext;
            CBounds fbounds((*i), (inext != m_frames.end()) ? (*inext) : m_frames.hi());

            if ((fbounds.low + (fbounds.hi - fbounds.low) / 3) < pos)
                i = inext;
        }

        frame = pos;
        m_frames.insert(i, frame, len);
        return Arrange(rc);
    }
    bool Undock(const DFMHDR* pHdr, const CRect& rc)
    {
        iterator i = std::find_if(m_frames.begin(), m_frames.end(), CFrame::CCmp(pHdr->hWnd));
        ATLASSERT(i != m_frames.end());
        m_frames.erase(i);
        return Arrange(rc);
    }
    bool Replace(const DFDOCKREPLACE* pHdr, const CRect& rc)
    {
        iterator i = std::find_if(m_frames.begin(), m_frames.end(), CFrame::CCmp(pHdr->hdr.hWnd));
        ATLASSERT(i != m_frames.end());
        m_frames.replace(i, CFrame(0, pHdr->hWnd));
        return Arrange(rc);
    }

protected:
    bool    m_bHorizontal;
    CFrames    m_frames;
};

template< class TTraits = CDockingFrameTraits >
class CWndFramesPackage : public CWndFramesPackageBase<CWndFrame, TTraits >
{
    typedef CWndFrame CFrame;
    typedef TTraits CTraits;
    typedef typename CTraits::CSplitterBar            CSplitterBar;
    typedef CWndFramesPackage<TTraits>                thisClass;
    typedef CWndFramesPackageBase<CFrame, TTraits >    baseClass;
public:
    CWndFramesPackage(bool bHorizontal): baseClass(bHorizontal)
    {
    }
    void PrepareForDocking(CWindow wnd, HDOCKBAR bar)
    {
        wnd.ShowWindow(SW_HIDE);
        DWORD style = wnd.GetWindowLong(GWL_STYLE);
        DWORD newStyle = style & (~WS_POPUP) | WS_CHILD;
        wnd.SetWindowLong(GWL_STYLE, newStyle);
        wnd.SetParent(bar);
        wnd.SendMessage(WM_NCACTIVATE, TRUE);
        wnd.SendMessage(WMDF_NDOCKSTATECHANGED,
                        MAKEWPARAM(TRUE, IsHorizontal()),
                        reinterpret_cast<LPARAM>(bar));
    }
    void PrepareForUndocking(CWindow wnd, HDOCKBAR bar)
    {
        wnd.ShowWindow(SW_HIDE);
        DWORD style = wnd.GetWindowLong(GWL_STYLE);
        DWORD newStyle = style & (~WS_CHILD) | WS_POPUP;
        wnd.SetWindowLong(GWL_STYLE, newStyle);
        wnd.SetParent(NULL);
        wnd.SendMessage(WMDF_NDOCKSTATECHANGED,
                        FALSE,
                        reinterpret_cast<LPARAM>(bar));
    }

    bool SetDockingPosition(const DFDOCKPOS* pHdr, const CRect& rc)
    {
        ATLASSERT(::IsWindow(pHdr->hdr.hWnd));
        CWindow wnd(pHdr->hdr.hWnd);
        PrepareForDocking(wnd, pHdr->hdr.hBar);
        position pos = m_frames.low() + position((m_frames.hi() - m_frames.low()) * pHdr->fPctPos);
        m_frames.insert(CFrame(pos, pHdr->hdr.hWnd), pHdr->nHeight);
//        wnd.ShowWindow(SW_SHOWNA);
        bool bRes = Arrange(rc);
        wnd.SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
        return bRes;
    }

    bool GetDockingPosition(DFDOCKPOS* pHdr, const CRect& /*rc*/) const
    {
        ATLASSERT(::IsWindow(pHdr->hdr.hWnd));
        const_iterator i = std::find_if(m_frames.begin(), m_frames.end(), CFrame::CCmp(pHdr->hdr.hWnd));
        bool bRes = (i != m_frames.end());

        if (bRes)
        {
            position pos = *i - m_frames.low();
            pHdr->fPctPos = float(pos) / (m_frames.hi() - m_frames.low());
            pHdr->nHeight = m_frames.get_frame_size(i) - typename CSplitterBar::Traits::GetSize(!IsHorizontal());

//            pHdr->nWidth=IsHorizontal() ? rc.Height() : rc.Width();
//            pHdr->nWidth-=CSplitterBar::GetThickness();
            if (m_frames.size() == 1)
                pHdr->dwDockSide |= CDockingSide::sSingle;
        }

        return bRes;
    }

    bool AcceptDock(DFDOCKRECT* pHdr, const CRect& rc)
    {
        return (!((m_frames.size() == 1)
                  && (m_frames.begin()->hwnd() == pHdr->hdr.hWnd)))
               && baseClass::AcceptDock(pHdr, rc);
    }
    bool Dock(DFDOCKRECT* pHdr, const CRect& rc)
    {
        ATLASSERT(::IsWindow(pHdr->hdr.hWnd));
        CWindow wnd(pHdr->hdr.hWnd);
        PrepareForDocking(wnd, pHdr->hdr.hBar);
        CFrame frame(0, pHdr->hdr.hWnd);
        bool bRes = baseClass::Dock(frame, pHdr, rc);
        wnd.SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE |
            SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE);
        wnd.GetTopLevelParent().SetActiveWindow();

        return bRes;
    }
    bool Undock(DFMHDR* pHdr, const CRect& rc)
    {
        bool bRes = baseClass::Undock(pHdr, rc);
        ATLASSERT(bRes);
        PrepareForUndocking(pHdr->hWnd, pHdr->hBar);

        if (m_frames.size() == 0)
        {
            DFMHDR dockHdr;
            dockHdr.hWnd = pHdr->hBar;
            dockHdr.hBar = ::GetParent(pHdr->hBar);
            ATLASSERT(::IsWindow(dockHdr.hBar));
            dockHdr.code = DC_UNDOCK;
            ::SendMessage(dockHdr.hBar, WMDF_DOCK, NULL, reinterpret_cast<LPARAM>(&dockHdr));
            ::PostMessage(dockHdr.hWnd, WM_CLOSE, NULL, NULL);
        }

        return bRes;
    }

    bool Replace(const DFDOCKREPLACE* pHdr, const CRect& rc)
    {
        PrepareForUndocking(pHdr->hdr.hWnd, pHdr->hdr.hBar);
        PrepareForDocking(pHdr->hWnd, pHdr->hdr.hBar);
        bool bRes = baseClass::Replace(pHdr, rc);
        ATLASSERT(bRes);
        ::SetWindowPos(pHdr->hWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
//        ::ShowWindow(pHdr->hWnd,SW_SHOWNA);
        return bRes;
    }

};

template<class T>
class CPtrFrame
{
    typedef CPtrFrame<T> thisClass;
public:
    typedef typename T::position position;
    typedef typename T::distance distance;

    class CCmp
    {
    public:
        CCmp(HWND hWnd)    : m_hWnd(hWnd)
        {
        }
        bool operator()(const thisClass& frame) const
        {
            return (frame.hwnd() == m_hWnd);
        }
    protected:
        HWND m_hWnd;
    };

    CPtrFrame(position pos, T* ptr)
        : m_pos(pos), m_ptr(ptr)
    {
    }
    HWND hwnd() const
    {
        return m_ptr->operator HWND();
    }

    operator position() const
    {
        return m_pos;
    }

    thisClass& operator += (position val)
    {
        m_pos += val;
        return *this;
    }
    thisClass& operator -= (position val)
    {
        m_pos -= val;
        return *this;
    }

    thisClass& operator = (position pos)
    {
        m_pos = pos;
        return *this;
    }

    HDWP DeferFramePos(HDWP hdwp, long x1, long y1, long x2, long y2) const
    {
        return m_ptr->DeferFramePos(hdwp, x1, y1, x2, y2);
    }
    T* operator ->() const
    {
        return m_ptr.get();
    }
    void GetMinMaxInfo(LPMINMAXINFO pMinMaxInfo) const
    {
        m_ptr->GetMinMaxInfo(pMinMaxInfo);
    }
    distance MinDistance() const
    {
        return m_ptr->MinDistance();
    }
protected:
    position m_pos;
    mutable std::tr1::shared_ptr<T> m_ptr;
};


struct IFrame
{
    typedef long position;
    typedef long distance;

    virtual  ~IFrame() {};
    virtual  HWND hwnd() const = 0;
    operator HWND() const
    {
        return hwnd();
    }
    virtual bool AcceptDock(DFDOCKRECT* pHdr) const = 0;
    virtual distance MinDistance() const = 0;
    virtual void GetMinMaxInfo(LPMINMAXINFO pMinMaxInfo) const = 0;

    virtual HDWP DeferFramePos(HDWP hdwp, long x1, long y1, long x2, long y2) const
    {
        return ::DeferWindowPos(hdwp, hwnd(),
                                NULL,
                                x1, y1,
                                x2 - x1, y2 - y1,
                                SWP_NOZORDER | SWP_NOACTIVATE);
    }
};


class CWindowPtrWrapper : public IFrame
{
public:
    CWindowPtrWrapper(HWND* phWnd): m_phWnd(phWnd)
    {
    }
    virtual HWND hwnd() const
    {
        return (*m_phWnd);
    }
    virtual bool AcceptDock(DFDOCKRECT* /*pHdr*/) const
    {
        return false;
    }
    virtual HDWP DeferFramePos(HDWP hdwp, long x1, long y1, long x2, long y2) const
    {
        if (*m_phWnd != NULL)
            hdwp =::DeferWindowPos(hdwp, hwnd(),
                                   NULL,
                                   x1, y1,
                                   x2 - x1, y2 - y1,
                                   SWP_NOZORDER | SWP_NOACTIVATE);

        return hdwp;
    }
    void GetMinMaxInfo(LPMINMAXINFO pMinMaxInfo) const
    {
        if (*m_phWnd != NULL)
            ::SendMessage(hwnd(), WM_GETMINMAXINFO, NULL, reinterpret_cast<LPARAM>(pMinMaxInfo));
    }
    virtual distance MinDistance() const
    {
        MINMAXINFO mmInfo;
        ZeroMemory(&mmInfo, sizeof(MINMAXINFO));
        GetMinMaxInfo(&mmInfo);
        return mmInfo.ptMinTrackSize.x;;
    }
protected:
    HWND* m_phWnd;
};

//TImbeddedPackegeWnd
template<class TPackageFrame, class TTraits = CDockingWindowTraits >
class CSubWndFramesPackage :
    public CRect,
    public CWndFramesPackageBase<CPtrFrame<IFrame>, TTraits >
{
    typedef CPtrFrame<IFrame> CFrame;
    typedef TTraits CTraits;
    typedef typename CTraits::CSplitterBar    CSplitterBar;
    typedef CSubWndFramesPackage<TPackageFrame, TTraits> thisClass;
    typedef CWndFramesPackageBase<CFrame, TTraits >        baseClass;
    typedef TPackageFrame    CPackageFrame;

    struct  CDockOrientationFlag
    {
        enum {low = 0, high = 1};
        static void SetHigh(DWORD& flag)
        {
            flag = high;
        }
        static void SetLow(DWORD& flag)
        {
            flag = low;
        }
        static bool IsLow(DWORD flag)
        {
            return (flag & high) == 0;
        }
    };
protected:
    class CFrameWrapper : public IFrame
    {
    public:
        CFrameWrapper(thisClass* ptr): m_ptr(ptr)
        {
        }
        virtual ~CFrameWrapper()
        {
        }
        virtual  HWND hwnd() const
        {
            return NULL;
        }
        /*
                virtual bool AcceptDock(DFDOCKRECT* pHdr) const
                {
                    return m_ptr->AcceptDock(pHdr);
                }
        */
        virtual bool AcceptDock(DFDOCKRECT* /*pHdr*/) const
        {
            ATLASSERT(false);
            return true;
        }

        virtual HDWP DeferFramePos(HDWP hdwp, long x1, long y1, long x2, long y2) const
        {
            m_ptr->UpdateLayout(CRect(x1, y1, x2, y2));
            return hdwp;
        }
        virtual void GetMinMaxInfo(LPMINMAXINFO pMinMaxInfo) const
        {
            m_ptr->GetMinMaxInfo(pMinMaxInfo);
        }
        virtual distance MinDistance() const
        {
            MINMAXINFO mmInfo;
            ZeroMemory(&mmInfo, sizeof(MINMAXINFO));
            GetMinMaxInfo(&mmInfo);
            return m_ptr->IsHorizontal() ? mmInfo.ptMinTrackSize.y : mmInfo.ptMinTrackSize.x;
        }
    protected:
        thisClass* m_ptr;
    };
public:
    CSubWndFramesPackage(bool bHorizontal)
        : baseClass(bHorizontal), m_pDecl(0)
    {
    }
    HCURSOR GetCursor(const CPoint& pt) const
    {
        return baseClass::GetCursor(pt, *this);
    }
    bool StartSliding(HWND hWnd, const CPoint& pt, bool bGhostMove)
    {
        return baseClass::StartSliding(hWnd, pt, *this, bGhostMove);
    }

#ifndef DW_PROPORTIONAL_RESIZE
    bool UpdateLayout(const CRect& rc)
    {
        bool bRes = EqualRect(&rc) != FALSE;

        if (!bRes)
        {
            CopyRect(rc);
            CBounds bounds;

            if (IsHorizontal())
            {
                bounds.low = rc.left;
                bounds.hi = rc.right;
            }
            else
            {
                bounds.low = rc.top;
                bounds.hi = rc.bottom;
            }

            bounds.low -= typename CSplitterBar::Traits::GetSize(!IsHorizontal());
            CBounds::distance_t limit = m_frames.distance_limit();

            if (bounds.distance() < limit)
                bounds.hi = bounds.low + limit;

            if (m_pDecl != 0)
                m_frames.set_bounds(bounds, CFrame::CCmp(m_pDecl->hwnd()));
            else
                m_frames.set_bounds(bounds);

            bRes = Arrange(rc);
        }

        return bRes;
    }
#else
    bool UpdateLayout(const CRect& rc)
    {
        bool bRes = EqualRect(&rc) != FALSE;

        if (!bRes)
        {
            CopyRect(rc);
            bRes = baseClass::UpdateLayout(*this);
        }

        return bRes;
    }
#endif //DW_PROPORTIONAL_RESIZE
    void Draw(CDC& dc)
    {
        baseClass::Draw(dc, *this);
    }

    int GetControlledLen()
    {
        return 15 + typename CSplitterBar::Traits::GetSize(!IsHorizontal());
    }

    bool AcceptDock(DFDOCKRECT* pHdr)
    {
        CPoint pt(pHdr->rect.left, pHdr->rect.top);
        CSize sz(0, 0);
        position pos;

        if (IsHorizontal())
        {
            pos = pt.x;
            sz.cx = GetControlledLen();
        }
        else
        {
            pos = pt.y;
            sz.cy = GetControlledLen();
        }

        bool bRes = PtInRect(pt) == TRUE;

        if (bRes)
        {
//            position pos=IsHorizontal() ? pHdr->rect.left :pHdr->rect.top;
            const_iterator i = m_frames.locate(pos);

            if (m_frames.get_frame_low(i) + GetControlledLen() > pos)
                bRes = AcceptDock(i, pHdr, *this);
            else
            {
                if (m_frames.get_frame_hi(i) - GetControlledLen() < pos)
                    bRes = AcceptDock(++i, pHdr, *this);
                else
                {
                    bRes = i->hwnd() != m_pDecl->hwnd();

                    if (bRes)
                        bRes = (*i)->AcceptDock(pHdr);
                }
            }
        }
        else
        {
            CRect rc(this);
            rc.InflateRect(sz);
            bRes = rc.PtInRect(pt) == TRUE;

            if (bRes)
            {
                if (pos < m_frames.hi())
                    bRes = AcceptDock(m_frames.begin(), pHdr, rc);
                else
                    bRes = AcceptDock(m_frames.end(), pHdr, rc);
            }
        }

        return bRes;
    }

    bool AcceptDock(const_iterator i, DFDOCKRECT* pHdr, const CRect& rc)
    {
        ATLASSERT(std::find_if(m_frames.begin(), m_frames.end(), CFrame::CCmp(m_pDecl->hwnd())) != m_frames.end());
        const_iterator begin = m_frames.begin();

        if (std::find_if(begin, i, CFrame::CCmp(m_pDecl->hwnd())) == i)
            CDockOrientationFlag::SetLow(pHdr->flag);
        else
            CDockOrientationFlag::SetHigh(pHdr->flag);

        bool bRes = baseClass::AcceptDock(pHdr, rc);

        if (bRes)
        {
            if (IsHorizontal())
            {
                long len = (pHdr->rect.right - pHdr->rect.left);

                if (CDockOrientationFlag::IsLow(pHdr->flag))
                {
                    ATLASSERT(i != m_frames.end());
                    pHdr->rect.left = (*i) + typename CSplitterBar::Traits::GetSize(!IsHorizontal());
                    pHdr->rect.right = pHdr->rect.left + len;
                }
                else
                {
                    pHdr->rect.right = (i == m_frames.end()) ? m_frames.hi() : (*i);
                    pHdr->rect.left = pHdr->rect.right - len;
                }
            }
            else
            {
                long len = (pHdr->rect.bottom - pHdr->rect.top);

                if (CDockOrientationFlag::IsLow(pHdr->flag))
                {
                    ATLASSERT(i != m_frames.end());
                    pHdr->rect.top = (*i) + typename CSplitterBar::Traits::GetSize(!IsHorizontal());
                    pHdr->rect.bottom = pHdr->rect.top + len;
                }
                else
                {
                    pHdr->rect.bottom = (i == m_frames.end()) ? m_frames.hi() : (*i);
                    pHdr->rect.top = pHdr->rect.bottom - len;
                }
            }
        }

        return bRes;
    }
    bool Dock(CFrame& frame, const DFDOCKRECT* pHdr, const CRect& rc)
    {
        position          pos;
        CFrames::distance len;

        if (IsHorizontal())
        {
            pos = pHdr->rect.left;
            len = pHdr->rect.right - pHdr->rect.left;
        }
        else
        {
            pos = pHdr->rect.top;
            len = pHdr->rect.bottom - pHdr->rect.top;
        }

        frame = pos;

        if (!CDockOrientationFlag::IsLow(pHdr->flag))
            pos += len;

        iterator i;

        if (pos != m_frames.hi())
            i = m_frames.locate(pos);
        else
            i = m_frames.end();

        m_frames.insert(i, CFrame::CCmp(m_pDecl->hwnd()), frame, len);
        return Arrange(rc);
    }

    bool Dock(DFDOCKRECT* pHdr)
    {
        CPackageFrame* ptr = CPackageFrame::CreateInstance(pHdr->hdr.hBar, !IsHorizontal());
        bool bRes = (ptr != 0);

        if (bRes)
        {
            HWND hDockWnd = pHdr->hdr.hWnd;
            pHdr->hdr.hWnd = ptr->hwnd();
            CFrame frame(0, ptr);
            bRes = Dock(frame, pHdr, *this);
            ATLASSERT(bRes);

            if (bRes)
            {
                pHdr->hdr.hWnd = hDockWnd;
                pHdr->hdr.hBar = ptr->hwnd();
                CWindow bar(pHdr->hdr.hBar);
                bar.GetWindowRect(&(pHdr->rect));
                bRes =::SendMessage(ptr->hwnd(), WMDF_DOCK, NULL, reinterpret_cast<LPARAM>(pHdr)) != FALSE;
            }
        }

        return bRes;
    }

    bool Undock(const DFMHDR* pHdr)
    {
        iterator i = std::find_if(m_frames.begin(), m_frames.end(), CFrame::CCmp(pHdr->hWnd));
        bool bRes = (i != m_frames.end());

        if (bRes)
        {
            m_frames.erase(i, CFrame::CCmp(m_pDecl->hwnd()));
            bRes = Arrange(*this);
        }

        return bRes;
    }

    bool GetDockingPosition(DFDOCKPOS* pHdr) const
    {
        const_iterator i = std::find_if(m_frames.begin(), m_frames.end(), CFrame::CCmp(pHdr->hdr.hBar));
        bool bRes = (i != m_frames.end());

        if (bRes)
        {
            pHdr->nWidth = m_frames.get_frame_size(i) -
                typename CSplitterBar::Traits::GetSize(!IsHorizontal());
            CFrames::size_type dWnd = std::distance(m_frames.begin(), i);
            i = std::find_if(m_frames.begin(), m_frames.end(), CFrame::CCmp(m_pDecl->hwnd()));
            ATLASSERT(i != m_frames.end());
            CFrames::size_type dBWnd = std::distance(m_frames.begin(), i);

            if (dBWnd > dWnd)
            {
                pHdr->nBar = dWnd;
                pHdr->dwDockSide |= CDockingSide::sTop;
            }
            else
            {
                pHdr->nBar = m_frames.size() - dWnd - 1;
//                pHdr->dwDockSide|=0;
            }

            bRes = (::SendMessage(pHdr->hdr.hBar, WMDF_DOCK, NULL, reinterpret_cast<LPARAM>(pHdr)) != FALSE);
        }

        return bRes;
    }

//    bool SetDockingPosition(DFDOCKPOS* pHdr)
//    {
//        bool bRes=true;
//        UINT limit=GetMinFrameDist(&(pHdr->hdr));
//        if(pHdr->nWidth<limit)
//                        pHdr->nWidth=limit;
//        CDockingSide side(pHdr->dwDockSide);
//        if(side.IsTop())
//        {
//            iterator i=ssec::search_n(m_frames.begin(),m_frames.end(),CFrame::CCmp(m_pDecl->hwnd()),pHdr->nBar);
//            ATLASSERT(i!=m_frames.end());
//            if(i->hwnd()==m_pDecl->hwnd() || side.IsSingle())
//            {
//                CPackageFrame* ptr=CPackageFrame::CreateInstance(pHdr->hdr.hBar,!IsHorizontal());
//                bRes=(ptr!=0);
//                if(bRes)
//                {
////                    i=m_frames.insert(i,CFrame(*i,ptr),pHdr->nWidth);
//                    i=m_frames.insert(i,CFrame::CCmp(m_pDecl->hwnd()),CFrame(*i,ptr),pHdr->nWidth+splitterSize);
//                }
//            }
//            else
//                m_frames.set_frame_size(i,pHdr->nWidth+splitterSize);
//            pHdr->hdr.hBar=i->hwnd();
//        }
//        else
//        {
//            reverse_iterator ri=ssec::search_n(m_frames.rbegin(),m_frames.rend(),CFrame::CCmp(m_pDecl->hwnd()),pHdr->nBar);
//            ATLASSERT(ri!=m_frames.rend());
//            iterator i=ri.base();
//            if(/*ri->hwnd()*/(*ri).hwnd()==m_pDecl->hwnd() || side.IsSingle())
//            {
//                CPackageFrame* ptr=CPackageFrame::CreateInstance(pHdr->hdr.hBar,!IsHorizontal());
//                bRes=(ptr!=0);
//                if(bRes)
//                {
//                    position pos=(i==m_frames.end())? m_frames.hi():*i;
//                    pos-=pHdr->nWidth+CSplitterBar::GetThickness();
//                    if(pos<m_frames.low())
//                        pos=m_frames.low();
////                    i=m_frames.insert(i,CFrame(pos,ptr),pHdr->nWidth);
//                    i=m_frames.insert(i,CFrame::CCmp(m_pDecl->hwnd()),CFrame(pos,ptr),pHdr->nWidth+splitterSize);
//                }
//            }
//            else
//            {
//                ATLASSERT(i!=m_frames.begin());
//                m_frames.set_frame_size(--i,pHdr->nWidth+splitterSize);
//            }
//            pHdr->hdr.hBar=i->hwnd();
//        }
//        bRes=Arrange(*this);
//        ATLASSERT(bRes);
//        if(bRes)
//        {
//            bRes=(::SendMessage(pHdr->hdr.hBar,WMDF_DOCK,NULL,reinterpret_cast<LPARAM>(pHdr))!=FALSE);
//            ATLASSERT(bRes);
//        }
//        return bRes;
//    }

    bool SetDockingPosition(DFDOCKPOS* pHdr)
    {
        bool bRes = true;
        LRESULT limit = GetMinFrameDist(&(pHdr->hdr));

        if (static_cast<LRESULT>(pHdr->nWidth) < limit)
            pHdr->nWidth = static_cast<UINT>(limit);

        CDockingSide side(pHdr->dwDockSide);

        if (side.IsTop())
        {
            iterator i = ssec::search_n(m_frames.begin(), m_frames.end(), CFrame::CCmp(m_pDecl->hwnd()), pHdr->nBar);
            ATLASSERT(i != m_frames.end());

            if (i->hwnd() == m_pDecl->hwnd() || side.IsSingle())
            {
                CPackageFrame* ptr = CPackageFrame::CreateInstance(pHdr->hdr.hBar, !IsHorizontal());
                bRes = (ptr != 0);

                if (bRes)
                {
//                    i=m_frames.insert(i,CFrame(*i,ptr),pHdr->nWidth);
                    i = m_frames.insert(i, CFrame::CCmp(m_pDecl->hwnd()), CFrame(*i, ptr), pHdr->nWidth +
                        typename CSplitterBar::Traits::GetSize(!IsHorizontal()));
                    bRes = Arrange(*this);
                    ATLASSERT(bRes);
                }
            }

            pHdr->hdr.hBar = i->hwnd();
        }
        else
        {
            reverse_iterator ri = ssec::search_n(m_frames.rbegin(), m_frames.rend(), CFrame::CCmp(m_pDecl->hwnd()), pHdr->nBar);
            ATLASSERT(ri != m_frames.rend());
            iterator i = ri.base();

            if (/*ri->hwnd()*/(*ri).hwnd() == m_pDecl->hwnd() || side.IsSingle())
            {
                CPackageFrame* ptr = CPackageFrame::CreateInstance(pHdr->hdr.hBar, !IsHorizontal());
                bRes = (ptr != 0);

                if (bRes)
                {
                    position pos = (i == m_frames.end()) ? m_frames.hi() : *i;
                    pos -= pHdr->nWidth + typename CSplitterBar::Traits::GetSize(!IsHorizontal());

                    if (pos < m_frames.low())
                        pos = m_frames.low();

//                    i=m_frames.insert(i,CFrame(pos,ptr),pHdr->nWidth);
                    i = m_frames.insert(i, CFrame::CCmp(m_pDecl->hwnd()), CFrame(pos, ptr), pHdr->nWidth + typename CSplitterBar::Traits::GetSize(!IsHorizontal()));
                    bRes = Arrange(*this);
                    ATLASSERT(bRes);
                    pHdr->hdr.hBar = i->hwnd();
                }
            }
            else
                pHdr->hdr.hBar =/*ri->hwnd()*/(*ri).hwnd();
        }

        if (bRes)
        {
            bRes = (::SendMessage(pHdr->hdr.hBar, WMDF_DOCK, NULL, reinterpret_cast<LPARAM>(pHdr)) != FALSE);
            ATLASSERT(bRes);
        }

        return bRes;
    }

    bool Insert(HWND* ptr, const CRect& rc)
    {
        DFDOCKRECT dockHdr;
        ::CopyRect(&dockHdr.rect, &rc);
        ATLASSERT(m_pDecl == 0);
        m_pDecl = new CWindowPtrWrapper(ptr);
        CFrame frame(0, m_pDecl);
        return baseClass::Dock(frame, &dockHdr, *this);
    }
    bool Insert(thisClass* ptr, const CRect& rc)
    {
        DFDOCKRECT dockHdr;
        ::CopyRect(&dockHdr.rect, &rc);
        ATLASSERT(m_pDecl == 0);
        m_pDecl = new CFrameWrapper(ptr);
        CFrame frame(0, m_pDecl);
        return baseClass::Dock(frame, &dockHdr, *this);
    }
protected:
    IFrame* m_pDecl;
};

}//namespace dockwins

#endif // WTL_DW_WNDFRMPKG_H_INCLUDED_
