//
// Created by mark on 6/5/25.
//

#include <ReportsAndCharts//WChartJSDataSeries.h>

#include <Wt/Json/Value.h>
#include <Wt/Json/Object.h>

// This is a variadic template that can accept any number of types (Ts...).
// In our case, the types will be the unique types of our lambdas.
template<class... Ts>
struct overloaded : Ts... { // <--- (1) Inherits from all lambda types
    using Ts::operator()...;   // <--- (2) Brings all call operators into scope
};

// This is a "Class Template Argument Deduction" (CTAD) guide, added in C++17.
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>; // <--- (3) Makes usage clean

    WChartJSDataSeries::WChartJSDataSeries(ChartJSType type) : chartType_(type) {
        setSeriesChartJSType(chartType_);
    }

    void WChartJSDataSeries::setSeriesChartJSType(ChartJSType type) {
        // This is used primarily for mixed bar/line where you must tag the actual
        // data series.
        switch (chartType_) {
        case ChartJSType::BAR:
            seriesOptions_["type"] = Wt::Json::Value("bar");
            break;
        case ChartJSType::BUBBLE:
            seriesOptions_["type"] = Wt::Json::Value("bubble");
            break;
        case ChartJSType::DOUGHNUT:
            seriesOptions_["type"] = Wt::Json::Value("doughnut");
            break;
        case ChartJSType::PIE:
            seriesOptions_["type"] = Wt::Json::Value("pie");
            break;
        case ChartJSType::LINE:
            seriesOptions_["type"] = Wt::Json::Value("line");
            break;
        case ChartJSType::POLAR:
            seriesOptions_["type"] = Wt::Json::Value("polarArea");
            break;
        case ChartJSType::RADAR:
            seriesOptions_["type"] = Wt::Json::Value("radar");
            break;
        case ChartJSType::SCATTER:
            seriesOptions_["type"] = Wt::Json::Value("scatter");
        }
    }
    // Use the following method to add an option not covered, or one that requires
    // a depth of Json structure more than the top level depth
    void WChartJSDataSeries::addSeriesOption(const std::string& propertyName, const Wt::Json::Object& option) {
        seriesOptions_[propertyName] = option;
    }

    Wt::Json::Object WChartJSDataSeries::getJson() {
        return seriesOptions_;
    }

    void WChartJSDataSeries::disableYaxisAnimation() {
        Wt::Json::Object animations;
        Wt::Json::Object yAxis;
        Wt::Json::Object yAxisOption;
        yAxisOption["duration"] = 0;
        yAxis["y"] = Wt::Json::Value(yAxisOption);
        animations["animations"] = Wt::Json::Value(yAxis);
        seriesOptions_["animation"] = Wt::Json::Value(animations);
    }

    // --- Common Dataset Options Implementation ---

    void WChartJSDataSeries::setLineBarCircleSeriesColor(const std::string& color) {
        lineBarCircleSeriesMainColor_ = color;
        seriesOptions_["backgroundColor"] = getBackgroundColorAsJson();
    }

    void WChartJSDataSeries::setLineBarCircleSeriesColor(const std::vector<std::string>& colors) {
        lineBarCircleSeriesMainColor_ = colors;
        // Wt::Json::Array colorArray;
        // for (const auto& color : lineBarCircleSeriesMainColor_) {
        //     colorArray.push_back(color);
        // }
        seriesOptions_["backgroundColor"] = getBackgroundColorAsJson();
    }

    Wt::Json::Value WChartJSDataSeries::getBackgroundColorAsJson() const {
        // Create an instance of our visitor by passing it two lambdas.
        // One for std::string, one for std::vector<std::string>.
        auto visitor = overloaded {
            // This lambda is called if the variant holds a std::string
            [](const std::string& color) -> Wt::Json::Value {
                return Wt::Json::Value(color);
            },
            // This lambda is called if the variant holds a std::vector<std::string>
            [](const std::vector<std::string>& colors) -> Wt::Json::Value {
                Wt::Json::Array colorArray;
                for (const auto& color : colors) {
                    colorArray.push_back(Wt::Json::Value(color));
                }
                return Wt::Json::Value(colorArray);
            }
        };

        // std::visit calls the appropriate lambda from our visitor
        // based on the type currently held in lineBarCircleSeriesMainColor_.
        return std::visit(visitor, lineBarCircleSeriesMainColor_);
    }

    Wt::Json::Value WChartJSDataSeries::getBorderColorAsJson() const {
        // Create an instance of our visitor by passing it two lambdas.
        // One for std::string, one for std::vector<std::string>.
        auto visitor = overloaded {
            // This lambda is called if the variant holds a std::string
            [](const std::string& color) -> Wt::Json::Value {
                return Wt::Json::Value(color);
            },
            // This lambda is called if the variant holds a std::vector<std::string>
            [](const std::vector<std::string>& colors) -> Wt::Json::Value {
                Wt::Json::Array colorArray;
                for (const auto& color : colors) {
                    colorArray.push_back(Wt::Json::Value(color));
                }
                return Wt::Json::Value(colorArray);
            }
        };

        // std::visit calls the appropriate lambda from our visitor
        // based on the type currently held in lineBarCircleSeriesMainColor_.
        return std::visit(visitor, lineBarCircleSeriesBorderColor_);
    }

    void WChartJSDataSeries::setLineBarCircleSeriesBorderColor(const std::string& color) {
        lineBarCircleSeriesBorderColor_ = color;
        seriesOptions_["borderColor"] = getBorderColorAsJson();
    }

    void WChartJSDataSeries::setBorderWidth(int width) {
        borderWidth_ = width;
        seriesOptions_["borderWidth"] = Wt::Json::Value(width);
    }

    void WChartJSDataSeries::setClip(int clip) {
        clip_ = clip;
        seriesOptions_["clip"] = Wt::Json::Value(clip);
    }

    void WChartJSDataSeries::setHidden(bool isHidden) {
        hidden_ = isHidden;
        seriesOptions_["hidden"] = Wt::Json::Value(isHidden);
    }

    void WChartJSDataSeries::setHoverBackgroundColor(const std::string& color) {
        hoverBackgroundColor_ = color;
        seriesOptions_["hoverBackgroundColor"] = Wt::Json::Value(color);
    }

    void WChartJSDataSeries::setHoverBorderColor(const std::string& color) {
        hoverBorderColor_ = color;
        seriesOptions_["hoverBorderColor"] = Wt::Json::Value(color);
    }

    void WChartJSDataSeries::setHoverBorderWidth(int width) {
        hoverBorderWidth_ = width;
        seriesOptions_["hoverBorderWidth"] = Wt::Json::Value(width);
    }

    void WChartJSDataSeries::setIndexAxis(const std::string& axis) {
        indexAxis_ = axis;
        seriesOptions_["indexAxis"] = Wt::Json::Value(axis);
    }

    void WChartJSDataSeries::setOrder(int order) {
        order_ = order;
        seriesOptions_["order"] = Wt::Json::Value(order);
    }

    void WChartJSDataSeries::setParsing(bool shouldParse) {
        parsing_ = shouldParse;
        seriesOptions_["parsing"] = Wt::Json::Value(shouldParse);
    }

    void WChartJSDataSeries::setStack(const std::string& stackId) {
        stack_ = stackId;
        seriesOptions_["stack"] = Wt::Json::Value(stackId);
    }

    void WChartJSDataSeries::setXAxisID(const std::string& id) {
        xAxisID_ = id;
        seriesOptions_["xAxisID"] = Wt::Json::Value(id);
    }

    void WChartJSDataSeries::setYAxisID(const std::string& id) {
        yAxisID_ = id;
        seriesOptions_["yAxisID"] = Wt::Json::Value(id);
    }

    void WChartJSDataSeries::setHeaderTextPosition(HeaderText headerOrientation) {
        if (headerOrientation == HeaderText::HORIZONTAL) {
            headerTextOrientation_ = 'H';
            seriesOptions_["headerTextOrientation"] = "H";
        } else {
            seriesOptions_["headerTextOrientation"] = "V";
            headerTextOrientation_ = 'V';
        }
    }

    // --- Line Chart Specific Options Implementation ---

    void WChartJSDataSeries::setBorderCapStyle(const std::string& style) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        borderCapStyle_ = style;
        seriesOptions_["borderCapStyle"] = Wt::Json::Value(style);
    }

    void WChartJSDataSeries::setBorderDash(const Wt::Json::Array& dashPattern) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        borderDash_ = dashPattern;
        seriesOptions_["borderDash"] = Wt::Json::Array(dashPattern);
    }

    void WChartJSDataSeries::setBorderDashOffset(double offset) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        borderDashOffset_ = offset;
        seriesOptions_["borderDashOffset"] = Wt::Json::Value(offset);
    }

    void WChartJSDataSeries::setBorderJoinStyle(const std::string& style) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        borderJoinStyle_ = style;
        seriesOptions_["borderJoinStyle"] = Wt::Json::Value(style);
    }

    void WChartJSDataSeries::setCubicInterpolationMode(const std::string& mode) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        cubicInterpolationMode_ = mode;
        seriesOptions_["cubicInterpolationMode"] = Wt::Json::Value(mode);
    }

    void WChartJSDataSeries::setFill(bool fill) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        fill_ = fill;
        seriesOptions_["fill"] = Wt::Json::Value(fill);
    }

    void WChartJSDataSeries::setFill(const std::string& fill) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        fill_ = fill;
        seriesOptions_["fill"] = Wt::Json::Value(fill);
    }

    void WChartJSDataSeries::setPointBackgroundColor(const std::string& color) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        pointBackgroundColor_ = color;
        seriesOptions_["pointBackgroundColor"] = Wt::Json::Value(color);
    }

    void WChartJSDataSeries::setPointBorderColor(const std::string& color) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        pointBorderColor_ = color;
        seriesOptions_["pointBorderColor"] = Wt::Json::Value(color);
    }

    void WChartJSDataSeries::setPointBorderWidth(int width) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        pointBorderWidth_ = width;
        seriesOptions_["pointBorderWidth"] = Wt::Json::Value(width);
    }

    void WChartJSDataSeries::setPointHitRadius(int radius) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        pointHitRadius_ = radius;
        seriesOptions_["pointHitRadius"] = Wt::Json::Value(radius);
    }

    void WChartJSDataSeries::setPointHoverBackgroundColor(const std::string& color) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        pointHoverBackgroundColor_ = color;
        seriesOptions_["pointHoverBackgroundColor"] = Wt::Json::Value(color);
    }

    void WChartJSDataSeries::setPointHoverBorderColor(const std::string& color) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        pointHoverBorderColor_ = color;
        seriesOptions_["pointHoverBorderColor"] = Wt::Json::Value(color);
    }

    void WChartJSDataSeries::setPointHoverBorderWidth(int width) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        pointHoverBorderWidth_ = width;
        seriesOptions_["pointHoverBorderWidth"] = Wt::Json::Value(width);
    }

    void WChartJSDataSeries::setPointHoverRadius(int radius) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        pointHoverRadius_ = radius;
        seriesOptions_["pointHoverRadius"] = Wt::Json::Value(radius);
    }

    void WChartJSDataSeries::setPointRadius(int radius) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        pointRadius_ = radius;
        seriesOptions_["pointRadius"] = Wt::Json::Value(radius);
    }

    void WChartJSDataSeries::setPointRotation(int angle) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        pointRotation_ = angle;
        seriesOptions_["pointRotation"] = Wt::Json::Value(angle);
    }

    void WChartJSDataSeries::setPointStyle(const std::string& style) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        pointStyle_ = style;
        seriesOptions_["pointStyle"] = Wt::Json::Value(style);
    }

    void WChartJSDataSeries::setShowLine(bool show) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        showLine_ = show;
        seriesOptions_["showLine"] = Wt::Json::Value(show);
    }

    void WChartJSDataSeries::setSpanGaps(bool span) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        spanGaps_ = span;
        seriesOptions_["spanGaps"] = Wt::Json::Value(span);
    }

    void WChartJSDataSeries::setStepped(bool stepped) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        stepped_ = stepped;
        seriesOptions_["stepped"] = Wt::Json::Value(stepped);
    }

    void WChartJSDataSeries::setStepped(const std::string& stepped) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        stepped_ = stepped;
        seriesOptions_["stepped"] = Wt::Json::Value(stepped);
    }

    void WChartJSDataSeries::setTension(double tension) {
        if (chartType_ != ChartJSType::LINE) assert(false);
        tension_ = tension;
        seriesOptions_["tension"] = Wt::Json::Value(tension);
    }


    // --- Bar Chart Specific Options Implementation ---

    void WChartJSDataSeries::setBase(double baseValue) {
        if (chartType_ != ChartJSType::BAR) assert(false);
        base_ = baseValue;
        seriesOptions_["base"] = Wt::Json::Value(baseValue);
    }

    void WChartJSDataSeries::setBarPercentage(double percentage) {
        if (chartType_ != ChartJSType::BAR) assert(false);
        barPercentage_ = percentage;
        seriesOptions_["barPercentage"] = Wt::Json::Value(percentage);
    }

    void WChartJSDataSeries::setCategoryPercentage(double percentage) {
        if (chartType_ != ChartJSType::BAR) assert(false);
        categoryPercentage_ = percentage;
        seriesOptions_["categoryPercentage"] = Wt::Json::Value(percentage);
    }

    void WChartJSDataSeries::setBorderRadius(int radius) {
        if (chartType_ != ChartJSType::BAR) assert(false);
        borderRadius_ = radius;
        seriesOptions_["borderRadius"] = Wt::Json::Value(radius);
    }

    void WChartJSDataSeries::setBorderSkipped(const std::string& edge) {
        if (chartType_ != ChartJSType::BAR) assert(false);
        borderSkipped_ = edge;
        seriesOptions_["borderSkipped"] = Wt::Json::Value(edge);
    }

    void WChartJSDataSeries::setGrouped(bool isGrouped) {
        if (chartType_ != ChartJSType::BAR) assert(false);
        grouped_ = isGrouped;
        seriesOptions_["grouped"] = Wt::Json::Value(isGrouped);
    }

    void WChartJSDataSeries::setMinBarLength(int length) {
        if (chartType_ != ChartJSType::BAR) assert(false);
        minBarLength_ = length;
        seriesOptions_["minBarLength"] = Wt::Json::Value(length);
    }


    // --- Pie & Doughnut Chart Specific Options Implementation ---

    void WChartJSDataSeries::setBorderAlign(const std::string& alignment) {
        if (chartType_ != ChartJSType::PIE && chartType_ != ChartJSType::DOUGHNUT) assert(false);
        borderAlign_ = alignment;
        seriesOptions_["borderAlign"] = Wt::Json::Value(alignment);
    }

    void WChartJSDataSeries::setHoverOffset(int offset) {
        if (chartType_ != ChartJSType::PIE && chartType_ != ChartJSType::DOUGHNUT) assert(false);
        hoverOffset_ = offset;
        seriesOptions_["hoverOffset"] = Wt::Json::Value(offset);
    }

    void WChartJSDataSeries::setOffset(int offset) {
        if (chartType_ != ChartJSType::PIE && chartType_ != ChartJSType::DOUGHNUT) assert(false);
        offset_ = offset;
        seriesOptions_["offset"] = Wt::Json::Value(offset);
    }

    void WChartJSDataSeries::setSpacing(int spacing) {
        if (chartType_ != ChartJSType::PIE && chartType_ != ChartJSType::DOUGHNUT) assert(false);
        spacing_ = spacing;
        seriesOptions_["spacing"] = Wt::Json::Value(spacing);
    }

    void WChartJSDataSeries::setWeight(double weight) {
        if (chartType_ != ChartJSType::PIE && chartType_ != ChartJSType::DOUGHNUT) assert(false);
        weight_ = weight;
        seriesOptions_["weight"] = Wt::Json::Value(weight);
    }

    void WChartJSDataSeries::setCutout(const std::string& cutout) {
        if (chartType_ != ChartJSType::PIE && chartType_ != ChartJSType::DOUGHNUT) assert(false);
        cutout_ = cutout;
        seriesOptions_["cutout"] = Wt::Json::Value(cutout);
    }