#pragma once

#include "render_2D/axis/Axis.h"
#include "render_2D/axis/Frequency_Axis.h"
#include "render_2D/axis/Time_Axis.h"
#include "render_2D/plottable/Afterglow.h"
#include "render_2D/plottable/Constellation_Diagram.h"
#include "render_2D/plottable/Frequency_Trace.h"
#include "render_2D/plottable/Selection_Rectangle_Overlay.h"
#include "render_2D/plottable/Spectrum.h"
#include "render_2D/plottable/Sweep_Spectrum.h"
#include "render_2D/plottable/Waterfall.h"
#include <structive/property/accessor.hpp>

#include <type_traits>
#include <utility>

namespace renderive::web {

template <class Object, auto Member>
struct Gallery_Property_Accessor {
    using object_type = Object;
    using Properties = typename Object::Properties;
    using value_type = std::remove_cvref_t<decltype(std::declval<Properties>().*Member)>;
    using storage_identity = void;
    using dependency_spec = structive::No_Property_Dependencies;
    static constexpr bool readable = true;
    static constexpr bool writable = true;
    static constexpr bool synchronized_view_read = false;
    static constexpr bool trusted_object_access = true;

    value_type read(const Object& object) const {
        return object.template adminive_read<Member>();
    }
    void write(Object& object, value_type value) const {
        object.template adminive_write<Member>(std::move(value));
    }
};

template <class Base>
class Gallery_Plottable : public Base {
public:
    using Properties = typename Base::Properties;
    using Base::Base;

private:
    template <auto Member>
    auto adminive_read() const {
        return this->template property_value<Member>();
    }
    template <auto Member, class Value>
    void adminive_write(Value&& value) {
        this->template set<Member>(std::forward<Value>(value));
    }
    template <class Object, auto Member>
    friend struct Gallery_Property_Accessor;
};

class Gallery_Spectrum final : public Gallery_Plottable<Spectrum> {
public:
    using Gallery_Plottable::Gallery_Plottable;
    using Builder = Renderable_Builder<Gallery_Spectrum, Properties>;
};

class Gallery_Waterfall final : public Gallery_Plottable<Waterfall> {
public:
    using Gallery_Plottable::Gallery_Plottable;
    using Builder = Renderable_Builder<Gallery_Waterfall, Properties>;
};

class Gallery_Afterglow final : public Gallery_Plottable<Afterglow> {
public:
    using Gallery_Plottable::Gallery_Plottable;
    using Builder = Renderable_Builder<Gallery_Afterglow, Properties>;
};

class Gallery_Sweep_Spectrum final : public Gallery_Plottable<Sweep_Spectrum> {
public:
    using Gallery_Plottable::Gallery_Plottable;
    using Builder = Renderable_Builder<Gallery_Sweep_Spectrum, Properties>;
};

class Gallery_Frequency_Trace final : public Gallery_Plottable<Frequency_Trace> {
public:
    using Gallery_Plottable::Gallery_Plottable;
    using Builder = Renderable_Builder<Gallery_Frequency_Trace, Properties>;
};

class Gallery_Selection_Rectangle_Overlay final
    : public Gallery_Plottable<Selection_Rectangle_Overlay> {
public:
    using Gallery_Plottable::Gallery_Plottable;
    using Builder = Renderable_Builder<Gallery_Selection_Rectangle_Overlay, Properties>;
};

class Gallery_Constellation_Diagram final
    : public Gallery_Plottable<Constellation_Diagram> {
public:
    using Gallery_Plottable::Gallery_Plottable;
    using Builder = Renderable_Builder<Gallery_Constellation_Diagram, Properties>;
};

template <class Base, class Properties_Type>
class Gallery_Axis_Base : public Base {
public:
    using Properties = Properties_Type;
    using Base::Base;

private:
    template <auto Member>
    auto adminive_read() const {
        return this->read([](const auto& state) {
            return state.*Member;
        });
    }
    template <auto Member, class Value>
    void adminive_write(Value&& value) {
        this->template set<Member>(std::forward<Value>(value));
    }
    template <class Object, auto Member>
    friend struct Gallery_Property_Accessor;
};

class Gallery_Axis final : public Gallery_Axis_Base<Axis, Axis_Properties> {
public:
    using Gallery_Axis_Base::Gallery_Axis_Base;
    using Builder = detail::Axis_Renderable_Builder<Gallery_Axis, Properties>;
};

class Gallery_Frequency_Axis final
    : public Gallery_Axis_Base<Frequency_Axis, Axis_Properties> {
public:
    using Gallery_Axis_Base::Gallery_Axis_Base;
    using Builder = detail::Axis_Renderable_Builder<Gallery_Frequency_Axis, Properties>;
};

class Gallery_Time_Axis final
    : public Gallery_Axis_Base<Time_Axis, Time_Axis_Properties> {
public:
    using Gallery_Axis_Base::Gallery_Axis_Base;
    using Builder = detail::Axis_Renderable_Builder<Gallery_Time_Axis, Properties>;
};

} // namespace renderive::web
