// Copyright (c) 2002
// Sergey Klimov (kidd@ukr.net)

#ifndef WTL_DW_SSTATE_H_INCLUDED_
#define WTL_DW_SSTATE_H_INCLUDED_

#pragma once

// NOTE: To be able to CSplitterWindowStateAdapter include atlsplit.h first

#include <algorithm>
#include <limits>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <utility>

#include "stg.h"

namespace sstate
{

const    TCHAR ctxtGeneral[]        = _T("General");
const    TCHAR ctxtCXScreen[]    = _T("SM_CXSCREEN");
const    TCHAR ctxtCYScreen[]    = _T("SM_CYSCREEN");
const    TCHAR ctxtPlacement[]    = _T("placement");
const    TCHAR ctxtPosition[]    = _T("position");
const    TCHAR ctxtMainWindow[]    = _T("MainWindow");
const    TCHAR ctxtVisible[]        = _T("visible");
const    TCHAR ctxtBand[]        = _T("band");
const    TCHAR ctxtWndPrefix[]    = _T("Wnd-");
typedef std::basic_string<TCHAR> tstring;
typedef UINT ID;
//typedef tstring ID;

const unsigned int ForceDefaultCmdShow = 0x80000000;

struct IState
{
    virtual ~IState() {}
    virtual bool Store(IStorge& /*stg*/) = 0;
    virtual bool Restore(IStorge& /*stg*/, const std::pair<long, long>& /*xratio*/, const std::pair<long, long>& /*yratio*/) = 0;
    virtual bool RestoreDefault() = 0;
    virtual void AddRef() = 0;
    virtual void Release() = 0;
};

template<class T>
class CStateBase : public T
{
public:
    CStateBase(): m_ref(1)
    {
    }
    virtual void AddRef()
    {
        m_ref++;
    }
    virtual void Release()
    {
        if (--m_ref == 0)
            delete this;
    }
    virtual ~CStateBase()
    {
        ATLASSERT(m_ref == 0);
    }
private:
    CStateBase(const CStateBase&);
    const CStateBase& operator=(const CStateBase&);
protected:
    UINT m_ref;
};

template<class T = IState>
class CStateHolder
{
    typedef CStateHolder<T> thisClass;
public:
    CStateHolder()
        : m_pState(0)
    {
    }
    CStateHolder(T* pState)
    {
        pState->AddRef();
        m_pState = pState;
    }
    CStateHolder(const thisClass& sholder)
    {
        *this = (sholder);
    }
    thisClass& operator = (const thisClass& sholder)
    {
        m_pState = const_cast<T*>(sholder.m_pState);

        if (m_pState != 0)
            m_pState->AddRef();

        return *this;
    }
    ~CStateHolder()
    {
        if (m_pState != 0)
            m_pState->Release();
    }
    const T* operator ->() const
    {
        return m_pState;
    }
    T* operator ->()
    {
        return m_pState;
    }
protected:
    T* m_pState;
};

template<class TStorage>
class CContainerImpl
    : public CStateBase<IState>
{
protected:
    typedef CStateHolder<IState> CItem;
    typedef std::map<ID, CItem> CBunch;
    class CStorer
    {
    public:
        CStorer(IStorge& stgTop)
            : m_stgTop(stgTop)
        {
        }
        void operator()(std::pair<const ID, CItem>& x) const
        {
            std::basic_ostringstream<TCHAR> sstrKey;
            sstrKey.flags(std::ios::hex | std::ios::showbase);
            sstrKey << ctxtWndPrefix << x.first;
            /*
                        CRegKey key;
                        DWORD dwDisposition;
                        LONG lRes = key.Create(m_keyTop,sstrKey.str().c_str(),REG_NONE,
                                                REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ,
                                                NULL,&dwDisposition);
                        if(lRes==ERROR_SUCCESS)
                            x.second->Store(key);
            */
            TStorage stg;

            if (stg.Create(m_stgTop, sstrKey.str().c_str(), IStorge::ReadWrite) == ERROR_SUCCESS)
                x.second->Store(stg);
        }
    protected:
        IStorge& m_stgTop;
    };

    class CRestorer
    {
    public:
        CRestorer(IStorge& stgTop, const std::pair<long, long>& xratio, const std::pair<long, long>& yratio)
            : m_stgTop(stgTop)
            , m_xratio(xratio)
            , m_yratio(yratio)
        {
        }
        void operator()(std::pair<const ID, CItem>& x) const
        {
            std::basic_ostringstream<TCHAR> sstrKey;
            sstrKey.flags(std::ios::hex | std::ios::showbase);
            sstrKey << ctxtWndPrefix << x.first;
            /*
                        CRegKey key;
                        LONG lRes = key.Open(m_keyTop,sstrKey.str().c_str(),KEY_READ);
                        if(lRes==ERROR_SUCCESS)
                            x.second->Restore(key,m_xratio,m_yratio);
                        else
                            x.second->RestoreDefault();
            */
            TStorage stg;

            if (stg.Open(m_stgTop, sstrKey.str().c_str(), IStorge::Read) != ERROR_SUCCESS
                    || !x.second->Restore(stg, m_xratio, m_yratio))
                x.second->RestoreDefault();
        }
    protected:
        IStorge& m_stgTop;
        std::pair<long, long> m_xratio;
        std::pair<long, long> m_yratio;
    };
    struct CDefRestorer
    {
        void operator()(std::pair<const ID, CItem>& x) const
        {
            x.second->RestoreDefault();
        }
    };
public:
    CContainerImpl()
        : m_nextFreeID(/*std::numeric_limits<ID>::max()*/ULONG_MAX)
    {
    }
    ID GetUniqueID() const
    {
        return m_nextFreeID--;
    }
    virtual bool Store(IStorge& stg)
    {
        std::for_each(m_bunch.begin(), m_bunch.end(), CStorer(stg));
        return true;
    }
    virtual bool Restore(IStorge& stg, const std::pair<long, long>& xratio, const std::pair<long, long>& yratio)
    {
        std::for_each(m_bunch.begin(), m_bunch.end(), CRestorer(stg, xratio, yratio));
        return true;
    }
    virtual bool RestoreDefault()
    {
        std::for_each(m_bunch.begin(), m_bunch.end(), CDefRestorer());
        return true;
    }
    ID Add(IState* pState)
    {
        ID id = GetUniqueID();
        Add(id, pState);
        return id;
    }
    void Add(ID id, IState* pState)
    {
        CStateHolder<IState> h(pState);
        m_bunch[id] = h;
    }
    void Remove(ID id)
    {
        ATLASSERT(m_bunch.find(id) != m_bunch.end());
        m_bunch.erase(id);
    }
protected:
    mutable ID    m_nextFreeID;
    CBunch m_bunch;
};

namespace
{
class SetWindowPlacementInsensibly
{
public:
    SetWindowPlacementInsensibly(HWND hWnd, const WINDOWPLACEMENT* wp)
        : m_hWnd(hWnd)
    {
        m_oldWindowProc = (WNDPROC)(::SetWindowLongPtr(m_hWnd, GWLP_WNDPROC, LONG_PTR(&WndProc)));
        ::SetWindowPlacement(m_hWnd, wp);
    }
    ~SetWindowPlacementInsensibly()
    {
        ::SetWindowLongPtr(m_hWnd, GWLP_WNDPROC, LONG_PTR(m_oldWindowProc));
    }
    static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        LRESULT res = 0;

        switch (uMsg)
        {
            case WM_PAINT:
            case WM_NCACTIVATE:
            case WM_ACTIVATE:
            case WM_SIZE:
                break;

            default:
                res =::DefWindowProc(hWnd, uMsg, wParam, lParam);
        }

        return res;
    }
private:
    HWND m_hWnd;
    WNDPROC m_oldWindowProc;
};
}

template<class TStorage>
class CWindowStateMgr
{
protected:
    class CImpl
        : public CContainerImpl<TStorage>
    {
        typedef CContainerImpl<TStorage> baseClass;
    public:
        CImpl(HWND hWnd = NULL, int nDefCmdShow = SW_SHOWDEFAULT)
            : m_hWnd(hWnd), m_nDefCmdShow(nDefCmdShow)
        {
        }
        void SetWindow(HWND hWnd = NULL, int nDefCmdShow = SW_SHOWDEFAULT)
        {
            ATLASSERT(::IsWindow(hWnd));
            m_hWnd = hWnd;
            m_nDefCmdShow = nDefCmdShow;
        }
        virtual bool Store(IStorge& stg)
        {
            ATLASSERT(IsWindow(m_hWnd));
            WINDOWPLACEMENT wp;
            wp.length = sizeof(WINDOWPLACEMENT);
            bool bRes = false;

            if (::GetWindowPlacement(m_hWnd, &wp))
            {
                if (wp.showCmd == SW_SHOWMINIMIZED)
                    wp.showCmd = SW_SHOWNORMAL;

                wp.flags &= WPF_RESTORETOMAXIMIZED;

//                if(::IsZoomed(m_hWnd))
//                    wp.flags |= WPF_RESTORETOMAXIMIZED;
                if (wp.flags != 0)
                    wp.showCmd = SW_SHOWMAXIMIZED;

                bRes = (stg.SetBinary(ctxtPlacement, &wp, sizeof(WINDOWPLACEMENT)) == ERROR_SUCCESS);
                /*
                                bRes=(::RegSetValueEx(key,ctxtPlacement,NULL,REG_BINARY,
                                                        reinterpret_cast<CONST BYTE *>(&wp),
                                                        sizeof(WINDOWPLACEMENT))==ERROR_SUCCESS);
                */
            }

            return baseClass::Store(stg);
        }
        virtual bool Restore(IStorge& stg, const std::pair<long, long>& xratio, const std::pair<long, long>& yratio)
        {
            ATLASSERT(IsWindow(m_hWnd));
            WINDOWPLACEMENT wp;
            /*
                        DWORD dwType;
                        DWORD cbData=sizeof(WINDOWPLACEMENT);
                        bool bRes=(::RegQueryValueEx(key,ctxtPlacement,NULL,&dwType,
                                                        reinterpret_cast<LPBYTE>(&wp),&cbData)==ERROR_SUCCESS)
                                                        &&(dwType==REG_BINARY);
            */
            size_t size = sizeof(WINDOWPLACEMENT);
            bool bRes = (stg.GetBinary(ctxtPlacement, &wp, size) == ERROR_SUCCESS
                         && (size == sizeof(WINDOWPLACEMENT)));

            if (bRes)
            {
                wp.ptMaxPosition.x = long(::MulDiv(wp.ptMaxPosition.x, xratio.first, xratio.second));
                wp.ptMaxPosition.y = long(::MulDiv(wp.ptMaxPosition.y, yratio.first, yratio.second));
                wp.ptMinPosition.x = long(::MulDiv(wp.ptMinPosition.x, xratio.first, xratio.second));
                wp.ptMinPosition.y = long(::MulDiv(wp.ptMinPosition.y, yratio.first, yratio.second));
                wp.rcNormalPosition.left = long(::MulDiv(wp.rcNormalPosition.left, xratio.first, xratio.second));
                wp.rcNormalPosition.top = long(::MulDiv(wp.rcNormalPosition.top, yratio.first, yratio.second));
                wp.rcNormalPosition.right = long(::MulDiv(wp.rcNormalPosition.right, xratio.first, xratio.second));
                wp.rcNormalPosition.bottom = long(::MulDiv(wp.rcNormalPosition.bottom, yratio.first, yratio.second));
                UINT showCmd = ((m_nDefCmdShow & ForceDefaultCmdShow) != 0)
                               ? m_nDefCmdShow & (~ForceDefaultCmdShow)
                               : wp.showCmd;
                wp.showCmd = SW_HIDE;

                if (showCmd == SW_MAXIMIZE)
                {
                    RECT rc;
                    GetWorkAreaOfMonitorFromWindow(&rc);
                    ::SetWindowPos(m_hWnd, 0, rc.left, rc.top,
                                   rc.right - rc.left,
                                   rc.bottom - rc.top,
                                   SWP_DEFERERASE | SWP_NOREDRAW | /*SWP_NOCOPYBITS|*/
                                   SWP_NOACTIVATE | SWP_NOZORDER);
                    bRes = baseClass::Restore(stg, xratio, yratio);
                    {
                        SetWindowPlacementInsensibly(m_hWnd, &wp);
                    }
                    ::ShowWindow(m_hWnd, SW_MAXIMIZE);
                    ::SetWindowPos(m_hWnd, 0, 0, 0, 0, 0,
                                   SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
                                   SWP_DRAWFRAME | SWP_FRAMECHANGED |
                                   SWP_NOOWNERZORDER | SWP_NOSENDCHANGING);
                }
                else
                {
                    ::SetWindowPlacement(m_hWnd, &wp);
                    bRes = baseClass::Restore(stg, xratio, yratio);
                    ::ShowWindow(m_hWnd, showCmd);
                }

                ::SetForegroundWindow(m_hWnd);    // Win95 needs this
            }
            else
                bRes = baseClass::RestoreDefault();

            return bRes;
        }
        virtual bool RestoreDefault()
        {
            ATLASSERT(IsWindow(m_hWnd));
            bool bRes = baseClass::RestoreDefault();
            ShowWindow(m_hWnd, m_nDefCmdShow & (~ForceDefaultCmdShow));
            return bRes;
        }

        void GetWorkAreaOfMonitorFromWindow(RECT* rc)
        {
#ifdef HMONITOR_DECLARED
            MONITORINFO info;
            info.cbSize = sizeof(MONITORINFO);
            HMONITOR mon;

            if (m_hWnd != 0
                    && ((mon =::MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTOPRIMARY)) != 0)
                    && (::GetMonitorInfo(mon, &info)))
                ::CopyRect(rc, &info.rcWork);
            else
#endif// HMONITOR_DECLARED
            {
                if (!::SystemParametersInfo(SPI_GETWORKAREA, 0, rc, 0))
                {
                    rc->left = rc->top = 0;
                    rc->right =::GetSystemMetrics(SM_CXFULLSCREEN);
                    rc->bottom =::GetSystemMetrics(SM_CYFULLSCREEN);
                }
            }
        }

    protected:
        HWND    m_hWnd;
        int        m_nDefCmdShow;
    };
public:
    CWindowStateMgr(HWND hWnd = NULL, int nDefCmdShow = SW_SHOWDEFAULT)
    {
        m_pImpl = new CImpl(hWnd, nDefCmdShow);
    }
    ~CWindowStateMgr()
    {
        ATLASSERT(m_pImpl);
        m_pImpl->Release();
    }
    operator IState* ()
    {
        return m_pImpl;
    }
    ID Add(IState* pState)
    {
        return m_pImpl->Add(pState);
    }
    void Add(ID id, IState* pState)
    {
        m_pImpl->Add(id, pState);
    }
    void Remove(ID id)
    {
        m_pImpl->Remove(id);
    }

    void Initialize(HWND hWnd, int nDefCmdShow = SW_SHOWDEFAULT)
    {
        m_pImpl->SetWindow(hWnd, nDefCmdShow);
    }

    bool Store(TStorage& stgMain)
    {
        TStorage general;

        if (general.Create(stgMain, ctxtGeneral, IStorge::ReadWrite) == ERROR_SUCCESS)
        {
            RECT rc;
            m_pImpl->GetWorkAreaOfMonitorFromWindow(&rc);
            DWORD val = rc.right - rc.left/*::GetSystemMetrics(SM_CXSCREEN)*/;
            general.SetBinary(ctxtCXScreen, &val, sizeof(DWORD));
            val = rc.bottom - rc.top/*::GetSystemMetrics(SM_CYSCREEN)*/;
            general.SetBinary(ctxtCYScreen, &val, sizeof(DWORD));
        }

        TStorage stg;
        bool bRes = (stg.Create(stgMain, ctxtMainWindow, IStorge::ReadWrite) == ERROR_SUCCESS);

        if (bRes)
            bRes = m_pImpl->Store(stg);

//         DWORD dwDisposition;
//         CRegKey keyMain;
//         if(keyMain.Create(HKEY_CURRENT_USER,m_strMainKey.c_str(),REG_NONE,
//                                     REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ,
//                                     NULL,&dwDisposition)==ERROR_SUCCESS)
//         {
//             CRegKey keyGeneral;
//             if(keyGeneral.Create(keyMain,ctxtGeneral,REG_NONE,
//                                         REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ,
//                                         NULL,&dwDisposition)==ERROR_SUCCESS)
//             {
//
//                 DWORD val=::GetSystemMetrics(SM_CXSCREEN);
//                 ::RegSetValueEx(keyGeneral, ctxtCXScreen, NULL, REG_DWORD,
//                                 reinterpret_cast<BYTE*>(&val), sizeof(DWORD));
//                 val=::GetSystemMetrics(SM_CYSCREEN);
//                 ::RegSetValueEx(keyGeneral, ctxtCYScreen, NULL, REG_DWORD,
//                                 reinterpret_cast<BYTE*>(&val), sizeof(DWORD));
// /*
//                 keyGeneral.SetValue(::GetSystemMetrics(SM_CXSCREEN),ctxtCXScreen);
//                 keyGeneral.SetValue(::GetSystemMetrics(SM_CYSCREEN),ctxtCYScreen);
// */
//             }
//             CRegKey key;
//             if(key.Create(keyMain,ctxtMainWindow,REG_NONE,
//                             REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ,
//                             NULL,&dwDisposition)==ERROR_SUCCESS)
//                                     m_pImpl->Store(key);
//         }
        return bRes;
    }

    bool Restore(TStorage& stgMain)
    {
        TStorage general;
        bool bRes = (general.Open(stgMain, ctxtGeneral, IStorge::Read) == ERROR_SUCCESS);

        if (bRes)
        {
            RECT rc;
            m_pImpl->GetWorkAreaOfMonitorFromWindow(&rc);
            std::pair<long, long> xratio(rc.right - rc.left, 1);
            std::pair<long, long> yratio(rc.bottom - rc.top, 1);
            DWORD val;
            size_t size = sizeof(DWORD);

            if (general.GetBinary(ctxtCXScreen, &val, size) == ERROR_SUCCESS
                    && (size == sizeof(DWORD))
                    && (xratio.first != long(val)))
                xratio.second = long(val);
            else
                xratio.first = 1;

            size = sizeof(DWORD);

            if (general.GetBinary(ctxtCYScreen, &val, size) == ERROR_SUCCESS
                    && (size == sizeof(DWORD))
                    && (yratio.first != long(val)))
                yratio.second = long(val);
            else
                yratio.first = 1;

            /*
                        SIZE szScreen;
                        size_t size = sizeof(DWORD);
                        float xratio=(general.GetBinary(ctxtCXScreen,&szScreen.cx,size)==ERROR_SUCCESS
                                         && (size == sizeof(DWORD))
                                                        ?float(::GetSystemMetrics(SM_CXSCREEN))/szScreen.cx
                                                        :float(1.0));

                        size = sizeof(DWORD);
                        float yratio=(general.GetBinary(ctxtCYScreen,&szScreen.cy,size)==ERROR_SUCCESS
                                        && (size == sizeof(DWORD))
                                                        ?float(::GetSystemMetrics(SM_CYSCREEN))/szScreen.cy
                                                        :float(1.0));
            */
            TStorage stg;
            bRes = (stg.Open(stgMain, ctxtMainWindow, IStorge::Read) == ERROR_SUCCESS
                    && m_pImpl->Restore(stg, xratio, yratio));
        }

//         CRegKey keyMain;
//         CRegKey keyGeneral;
//         bool bRes=keyMain.Open(HKEY_CURRENT_USER,m_strMainKey.c_str(),KEY_READ)==ERROR_SUCCESS
//                                     && (keyGeneral.Open(keyMain,ctxtGeneral,KEY_READ)==ERROR_SUCCESS);
//         if(bRes)
//         {
//             SIZE szScreen;
//             DWORD dwCount = sizeof(DWORD);
//             float xratio=(::RegQueryValueEx(keyGeneral,ctxtCXScreen,NULL,NULL,
//                             reinterpret_cast<LPBYTE>(&szScreen.cx),&dwCount) ==ERROR_SUCCESS
//                                 && (dwCount == sizeof(DWORD)))
//                                             ?float(::GetSystemMetrics(SM_CXSCREEN))/szScreen.cx
//                                             :float(1.0);
//             dwCount = sizeof(DWORD);
//             float yratio=(::RegQueryValueEx(keyGeneral,ctxtCYScreen,NULL,NULL,
//                             reinterpret_cast<LPBYTE>(&szScreen.cy),&dwCount) ==ERROR_SUCCESS
//                                 &&(dwCount == sizeof(DWORD)))
//                                             ?float(::GetSystemMetrics(SM_CYSCREEN))/szScreen.cy
//                                             :float(1.0);
// /*
//             xratio=(keyGeneral.QueryValue(reinterpret_cast<DWORD&>(szScreen.cx),ctxtCXScreen)==ERROR_SUCCESS)
//                                             ?float(::GetSystemMetrics(SM_CXSCREEN))/szScreen.cx
//                                             :float(1.0);
//             yratio=(keyGeneral.QueryValue(reinterpret_cast<DWORD&>(szScreen.cy),ctxtCYScreen)==ERROR_SUCCESS)
//                                             ?float(::GetSystemMetrics(SM_CYSCREEN))/szScreen.cy
//                                             :float(1.0);
// */
//             CRegKey key;
//             bRes=key.Open(keyMain,ctxtMainWindow,KEY_READ)==ERROR_SUCCESS
//                     &&    m_pImpl->Restore(key,xratio,yratio);
//
//         }

        if (!bRes)
            bRes = m_pImpl->RestoreDefault();

        return bRes;
    }
    bool RestoreDefault()
    {
        return m_pImpl->RestoreDefault();
    }
protected:
    CImpl*    m_pImpl;
};

class CWindowStateAdapter
{
protected:
    class CImpl : public CStateBase<IState>
    {
    public:
        CImpl(HWND hWnd, int nDefCmdShow = SW_SHOWNA)
            : m_hWnd(hWnd), m_nDefCmdShow(nDefCmdShow)
        {
            ATLASSERT(::IsWindow(hWnd));
        }
        virtual bool Store(IStorge& stg)
        {
            WINDOWPLACEMENT wp;
            wp.length = sizeof(WINDOWPLACEMENT);
            ATLASSERT(::IsWindow(m_hWnd));
            bool bRes = false;

            if (::GetWindowPlacement(m_hWnd, &wp))
            {
                wp.flags = 0;

                if (::IsZoomed(m_hWnd))
                    wp.flags |= WPF_RESTORETOMAXIMIZED;

                /*
                                bRes=(::RegSetValueEx(key,ctxtPlacement,NULL,REG_BINARY,
                                                        reinterpret_cast<CONST BYTE *>(&wp),
                                                        sizeof(WINDOWPLACEMENT))==ERROR_SUCCESS);
                */
                bRes = (stg.SetBinary(ctxtPlacement, &wp, sizeof(WINDOWPLACEMENT)) == ERROR_SUCCESS);
            }

            return bRes;
        }
        virtual bool Restore(IStorge& stg, const std::pair<long, long>& /*xratio*/, const std::pair<long, long>& /*yratio*/)
        {
            ATLASSERT(::IsWindow(m_hWnd));
            WINDOWPLACEMENT wp;
            /*
                        DWORD dwType;
                        DWORD cbData=sizeof(WINDOWPLACEMENT);
                        bool bRes=(::RegQueryValueEx(key,ctxtPlacement,NULL,&dwType,
                                                        reinterpret_cast<LPBYTE>(&wp),&cbData)==ERROR_SUCCESS)
                                                        &&(dwType==REG_BINARY);
            */
            size_t size = sizeof(WINDOWPLACEMENT);
            bool bRes = (stg.GetBinary(ctxtPlacement, &wp, size) == ERROR_SUCCESS
                         && (size == sizeof(WINDOWPLACEMENT)));

            if (bRes)
                bRes = (::SetWindowPlacement(m_hWnd, &wp) != FALSE);

            return bRes;
        }
        virtual bool RestoreDefault()
        {
            ::ShowWindow(m_hWnd, m_nDefCmdShow);
            return true;
        }
    protected:
        HWND    m_hWnd;
        int        m_nDefCmdShow;
    };
public:
    CWindowStateAdapter(HWND hWnd, int nDefCmdShow = SW_SHOWNOACTIVATE)
    {
        m_pImpl = new CImpl(hWnd, nDefCmdShow);
    }
    ~CWindowStateAdapter()
    {
        ATLASSERT(m_pImpl);
        m_pImpl->Release();
    }
    operator IState* ()
    {
        return m_pImpl;
    }
protected:
    CImpl* m_pImpl;
};

class CToggleWindowAdapter
{
protected:
    class CImpl : public CStateBase<IState>
    {
    public:
        CImpl(HWND hWnd, int nDefCmdShow = SW_SHOWNA)
            : m_hWnd(hWnd), m_nDefCmdShow(nDefCmdShow)
        {
            ATLASSERT(::IsWindow(hWnd));
        }
        virtual bool Store(IStorge& stg)
        {
            DWORD visible = (::GetWindowLong(m_hWnd, GWL_STYLE)&WS_VISIBLE) != 0;
//            DWORD visible=::IsWindowVisible(m_hWnd);
            /*
                        return (::RegSetValueEx(key, ctxtVisible, NULL, REG_DWORD,
                                            reinterpret_cast<BYTE*>(&visible), sizeof(DWORD))==ERROR_SUCCESS);
            //            return (key.SetValue(visible,ctxtVisible)==ERROR_SUCCESS);
            */
            return (stg.SetBinary(ctxtVisible, &visible, sizeof(DWORD)) == ERROR_SUCCESS);
        }
        virtual bool Restore(IStorge& stg, const std::pair<long, long>& /*xratio*/, const std::pair<long, long>& /*yratio*/)
        {
            DWORD visible;
//          bool bRes=(key.QueryValue(visible,ctxtVisible)==ERROR_SUCCESS);
            /*
                        DWORD dwCount = sizeof(DWORD);
                        bool bRes=(::RegQueryValueEx(key,ctxtVisible,NULL,NULL,
                                            reinterpret_cast<LPBYTE>(&visible),&dwCount)==ERROR_SUCCESS
                                                 && (dwCount == sizeof(DWORD)));
            */
            size_t size = sizeof(DWORD);
            bool bRes = (stg.GetBinary(ctxtVisible, &visible, size) == ERROR_SUCCESS
                         && (size == sizeof(DWORD)));

            if (bRes)
                ::ShowWindow(m_hWnd, (visible != 0) ? SW_SHOWNA : SW_HIDE);
            else
                RestoreDefault();

            return bRes;
        }
        virtual bool RestoreDefault()
        {
            ::ShowWindow(m_hWnd, m_nDefCmdShow);
            return true;
        }
    protected:
        HWND    m_hWnd;
        int        m_nDefCmdShow;
    };
public:
    CToggleWindowAdapter(HWND hWnd, int nDefCmdShow = SW_SHOWNOACTIVATE)
    {
        m_pImpl = new CImpl(hWnd, nDefCmdShow);
    }
    ~CToggleWindowAdapter()
    {
        ATLASSERT(m_pImpl);
        m_pImpl->Release();
    }
    operator IState* ()
    {
        return m_pImpl;
    }
protected:
    CImpl* m_pImpl;
};

class CRebarStateAdapter
{
protected:
    class CImpl : public CStateBase<IState>
    {
    public:
        CImpl(HWND hWnd)
            : m_rebar(hWnd)
        {
            ATLASSERT(::IsWindow(hWnd));
        }

        virtual bool Store(IStorge& stg)
        {
            ATLASSERT(m_rebar.IsWindow());
            unsigned int bandCount = m_rebar.GetBandCount();

            for (unsigned int i = 0; i < bandCount; i++)
            {
                std::basic_ostringstream<TCHAR> sstrKey;
                sstrKey << ctxtBand << i;
                REBARBANDINFO rbi;
                ZeroMemory(&rbi, sizeof(REBARBANDINFO));
                rbi.cbSize = sizeof(REBARBANDINFO);
                rbi.fMask = RBBIM_ID | RBBIM_COLORS |
                            RBBIM_SIZE | RBBIM_STYLE
                            | RBBIM_CHILDSIZE
#if (_WIN32_IE >= 0x0400)
                            | /*RBBIM_HEADERSIZE |*/ RBBIM_IDEALSIZE
#endif
                            ;
                m_rebar.GetBandInfo(i, &rbi);
                /*
                                ::RegSetValueEx(key,sstrKey.str().c_str(),NULL,REG_BINARY,
                                                reinterpret_cast<CONST BYTE *>(&rbi),
                                                rbi.cbSize);
                */
                stg.SetBinary(sstrKey.str().c_str(), &rbi, size_t(rbi.cbSize));
            }

            return true;
        }

        virtual bool Restore(IStorge& stg, const std::pair<long, long>& /*xratio*/, const std::pair<long, long>& /*yratio*/)
        {
            unsigned int bandCount = m_rebar.GetBandCount();

            for (unsigned int i = 0; i < bandCount; i++)
            {
                std::basic_ostringstream<TCHAR> sstrKey;
                sstrKey << ctxtBand << i;
                REBARBANDINFO rbi;
                //ZeroMemory(&rbi,sizeof(REBARBANDINFO));
                /*
                                DWORD dwType;
                                DWORD cbData=sizeof(REBARBANDINFO);
                                if((::RegQueryValueEx(key,sstrKey.str().c_str(),NULL,&dwType,
                                            reinterpret_cast<LPBYTE>(&rbi),&cbData)==ERROR_SUCCESS)
                                            &&(dwType==REG_BINARY))
                */
                size_t size = sizeof(REBARBANDINFO);

                if (stg.GetBinary(sstrKey.str().c_str(), &rbi, size) == ERROR_SUCCESS
                        && (size == sizeof(REBARBANDINFO)))
                {
                    m_rebar.MoveBand(m_rebar.IdToIndex(rbi.wID), i);
                    m_rebar.SetBandInfo(i, &rbi);
                }
            }

            return true;
        }
        virtual bool RestoreDefault()
        {
            return true;
        }
    protected:
        CReBarCtrl m_rebar;
    };
public:
    CRebarStateAdapter(HWND hWnd)
    {
        m_pImpl = new CImpl(hWnd);
    }
    ~CRebarStateAdapter()
    {
        ATLASSERT(m_pImpl);
        m_pImpl->Release();
    }
    operator IState* ()
    {
        return m_pImpl;
    }
protected:
    CImpl* m_pImpl;
};

#ifdef __ATLSPLIT_H__

template <bool t_bVertical = true>
class CSplitterWindowStateAdapter
{
protected:
    template <bool t_bVertical>
    class CImplT : public CStateBase<IState>
    {
    public:
        CImplT(CSplitterWindow& splitter, int defaultPos)
            : m_splitter(splitter)
            , m_defaultPos(defaultPos)
        {
        }

        virtual bool Store(IStorge& stg)
        {
            int pos = m_splitter.GetSplitterPos();
            return (stg.SetBinary(ctxtPosition, &pos, sizeof(int)) == ERROR_SUCCESS);
        }

        virtual bool Restore(IStorge& stg, const std::pair<long, long>& xratio, const std::pair<long, long>& yratio)
        {
            int pos;
            size_t size = sizeof(int);
            bool res = (stg.GetBinary(ctxtPosition, &pos, size) == ERROR_SUCCESS
                        && (size == sizeof(int)));

            if (res)
            {
                if (t_bVertical)
                    pos =::MulDiv(pos, xratio.first, xratio.second);
                else
                    pos =::MulDiv(pos, yratio.first, yratio.second);

                m_splitter.SetSplitterPos(pos);
            }

            return true;
        }
        virtual bool RestoreDefault()
        {
            m_splitter.SetSplitterPos(m_defaultPos);
            return true;
        }
    protected:
        CSplitterWindowT<t_bVertical>& m_splitter;
        int m_defaultPos;
    };
    typedef CImplT<t_bVertical> CImpl;
public:
    CSplitterWindowStateAdapter(CSplitterWindow& splitter, int defaultPos = -1)
    {
        m_pImpl = new CImpl(splitter, defaultPos);
    }
    ~CSplitterWindowStateAdapter()
    {
        ATLASSERT(m_pImpl);
        m_pImpl->Release();
    }
    operator IState* ()
    {
        return m_pImpl;
    }
protected:
    CImpl* m_pImpl;
};

#endif // __ATLSPLIT_H__

} // namespace sstate

#endif // WTL_DW_SSTATE_H_INCLUDED_
