#include "UIArc.h"
#include "McRFPy_API.h"
#include <cmath>
#include <sstream>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

UIArc::UIArc()
    : center(0.0f, 0.0f), radius(0.0f), start_angle(0.0f), end_angle(90.0f),
      color(sf::Color::White), thickness(1.0f), vertices_dirty(true)
{
    position = center;
}

UIArc::UIArc(sf::Vector2f center, float radius, float startAngle, float endAngle,
             sf::Color color, float thickness)
    : center(center), radius(radius), start_angle(startAngle), end_angle(endAngle),
      color(color), thickness(thickness), vertices_dirty(true)
{
    position = center;
}

UIArc::UIArc(const UIArc& other)
    : UIDrawable(other),
      center(other.center),
      radius(other.radius),
      start_angle(other.start_angle),
      end_angle(other.end_angle),
      color(other.color),
      thickness(other.thickness),
      vertices_dirty(true)
{
}

UIArc& UIArc::operator=(const UIArc& other) {
    if (this != &other) {
        UIDrawable::operator=(other);
        center = other.center;
        radius = other.radius;
        start_angle = other.start_angle;
        end_angle = other.end_angle;
        color = other.color;
        thickness = other.thickness;
        vertices_dirty = true;
    }
    return *this;
}

UIArc::UIArc(UIArc&& other) noexcept
    : UIDrawable(std::move(other)),
      center(other.center),
      radius(other.radius),
      start_angle(other.start_angle),
      end_angle(other.end_angle),
      color(other.color),
      thickness(other.thickness),
      vertices(std::move(other.vertices)),
      vertices_dirty(other.vertices_dirty)
{
}

UIArc& UIArc::operator=(UIArc&& other) noexcept {
    if (this != &other) {
        UIDrawable::operator=(std::move(other));
        center = other.center;
        radius = other.radius;
        start_angle = other.start_angle;
        end_angle = other.end_angle;
        color = other.color;
        thickness = other.thickness;
        vertices = std::move(other.vertices);
        vertices_dirty = other.vertices_dirty;
    }
    return *this;
}

void UIArc::rebuildVertices() {
    vertices.clear();
    vertices.setPrimitiveType(sf::TriangleStrip);

    // Calculate the arc parameters
    float inner_radius = radius - thickness / 2.0f;
    float outer_radius = radius + thickness / 2.0f;

    if (inner_radius < 0) inner_radius = 0;

    // Normalize angles
    float start_rad = start_angle * M_PI / 180.0f;
    float end_rad = end_angle * M_PI / 180.0f;

    // Calculate number of segments based on arc length
    float angle_span = end_rad - start_rad;
    int num_segments = std::max(3, static_cast<int>(std::abs(angle_span * radius) / 5.0f));
    num_segments = std::min(num_segments, 100);  // Cap at 100 segments

    float angle_step = angle_span / num_segments;

    // Apply opacity to color
    sf::Color render_color = color;
    render_color.a = static_cast<sf::Uint8>(render_color.a * opacity);

    // Build the triangle strip
    for (int i = 0; i <= num_segments; ++i) {
        float angle = start_rad + i * angle_step;
        float cos_a = std::cos(angle);
        float sin_a = std::sin(angle);

        // Inner vertex
        sf::Vector2f inner_pos(
            center.x + inner_radius * cos_a,
            center.y + inner_radius * sin_a
        );
        vertices.append(sf::Vertex(inner_pos, render_color));

        // Outer vertex
        sf::Vector2f outer_pos(
            center.x + outer_radius * cos_a,
            center.y + outer_radius * sin_a
        );
        vertices.append(sf::Vertex(outer_pos, render_color));
    }

    vertices_dirty = false;
}

void UIArc::render(sf::Vector2f offset, sf::RenderTarget& target) {
    if (!visible) return;

    if (vertices_dirty) {
        rebuildVertices();
    }

    // Apply offset by creating a transformed copy
    sf::Transform transform;
    transform.translate(offset);
    target.draw(vertices, transform);
}

UIDrawable* UIArc::click_at(sf::Vector2f point) {
    if (!visible) return nullptr;

    // Calculate distance from center
    float dx = point.x - center.x;
    float dy = point.y - center.y;
    float dist = std::sqrt(dx * dx + dy * dy);

    // Check if within the arc's radial range
    float inner_radius = radius - thickness / 2.0f;
    float outer_radius = radius + thickness / 2.0f;
    if (inner_radius < 0) inner_radius = 0;

    if (dist < inner_radius || dist > outer_radius) {
        return nullptr;
    }

    // Check if within the angle range
    float angle = std::atan2(dy, dx) * 180.0f / M_PI;

    // Normalize angle to match start/end angle range
    float start = start_angle;
    float end = end_angle;

    // Handle angle wrapping
    while (angle < start - 180.0f) angle += 360.0f;
    while (angle > start + 180.0f) angle -= 360.0f;

    if ((start <= end && angle >= start && angle <= end) ||
        (start > end && (angle >= start || angle <= end))) {
        return this;
    }

    return nullptr;
}

PyObjectsEnum UIArc::derived_type() {
    return PyObjectsEnum::UIARC;
}

sf::FloatRect UIArc::get_bounds() const {
    float outer_radius = radius + thickness / 2.0f;
    return sf::FloatRect(
        center.x - outer_radius,
        center.y - outer_radius,
        outer_radius * 2,
        outer_radius * 2
    );
}

void UIArc::move(float dx, float dy) {
    center.x += dx;
    center.y += dy;
    position = center;
    vertices_dirty = true;
}

void UIArc::resize(float w, float h) {
    // Resize by adjusting radius to fit in the given dimensions
    radius = std::min(w, h) / 2.0f - thickness / 2.0f;
    if (radius < 0) radius = 0;
    vertices_dirty = true;
}

// Property setters
bool UIArc::setProperty(const std::string& name, float value) {
    if (name == "radius") {
        setRadius(value);
        markDirty();  // #144 - Content change
        return true;
    }
    else if (name == "start_angle") {
        setStartAngle(value);
        markDirty();  // #144 - Content change
        return true;
    }
    else if (name == "end_angle") {
        setEndAngle(value);
        markDirty();  // #144 - Content change
        return true;
    }
    else if (name == "thickness") {
        setThickness(value);
        markDirty();  // #144 - Content change
        return true;
    }
    else if (name == "x") {
        center.x = value;
        position = center;
        vertices_dirty = true;
        markCompositeDirty();  // #144 - Position change, texture still valid
        return true;
    }
    else if (name == "y") {
        center.y = value;
        position = center;
        vertices_dirty = true;
        markCompositeDirty();  // #144 - Position change, texture still valid
        return true;
    }
    return false;
}

bool UIArc::setProperty(const std::string& name, const sf::Color& value) {
    if (name == "color") {
        setColor(value);
        markDirty();  // #144 - Content change
        return true;
    }
    return false;
}

bool UIArc::setProperty(const std::string& name, const sf::Vector2f& value) {
    if (name == "center") {
        setCenter(value);
        markCompositeDirty();  // #144 - Position change, texture still valid
        return true;
    }
    return false;
}

bool UIArc::getProperty(const std::string& name, float& value) const {
    if (name == "radius") {
        value = radius;
        return true;
    }
    else if (name == "start_angle") {
        value = start_angle;
        return true;
    }
    else if (name == "end_angle") {
        value = end_angle;
        return true;
    }
    else if (name == "thickness") {
        value = thickness;
        return true;
    }
    else if (name == "x") {
        value = center.x;
        return true;
    }
    else if (name == "y") {
        value = center.y;
        return true;
    }
    return false;
}

bool UIArc::getProperty(const std::string& name, sf::Color& value) const {
    if (name == "color") {
        value = color;
        return true;
    }
    return false;
}

bool UIArc::getProperty(const std::string& name, sf::Vector2f& value) const {
    if (name == "center") {
        value = center;
        return true;
    }
    return false;
}

// Python API implementation
PyObject* UIArc::get_center(PyUIArcObject* self, void* closure) {
    auto center = self->data->getCenter();
    auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
    if (!type) return NULL;
    PyObject* result = PyObject_CallFunction((PyObject*)type, "ff", center.x, center.y);
    Py_DECREF(type);
    return result;
}

int UIArc::set_center(PyUIArcObject* self, PyObject* value, void* closure) {
    PyVectorObject* vec = PyVector::from_arg(value);
    if (!vec) {
        PyErr_Clear();
        PyErr_SetString(PyExc_TypeError, "center must be a Vector or tuple (x, y)");
        return -1;
    }
    self->data->setCenter(vec->data);
    return 0;
}

PyObject* UIArc::get_radius(PyUIArcObject* self, void* closure) {
    return PyFloat_FromDouble(self->data->getRadius());
}

int UIArc::set_radius(PyUIArcObject* self, PyObject* value, void* closure) {
    if (!PyNumber_Check(value)) {
        PyErr_SetString(PyExc_TypeError, "radius must be a number");
        return -1;
    }
    self->data->setRadius(static_cast<float>(PyFloat_AsDouble(value)));
    return 0;
}

PyObject* UIArc::get_start_angle(PyUIArcObject* self, void* closure) {
    return PyFloat_FromDouble(self->data->getStartAngle());
}

int UIArc::set_start_angle(PyUIArcObject* self, PyObject* value, void* closure) {
    if (!PyNumber_Check(value)) {
        PyErr_SetString(PyExc_TypeError, "start_angle must be a number");
        return -1;
    }
    self->data->setStartAngle(static_cast<float>(PyFloat_AsDouble(value)));
    return 0;
}

PyObject* UIArc::get_end_angle(PyUIArcObject* self, void* closure) {
    return PyFloat_FromDouble(self->data->getEndAngle());
}

int UIArc::set_end_angle(PyUIArcObject* self, PyObject* value, void* closure) {
    if (!PyNumber_Check(value)) {
        PyErr_SetString(PyExc_TypeError, "end_angle must be a number");
        return -1;
    }
    self->data->setEndAngle(static_cast<float>(PyFloat_AsDouble(value)));
    return 0;
}

PyObject* UIArc::get_color(PyUIArcObject* self, void* closure) {
    auto color = self->data->getColor();
    auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color");
    PyObject* args = Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a);
    PyObject* obj = PyObject_CallObject((PyObject*)type, args);
    Py_DECREF(args);
    Py_DECREF(type);
    return obj;
}

int UIArc::set_color(PyUIArcObject* self, PyObject* value, void* closure) {
    auto color = PyColor::from_arg(value);
    if (!color) {
        PyErr_SetString(PyExc_TypeError, "color must be a Color or tuple (r, g, b) or (r, g, b, a)");
        return -1;
    }
    self->data->setColor(color->data);
    return 0;
}

PyObject* UIArc::get_thickness(PyUIArcObject* self, void* closure) {
    return PyFloat_FromDouble(self->data->getThickness());
}

int UIArc::set_thickness(PyUIArcObject* self, PyObject* value, void* closure) {
    if (!PyNumber_Check(value)) {
        PyErr_SetString(PyExc_TypeError, "thickness must be a number");
        return -1;
    }
    self->data->setThickness(static_cast<float>(PyFloat_AsDouble(value)));
    return 0;
}

// Required typedef for UIDRAWABLE_GETSETTERS and UIDRAWABLE_METHODS macro templates
typedef PyUIArcObject PyObjectType;

PyGetSetDef UIArc::getsetters[] = {
    {"center", (getter)UIArc::get_center, (setter)UIArc::set_center,
     "Center position of the arc", NULL},
    {"radius", (getter)UIArc::get_radius, (setter)UIArc::set_radius,
     "Arc radius in pixels", NULL},
    {"start_angle", (getter)UIArc::get_start_angle, (setter)UIArc::set_start_angle,
     "Starting angle in degrees", NULL},
    {"end_angle", (getter)UIArc::get_end_angle, (setter)UIArc::set_end_angle,
     "Ending angle in degrees", NULL},
    {"color", (getter)UIArc::get_color, (setter)UIArc::set_color,
     "Arc color", NULL},
    {"thickness", (getter)UIArc::get_thickness, (setter)UIArc::set_thickness,
     "Line thickness", NULL},
    {"on_click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click,
     "Callable executed when arc is clicked.", (void*)PyObjectsEnum::UIARC},
    {"z_index", (getter)UIDrawable::get_int, (setter)UIDrawable::set_int,
     "Z-order for rendering (lower values rendered first).", (void*)PyObjectsEnum::UIARC},
    {"name", (getter)UIDrawable::get_name, (setter)UIDrawable::set_name,
     "Name for finding this element.", (void*)PyObjectsEnum::UIARC},
    {"pos", (getter)UIDrawable::get_pos, (setter)UIDrawable::set_pos,
     "Position as a Vector (same as center).", (void*)PyObjectsEnum::UIARC},
    UIDRAWABLE_GETSETTERS,
    UIDRAWABLE_PARENT_GETSETTERS(PyObjectsEnum::UIARC),
    {NULL}
};

PyMethodDef UIArc_methods[] = {
    UIDRAWABLE_METHODS,
    {NULL}
};

PyObject* UIArc::repr(PyUIArcObject* self) {
    std::ostringstream oss;
    if (!self->data) {
        oss << "<Arc (invalid internal object)>";
    } else {
        auto center = self->data->getCenter();
        auto color = self->data->getColor();
        oss << "<Arc center=(" << center.x << ", " << center.y << ") "
            << "radius=" << self->data->getRadius() << " "
            << "angles=(" << self->data->getStartAngle() << ", " << self->data->getEndAngle() << ") "
            << "color=(" << (int)color.r << ", " << (int)color.g << ", "
            << (int)color.b << ", " << (int)color.a << ")>";
    }
    std::string repr_str = oss.str();
    return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}

int UIArc::init(PyUIArcObject* self, PyObject* args, PyObject* kwds) {
    // Arguments
    PyObject* center_obj = nullptr;
    float radius = 0.0f;
    float start_angle = 0.0f;
    float end_angle = 90.0f;
    PyObject* color_obj = nullptr;
    float thickness = 1.0f;
    PyObject* click_handler = nullptr;
    int visible = 1;
    float opacity = 1.0f;
    int z_index = 0;
    const char* name = nullptr;

    static const char* kwlist[] = {
        "center", "radius", "start_angle", "end_angle", "color", "thickness",
        "click", "visible", "opacity", "z_index", "name",
        nullptr
    };

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OfffOfOifiz", const_cast<char**>(kwlist),
                                     &center_obj, &radius, &start_angle, &end_angle,
                                     &color_obj, &thickness,
                                     &click_handler, &visible, &opacity, &z_index, &name)) {
        return -1;
    }

    // Parse center position
    sf::Vector2f center(0.0f, 0.0f);
    if (center_obj) {
        PyVectorObject* vec = PyVector::from_arg(center_obj);
        if (vec) {
            center = vec->data;
        } else {
            PyErr_Clear();
            PyErr_SetString(PyExc_TypeError, "center must be a Vector or tuple (x, y)");
            return -1;
        }
    }

    // Parse color
    sf::Color color = sf::Color::White;
    if (color_obj) {
        auto pycolor = PyColor::from_arg(color_obj);
        if (pycolor) {
            color = pycolor->data;
        } else {
            PyErr_Clear();
            PyErr_SetString(PyExc_TypeError, "color must be a Color or tuple (r, g, b) or (r, g, b, a)");
            return -1;
        }
    }

    // Set values
    self->data->setCenter(center);
    self->data->setRadius(radius);
    self->data->setStartAngle(start_angle);
    self->data->setEndAngle(end_angle);
    self->data->setColor(color);
    self->data->setThickness(thickness);

    // Handle common UIDrawable properties
    if (click_handler && click_handler != Py_None) {
        if (!PyCallable_Check(click_handler)) {
            PyErr_SetString(PyExc_TypeError, "click must be callable");
            return -1;
        }
        self->data->click_register(click_handler);
    }

    self->data->visible = (visible != 0);
    self->data->opacity = opacity;
    self->data->z_index = z_index;

    if (name) {
        self->data->name = name;
    }

    return 0;
}
