use crate::utils::{Position, Rectangle};
use crate::PlutoObject;
use crate::PlutoniumEngine;
use std::cell::RefCell;
use std::f32::consts::PI;
use std::rc::Rc;
use uuid::Uuid;

#[derive(Clone)]
/// Options for shape type.
pub enum ShapeType {
    /// Rectangle option.
    Rectangle,
    /// Circle option.
    Circle,
    /// Polygon option.
    Polygon(u32),
}

pub(crate) struct ShapeInternal {
    id: Uuid,
    texture_id: Uuid,
    bounds: Rectangle,
    position: Position,
    fill: String,
    outline: String,
    stroke: f32,
    shape_type: ShapeType,
    z: i32,
}

impl ShapeInternal {
    pub fn set_ids(&mut self, id: Uuid, texture_id: Uuid) {
        self.id = id;
        self.texture_id = texture_id;
    }

    #[allow(clippy::too_many_arguments)]
    pub fn new(
        id: Uuid,
        texture_id: Uuid,
        bounds: Rectangle,
        position: Position,
        fill: String,
        outline: String,
        stroke: f32,
        shape_type: ShapeType,
    ) -> Self {
        Self {
            id,
            texture_id,
            bounds,
            position,
            fill,
            outline,
            stroke,
            shape_type,
            z: 0,
        }
    }

    pub fn generate_svg_data(&self) -> String {
        let width = self.bounds.width;
        let height = self.bounds.height;

        match &self.shape_type {
            ShapeType::Rectangle => format!(
                r#"<svg xmlns="http://www.w3.org/2000/svg" width="{}" height="{}">
                    <rect width="{}" height="{}" 
                        fill="{}" 
                        stroke="{}" 
                        stroke-width="{}"/>
                </svg>"#,
                width, height, width, height, self.fill, self.outline, self.stroke
            ),

            ShapeType::Circle => format!(
                r#"<svg xmlns="http://www.w3.org/2000/svg" width="{}" height="{}">
                    <circle cx="{}" cy="{}" r="{}"
                        fill="{}"
                        stroke="{}"
                        stroke-width="{}"/>
                </svg>"#,
                width,
                height,
                width / 2.0,
                height / 2.0,
                width / 2.0,
                self.fill,
                self.outline,
                self.stroke
            ),

            ShapeType::Polygon(points) => {
                let center_x = width / 2.0;
                let center_y = height / 2.0;
                let radius = width / 2.0;
                let mut path = String::new();

                for i in 0..*points {
                    let angle = (i as f32) * 2.0 * PI / (*points as f32);
                    let x = center_x + radius * angle.cos();
                    let y = center_y + radius * angle.sin();

                    if i == 0 {
                        path.push_str(&format!("M {} {}", x, y));
                    } else {
                        path.push_str(&format!(" L {} {}", x, y));
                    }
                }
                path.push_str(" Z");

                format!(
                    r#"<svg xmlns="http://www.w3.org/2000/svg" width="{}" height="{}">
                        <path d="{}"
                            fill="{}"
                            stroke="{}"
                            stroke-width="{}"/>
                    </svg>"#,
                    width, height, path, self.fill, self.outline, self.stroke
                )
            }
        }
    }

    pub fn set_z(&mut self, z: i32) {
        self.z = z;
    }

    pub fn get_z(&self) -> i32 {
        self.z
    }

    pub fn render_with_z(&self, engine: &mut PlutoniumEngine, z: i32) {
        engine.queue_texture_with_layer(&self.texture_id, Some(self.position), z);
    }
}

/// Shape data.
pub struct Shape {
    internal: Rc<RefCell<ShapeInternal>>,
}

impl Shape {
    impl_wrapper_new!(Shape, ShapeInternal);

    // Wrapper functions to match other objects
    /// Returns the id.
    pub fn get_id(&self) -> Uuid {
        self.internal.borrow().get_id()
    }

    /// Texture key.
    pub fn texture_key(&self) -> Uuid {
        self.internal.borrow().texture_key()
    }

    /// Dimensions.
    pub fn dimensions(&self) -> Rectangle {
        self.internal.borrow().dimensions()
    }

    /// Returns the current position.
    pub fn pos(&self) -> Position {
        self.internal.borrow().pos()
    }

    /// Sets the dimensions.
    pub fn set_dimensions(&mut self, new_dimensions: Rectangle) {
        self.internal.borrow_mut().set_dimensions(new_dimensions);
    }

    /// Sets the pos.
    pub fn set_pos(&mut self, new_pos: Position) {
        self.internal.borrow_mut().set_pos(new_pos);
    }

    // Additional shape-specific getters if needed
    /// Fill.
    pub fn fill(&self) -> String {
        self.internal.borrow().fill.clone()
    }

    /// Outline.
    pub fn outline(&self) -> String {
        self.internal.borrow().outline.clone()
    }

    /// Stroke.
    pub fn stroke(&self) -> f32 {
        self.internal.borrow().stroke
    }

    // You might want to add setters for these as well
    /// Sets the fill.
    pub fn set_fill(&mut self, fill: String) {
        self.internal.borrow_mut().fill = fill;
    }

    /// Sets the outline.
    pub fn set_outline(&mut self, outline: String) {
        self.internal.borrow_mut().outline = outline;
    }

    /// Sets the stroke.
    pub fn set_stroke(&mut self, stroke: f32) {
        self.internal.borrow_mut().stroke = stroke;
    }

    /// Queues this object for rendering.
    pub fn render(&self, engine: &mut PlutoniumEngine) {
        self.internal.borrow().render(engine);
    }

    /// Render with z.
    pub fn render_with_z(&self, engine: &mut PlutoniumEngine, z: i32) {
        self.internal.borrow().render_with_z(engine, z);
    }

    /// Sets the z.
    pub fn set_z(&self, z: i32) {
        self.internal.borrow_mut().set_z(z);
    }

    /// Returns the z.
    pub fn get_z(&self) -> i32 {
        self.internal.borrow().get_z()
    }

    /// Returns this value with z configured.
    pub fn with_z(self, z: i32) -> Self {
        self.set_z(z);
        self
    }
}
// Move PlutoObject implementation to ShapeInternal
impl PlutoObject for ShapeInternal {
    fn texture_key(&self) -> Uuid {
        self.texture_id
    }

    fn get_id(&self) -> Uuid {
        self.id
    }

    fn dimensions(&self) -> Rectangle {
        self.bounds
    }

    fn pos(&self) -> Position {
        self.position
    }

    fn set_dimensions(&mut self, new_dimensions: Rectangle) {
        self.bounds = new_dimensions;
    }

    fn set_pos(&mut self, new_pos: Position) {
        self.position = new_pos;
    }

    fn render(&self, engine: &mut PlutoniumEngine) {
        self.render_with_z(engine, self.z);
    }
}
