//
// Created by Alan Freitas on 2020-07-04.
//

#ifndef MATPLOTPLUSPLUS_BOX_CHART_H
#define MATPLOTPLUSPLUS_BOX_CHART_H

#include <matplot/detail/config.h>

#include <array>
#include <cmath>
#include <map>
#include <matplot/core/figure_type.h>
#include <matplot/util/concepts.h>
#include <matplot/util/handle_types.h>

#include <matplot/core/axes_object.h>
#include <matplot/core/line_spec.h>
#include <matplot/util/common.h>

namespace matplot {
    class axes_type;
    class MATPLOT_EXPORTS box_chart : public axes_object {
      public:
        enum class box_style_option {
            outline, // unfilled / dashed whiskers
            filled,  // filled / solid whiskers
            violin,  // show data distribution
        };

      public:
        explicit box_chart(class axes_type *parent);
        box_chart(class axes_type *parent, const std::vector<double> &y_data,
                  const std::vector<double> &groups = {});
        //        box_chart(class xlim* parent, const std::vector<double>& data,
        //        const std::vector<double>& edges, enum
        //        box_chart::normalization normalization_alg =
        //        box_chart::normalization::count); box_chart(class xlim*
        //        parent, const std::vector<double>& data, binning_algorithm
        //        algorithm = binning_algorithm::automatic,
        //        box_chart::normalization normalization_alg =
        //        box_chart::normalization::count);

        /// If we receive an axes_handle, we can convert it to a raw
        /// pointer because there is no ownership involved here
        template <class... Args>
        box_chart(const axes_handle &parent, Args&&... args)
            : box_chart(parent.get(), std::forward<Args>(args)...) {}

        virtual ~box_chart() = default;
      public /* xlim object virtual functions */:
        std::string set_variables_string() override;
        std::string plot_string() override;
        // std::string legend_string(const std::string& title) override;
        std::string data_string() override;
        std::string unset_variables_string() override;
        bool requires_colormap() override;
        // double xmax() override;
        // double xmin() override;
        // double ymax() override;
        // double ymin() override;
        enum axes_object::axes_category axes_category() override;

      public /* useful functions for box_charts */:
      public /* getters and setters */:
        const std::vector<double> &y_data() const;

        class box_chart &y_data(const std::vector<double> &y_data);

        const std::vector<double> &x_data() const;

        class box_chart &x_data(const std::vector<double> &x_data);

        const color_array &face_color() const;

        class box_chart &face_color(const color_array &face_color);

        bool manual_face_color() const;

        class box_chart &manual_face_color(bool manual_face_color);

        const color_array &edge_color() const;

        class box_chart &edge_color(const color_array &edge_color);

        float edge_width() const;

        class box_chart &edge_width(float edge_width);

        enum line_spec::marker_style whisker_style() const;

        class box_chart &
        whisker_style(enum line_spec::marker_style whisker_style);

        float whisker_size() const;

        class box_chart &whisker_size(float whisker_size);

        bool whisker_face() const;

        class box_chart &whisker_face(bool whisker_face);

        bool jitter_outliers() const;

        class box_chart &jitter_outliers(bool jitter_outliers);

        float box_width() const;

        class box_chart &box_width(float box_width);

        float cap_size() const;

        class box_chart &cap_size(float cap_size);

      public /* getters and setters bypassing the line_spec */:
        box_style_option box_style() const;
        class box_chart &box_style(box_style_option box_style);

      private /* helper functions to generate the plot */:
        void maybe_update_face_color();

      protected:
        // sample data
        std::vector<double> y_data_;

        // sample groups (same length as y_data_)
        // this describes the groups and the positions at the same time
        std::vector<double> x_data_;

        // color and style
        color_array face_color_{{0.4f, 0, 0, 0}};
        bool manual_face_color_{false};
        color_array edge_color_{0, 0, 0, 0};
        float edge_width_{0.5};
        enum line_spec::marker_style whisker_style_{
            line_spec::marker_style::circle};
        float whisker_size_{1.};
        bool whisker_face_{false};
        bool jitter_outliers_{false};
        float box_width_{0.5};
        box_style_option box_style_{box_style_option::filled};
        float cap_size_{3.};
    };

} // namespace matplot

#endif // MATPLOTPLUSPLUS_BOX_CHART_H
