// -*- C++ -*-
#ifndef _Workspace_h_
#define _Workspace_h_

#include "LayoutTree.h"
#include "GenerateCodeDlg.h"

#include <GG/Button.h>
#include <GG/Menu.h>
#include <GG/Layout.h>
#include <GG/Wnd.h>


class Workspace : public GG::Wnd
{
public:
    Workspace();

    virtual void LClick(const GG::Pt& pt, Uint32 keys);

    mutable boost::signal<void ()> ClickedSignal;
};


class Resizer;
class Dialog : public GG::Wnd
{
public:
    Dialog(int w, int h);
    ~Dialog();

    void AddWnd(GG::Wnd* wnd, const std::string& name, const std::string& type_str);
    void New();
    void Load(const std::string& filename);
    void Save(const std::string& filename);
    void Export(const std::string& base_filename,
                GenerateCodeDlg::ExportFiles exported_files,
                const std::string& header_extension,
                const std::string& impl_extension,
                const std::string& sketch_extension);
    void WndRenamedSlot(GG::Wnd* wnd, const std::string& name);
    void WndEditedSlot(GG::Wnd* wnd);
    void WorkspaceClickedSlot();

    virtual void   Render();
    virtual void   LButtonDown(const GG::Pt& pt, Uint32 keys);
    virtual void   LDrag(const GG::Pt& pt, const GG::Pt& move, Uint32 keys);
    virtual void   LButtonUp(const GG::Pt& pt, Uint32 keys);
    virtual void   LClick(const GG::Pt& pt, Uint32 keys);
    virtual void   RButtonDown(const GG::Pt& pt, Uint32 keys);

    virtual GG::Pt ClientUpperLeft() const;
    virtual GG::Pt ClientLowerRight() const;
    virtual void   AcceptDrops(std::list<GG::Wnd*>& wnds, const GG::Pt& pt);

    void DoHorizontalLayout();
    void DoVerticalLayout();
    void DoGridLayout();
    void DoRemoveLayout();

    void GroupHorizontalLayout();
    void GroupVerticalLayout();
    void GroupGridLayout();
    void GroupRemoveLayout();

    void RemoveRadioButtonGroup();

    mutable boost::signal<void (GG::Wnd*, const std::string&)> EditableWndSignal;
    mutable boost::signal<void (const std::string&, const GG::Pt&)> DroppedWndSignal;

    static void SetColor(const GG::Clr& color);

protected:
    virtual bool EventFilter(Wnd* w, const Event& event);

private:
    struct WndData
    {
        WndData();
        WndData(const std::string& name, const std::string& type_str);
        std::string m_name;
        std::string m_type_str;
        Resizer*    m_resizer;
    };

    typedef GG::Wnd* WndPtr;
    typedef std::map<WndPtr, WndData> SnapshotWnds;

    struct Snapshot
    {
        Snapshot();
        std::string              m_name;
        GG::Layout*              m_layout;
        SnapshotWnds             m_snapshot_wnds;
    };

    enum LayoutType {
        VERT,
        HORZ,
        GRID
    };

    std::string SaveToString();
    WndPtr WndUnderPt(const GG::Pt& pt, bool shift_pressed);
    WndPtr WndUnderPtWithinWnd(Wnd* wnd, const GG::Pt& pt);
    Resizer* NewResizer(WndPtr wnd);
    void DeleteResizer(Resizer* resizer);
    void DeleteWnd(WndPtr wnd, std::map<WndPtr, WndData>& wnds);
    void ClearResizers();
    void ClearWnds();
    void ClearSnapshots(int from_idx = 0);
    void ClearClipboard();
    void ResizerReselectedSlot(Resizer* resizer);
    void ResizerDeselectedSlot(Resizer* resizer);
    bool Clear();
    bool Cut();
    bool Copy();
    bool Paste();
    void Copy(bool delete_copies);
    void AddCloneTree(WndPtr wnd, WndPtr clone,
                      std::map<WndPtr, WndData>& src,
                      std::map<WndPtr, WndData>& dst,
                      bool create_resizers);
    void TakeSnapshot(const std::string& wnd_editor_wnd_name);
    void RestoreToSnapshot(int idx, bool undo);
    bool Undo();
    bool Redo();
    void DoLayout(LayoutType type);
    void GroupLayout(LayoutType type);
    void EmitEditableWndSignal();
    void FilterChildren();
    void UnfilterChildren();
    void ChildifyControls();
    void DechildifyControls();
    void EnableOutlines(bool enable);
    std::set<WndPtr> FindTopLevelWnds(const std::map<WndPtr, WndData>& all_wnds);
    std::set<WndPtr> FindTopLevelSelectedWnds();
    template <class T>
    void VisitDialogLayouts(T functor, bool recurse_into_radio_button_groups);
    template <class T>
    void VisitChildLayouts(T functor, bool recurse_into_radio_button_groups);
    template <class T>
    void VisitLayoutStack(T functor, std::vector<GG::Layout*>& layout_stack, bool recurse_into_radio_button_groups);
    GG::MenuItem ContextMenu(Dialog* dialog);
    GG::MenuItem ContextMenu(Dialog* dialog, Resizer* resizer);

    std::set<Resizer*> m_resizers;
    std::map<WndPtr, WndData> m_wnds;
    WndPtr m_button_down_wnd;
    GG::Pt m_drag_origin;
    GG::Pt m_drag_offset;
    GG::Pt m_drag_position;
    Resizer* m_dragged_resizer;
    GG::Pt m_resizer_drag_offset;
    SnapshotWnds m_clipboard;
    std::vector<Snapshot> m_undo_history;
    int m_undo_position;
    LayoutTree m_layout_tree;

    static GG::Clr s_color;
};

// template implementations
template <class T>
void Dialog::VisitDialogLayouts(T functor, bool recurse_into_radio_button_groups)
{
    std::vector<GG::Layout*> layout_stack;
    if (GG::Layout* layout = GetLayout())
        layout_stack.push_back(layout);
    VisitLayoutStack(functor, layout_stack, recurse_into_radio_button_groups);
}

template <class T>
void Dialog::VisitChildLayouts(T functor, bool recurse_into_radio_button_groups)
{
    std::vector<GG::Layout*> layout_stack;
    for (std::map<WndPtr, WndData>::iterator it = m_wnds.begin(); it != m_wnds.end(); ++it) {
        GG::Layout* layout = dynamic_cast<GG::Layout*>(it->first);
        if (layout && (recurse_into_radio_button_groups || !dynamic_cast<GG::RadioButtonGroup*>(layout->Parent())))
            layout_stack.push_back(layout);
    }
    VisitLayoutStack(functor, layout_stack, recurse_into_radio_button_groups);
}

template <class T>
void Dialog::VisitLayoutStack(T functor, std::vector<GG::Layout*>& layout_stack, bool recurse_into_radio_button_groups)
{
    while (!layout_stack.empty()) {
        GG::Layout* layout = layout_stack.back();
        layout_stack.pop_back();
        functor(layout);
        for (std::list<GG::Wnd*>::const_iterator it = layout->Children().begin(); it != layout->Children().end(); ++it) {
            GG::Layout* sub_layout = dynamic_cast<GG::Layout*>(*it);
            if (sub_layout && (recurse_into_radio_button_groups || !dynamic_cast<GG::RadioButtonGroup*>(layout->Parent())))
                layout_stack.push_back(sub_layout);
        }
    }
    std::set<WndPtr> top_level_wnds = FindTopLevelWnds(m_wnds);
    m_layout_tree.Reset(this, top_level_wnds);
}

#endif // _Workspace_h_
