///////////////////////////////////////////////////////////////////////////////
// Name:        wx/private/tlwgeom.h
// Purpose:     Declaration of platform-specific and private wxTLWGeometry.
// Author:      Vadim Zeitlin
// Created:     2018-04-29
// Copyright:   (c) 2018 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////

#ifndef _WX_PRIVATE_TLWGEOM_H_
#define _WX_PRIVATE_TLWGEOM_H_

#include "wx/display.h"
#include "wx/toplevel.h"

// ----------------------------------------------------------------------------
// wxTLWGeometryBase: abstract base class for platform-specific classes
// ----------------------------------------------------------------------------

// wxTLWGeometry contains full information about the window geometry, which may
// include things other than the obvious ones like its current position and
// size (e.g. under MSW it also stores the position of the maximized window,
// under GTK the size of non-client decorations etc). It is private to wx and
// is only used indirectly, via wxTopLevelWindow::SaveGeometry() and
// RestoreToGeometry() methods, in the public API.

class wxTLWGeometryBase
{
public:
    typedef wxTopLevelWindow::GeometryStore Store;

    wxTLWGeometryBase() = default;
    virtual ~wxTLWGeometryBase() = default;

    // Initialize from the given window.
    virtual bool GetFrom(const wxTopLevelWindow* tlw) = 0;

    // Resize the window to use this geometry.
    virtual bool ApplyTo(wxTopLevelWindow* tlw) = 0;

    // Serialize or deserialize the object by using the provided object for
    // writing/reading the values of the different fields of this object.
    virtual bool Save(Store& store) const = 0;
    virtual bool Restore(const Store& store) = 0;
};

// ----------------------------------------------------------------------------
// wxTLWGeometryGeneric: simplest possible generic implementation
// ----------------------------------------------------------------------------

// names for various persistent options
#define wxPERSIST_TLW_X "x"
#define wxPERSIST_TLW_Y "y"
#define wxPERSIST_TLW_W "w"
#define wxPERSIST_TLW_H "h"

#define wxPERSIST_TLW_MAXIMIZED "Maximized"
#define wxPERSIST_TLW_ICONIZED "Iconized"

// MSW has its own native implementation and doesn't use this class.
#ifndef __WXMSW__

class wxTLWGeometryGeneric : public wxTLWGeometryBase
{
public:
    wxTLWGeometryGeneric()
    {
        m_hasPos =
        m_hasSize =
        m_iconized =
        m_maximized = false;
    }

    virtual bool Save(Store& store) const override
    {
        if ( !store.SaveValue(wxPERSIST_TLW_X, m_rectScreen.x) ||
             !store.SaveValue(wxPERSIST_TLW_Y, m_rectScreen.y) )
            return false;

        if ( !store.SaveValue(wxPERSIST_TLW_W, m_rectScreen.width) ||
             !store.SaveValue(wxPERSIST_TLW_H, m_rectScreen.height) )
            return false;

        if ( !store.SaveValue(wxPERSIST_TLW_MAXIMIZED, m_maximized) )
            return false;

        if ( !store.SaveValue(wxPERSIST_TLW_ICONIZED, m_iconized) )
            return false;

        return true;
    }

    virtual bool Restore(const Store& store) override
    {
        m_hasPos = store.RestoreValue(wxPERSIST_TLW_X, &m_rectScreen.x) &&
                   store.RestoreValue(wxPERSIST_TLW_Y, &m_rectScreen.y);

        m_hasSize = store.RestoreValue(wxPERSIST_TLW_W, &m_rectScreen.width) &&
                    store.RestoreValue(wxPERSIST_TLW_H, &m_rectScreen.height);

        int tmp;
        if ( store.RestoreValue(wxPERSIST_TLW_MAXIMIZED, &tmp) )
            m_maximized = tmp != 0;

        if ( store.RestoreValue(wxPERSIST_TLW_ICONIZED, &tmp) )
            m_iconized = tmp != 0;

        // If we restored at least something, return true.
        return m_hasPos || m_hasSize || m_maximized || m_iconized;
    }

    virtual bool GetFrom(const wxTopLevelWindow* tlw) override
    {
        m_rectScreen = tlw->GetScreenRect();
        m_hasPos =
        m_hasSize = true;
        m_iconized = tlw->IsIconized();
        m_maximized = tlw->IsMaximized();

        return true;
    }

    virtual bool ApplyTo(wxTopLevelWindow* tlw) override
    {
        if ( m_hasPos )
        {
            // to avoid making the window completely invisible if it had been
            // shown on a monitor which was disconnected since the last run
            // (this is pretty common for notebook with external displays)
            //
            // NB: we should allow window position to be (slightly) off screen,
            //     it's not uncommon to position the window so that its upper
            //     left corner has slightly negative coordinate
            if ( wxDisplay::GetFromPoint(m_rectScreen.GetTopLeft()) != wxNOT_FOUND ||
                 (m_hasSize &&
                  wxDisplay::GetFromPoint(m_rectScreen.GetBottomRight()) != wxNOT_FOUND) )
            {
                tlw->Move(m_rectScreen.GetTopLeft(), wxSIZE_ALLOW_MINUS_ONE);
            }
            //else: should we try to adjust position/size somehow?
        }

        if ( m_hasSize )
        {
            // a previous version of the program could have saved the window
            // size which used to be big enough, but which is not big enough
            // any more for the new version, so check that the size we restore
            // doesn't cut off parts of the window
            wxSize size = m_rectScreen.GetSize();
            size.IncTo(tlw->GetBestSize());
            tlw->SetSize(size);
        }

        // note that the window can be both maximized and iconized
        if ( m_maximized )
            tlw->Maximize();

        if ( m_iconized )
            tlw->Iconize();

        return true;
    }

private:
    wxRect m_rectScreen;
    bool m_hasPos;
    bool m_hasSize;
    bool m_iconized;
    bool m_maximized;
};

#endif // !__WXMSW__

#ifdef __WXGTK__
    #include "wx/gtk/private/tlwgeom.h"
#elif defined(__WXMSW__)
    #include "wx/msw/private/tlwgeom.h"
#else
    class wxTLWGeometry : public wxTLWGeometryGeneric
    {
    };
#endif

#endif // _WX_PRIVATE_TLWGEOM_H_
