/*
 * Copyright (c) 2023, Torsten Engelmann <engelTorsten@gmx.de>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <LibGUI/Frame.h>
#include <LibGUI/Label.h>
#include <LibGUI/LabelWithEventDispatcher.h>
#include <LibGUI/Window.h>

namespace GUI {

class DynamicWidgetContainer : public Frame {
    C_OBJECT(DynamicWidgetContainer);

public:
    enum class ViewState {
        Expanded,
        Collapsed,
        Detached
    };

    enum class MoveTargetOperation {
        SetTarget,
        ClearAllTargets
    };

    ViewState view_state() const { return m_view_state; }
    void set_view_state(ViewState);
    StringView section_label() const& { return m_section_label; }
    void set_section_label(String);
    StringView config_domain() const& { return m_config_domain; }
    void set_config_domain(String);
    bool persist_state() const { return !m_config_domain.is_empty(); }
    void set_detached_size(Gfx::IntSize const);
    Gfx::IntSize detached_size() const { return m_detached_size.value(); }
    bool has_detached_size() { return m_detached_size.has_value(); }
    void set_container_with_individual_order(bool);
    bool is_container_with_individual_order() const { return m_is_container_with_individual_order; }
    void set_show_controls(bool);
    bool show_controls() const { return m_show_controls; }
    void set_parent_container(RefPtr<GUI::DynamicWidgetContainer>);
    bool check_has_move_target(Gfx::IntPoint, MoveTargetOperation);
    // FIXME: this should not be a public api and static - but currently the destructor is not being called when the widget was created via a gml file.
    static void close_all_detached_windows();
    virtual ~DynamicWidgetContainer() override;

protected:
    explicit DynamicWidgetContainer(Gfx::Orientation = Gfx::Orientation::Vertical);
    virtual void paint_event(PaintEvent&) override {};
    virtual void second_paint_event(PaintEvent&) override;
    virtual void resize_event(ResizeEvent&) override;
    virtual void child_event(Core::ChildEvent&) override;

    template<typename Callback>
    void for_each_child_container(Callback callback);
    Vector<GUI::DynamicWidgetContainer&> child_containers() const;

private:
    struct RelevantSizes {
        UISize preferred_size;
        UISize min_size;
    };
    ViewState m_view_state = ViewState::Expanded;
    String m_section_label;
    String m_config_domain;
    bool m_is_container_with_individual_order { false };
    bool m_persist_state { false };
    bool m_is_dragging { false };
    bool m_render_as_move_target { false };
    bool m_show_controls { true };
    Gfx::IntPoint m_drag_start_location;
    Gfx::IntPoint m_current_mouse_position;
    RefPtr<GUI::Widget> m_controls_widget;
    RefPtr<GUI::LabelWithEventDispatcher> m_label_widget;
    Gfx::IntRect m_move_widget_knurl = { 0, 0, 16, 16 };
    Optional<NonnullRefPtr<GUI::Window>> m_detached_widgets_window;
    Optional<Gfx::FrameStyle> m_previous_frame_style;
    Optional<RelevantSizes> m_dimensions_before_collapse;
    Optional<Gfx::IntSize> m_detached_size;
    RefPtr<GUI::DynamicWidgetContainer> m_parent_container;
    static Vector<NonnullRefPtr<GUI::Window>> s_open_windows;

    ErrorOr<void> detach_widgets();
    void restore_view_state();
    void register_open_window(NonnullRefPtr<GUI::Window>);
    void unregister_open_window(NonnullRefPtr<GUI::Window>);
    void set_render_as_move_target(bool);
    void swap_widget_positions(NonnullRefPtr<Core::EventReceiver> source, Gfx::IntPoint destination_positon);

    void handle_mousemove_event(MouseEvent&);
    void handle_mouseup_event(MouseEvent&);
    void handle_doubleclick_event(MouseEvent&);
    void update_control_button_visibility();
};
}