#pragma once

#include <cmath>
#include <optional>
#include <sstream>
#include <vector>

#include "application/config/ConfigValue.h"
#include "application/config/persistence/ConfigParse.h"
#include "foundation/text/StringUtils.h"

namespace ta::application::config
{
struct ConfigColor
{
    float x = 1.0f;
    float y = 1.0f;
    float z = 1.0f;
    float w = 1.0f;
};

class ColorpickerValue : public ConfigValue
{
public:
    ColorpickerValue(const ConfigColor initial, const std::string& name)
        : ConfigValue(name), value_(initial), def_(initial) {}

    [[nodiscard]] ConfigValueKind kind() const override { return ConfigValueKind::Colorpicker; }
    [[nodiscard]] std::string get() const override { return to_string(value_); }
    [[nodiscard]] std::string get_default() const override { return to_string(def_); }
    [[nodiscard]] ConfigColor get_value() const noexcept { return value_; }

protected:
    [[nodiscard]] ApplyError apply_value(const ConfigValueData& value) override
    {
        const ConfigColorData* typed = std::get_if<ConfigColorData>(&value);
        if (!typed)
            return ApplyError::InvalidType;
        if (!std::isfinite(typed->r) || !std::isfinite(typed->g) ||
            !std::isfinite(typed->b) || !std::isfinite(typed->a))
        {
            return ApplyError::InvalidValue;
        }
        value_ = {typed->r, typed->g, typed->b, typed->a};
        return ApplyError::None;
    }

    [[nodiscard]] ApplyError set_serialized(const std::string_view text) override
    {
        const auto parsed = from_string(std::string(text));
        if (!parsed)
            return ApplyError::DecodeFailed;
        value_ = *parsed;
        return ApplyError::None;
    }

private:
    ConfigColor value_;
    ConfigColor def_;

    static std::string to_string(const ConfigColor value)
    {
        std::stringstream stream;
        stream << value.x << "|" << value.y << "|" << value.z << "|" << value.w;
        return stream.str();
    }

    static std::optional<ConfigColor> from_string(const std::string& text)
    {
        const std::vector<std::string> elements = ta::foundation::text::split(text, '|');
        if (elements.size() != 4)
            return std::nullopt;
        const auto x = parse::floating(elements[0]);
        const auto y = parse::floating(elements[1]);
        const auto z = parse::floating(elements[2]);
        const auto w = parse::floating(elements[3]);
        if (!x || !y || !z || !w)
            return std::nullopt;
        return ConfigColor{*x, *y, *z, *w};
    }
};
}
