use std::sync::{Arc, Mutex};

use crate::color;
use crate::pixel::Pixel;
use crate::drawable::Drawable;
use crate::pixel_buf::PixelBuf;

/// Speed at which the radius shrinks, in pixels per second.
const RADIUS_SHRINK_RATE: f32 = 30.0;

/// Speed at which the hue cycles, in degrees per second.
const HUE_CYCLE_RATE: f32 = 180.0;

/// Represents a circle drawn at coordinates received via shared state.
pub struct Circle {
    x: Arc<Mutex<u32>>,
    y: Arc<Mutex<u32>>,
    set: Arc<Mutex<bool>>,
    radius: f32,
    /// Cached draw coordinates (read from mutex in update, used in draw).
    draw_x: u32,
    draw_y: u32,
    color: color::Rgb
}

impl Circle {
    pub fn new(x: Arc<Mutex<u32>>, y: Arc<Mutex<u32>>, set: Arc<Mutex<bool>>) -> Self {
        Circle {
            x,
            y,
            set,
            radius: 0.0,
            draw_x: 0,
            draw_y: 0,
            color: color::Rgb { r:0,g:0,b:0}
        }
    }

    /// Draws a circle using the Midpoint Circle Algorithm, writing 8 octant
    /// points per step.
    fn draw_circle(
        buf: &mut PixelBuf,
        cx: i32,
        cy: i32,
        radius: u32,
        r: u8,
        g: u8,
        b: u8,
    ) {
        let rad = radius as i32;
        let mut x: i32 = 0;
        let mut y: i32 = rad;
        let mut d: i32 = 3 - 2 * rad;

        while y >= x {
            // 8 octant points.
            buf.set_pixel(Pixel {x:(cx + x) as u16, y:(cy + y) as u16, r, g, b});
            buf.set_pixel(Pixel {x:(cx - x) as u16, y:(cy + y) as u16, r, g, b});
            buf.set_pixel(Pixel {x:(cx + x) as u16, y:(cy - y) as u16, r, g, b});
            buf.set_pixel(Pixel {x:(cx - x) as u16, y:(cy - y) as u16, r, g, b});
            buf.set_pixel(Pixel {x:(cx + y) as u16, y:(cy + x) as u16, r, g, b});
            buf.set_pixel(Pixel {x:(cx - y) as u16, y:(cy + x) as u16, r, g, b});
            buf.set_pixel(Pixel {x:(cx + y) as u16, y:(cy - x) as u16, r, g, b});
            buf.set_pixel(Pixel {x:(cx - y) as u16, y:(cy - x) as u16, r, g, b});

            x += 1;
            if d > 0 {
                y -= 1;
                d = d + 4 * (x - y) + 10;
            } else {
                d = d + 4 * x + 6;
            }
        }
    }
}

impl Drawable for Circle {
    fn draw(&self, buf: &mut PixelBuf, _elapsed: f32) {


        let cx = self.draw_y as i32;
        let cy = self.draw_x as i32;
        let radius = self.radius as u32;

        for i in 0..10 {
            if radius > i {
                Self::draw_circle(buf, cx, cy, radius - i, self.color.r, self.color.g, self.color.b);
            }
        }
    }

    fn update(&mut self, dt: f32, elapsed: f32) {
        self.radius = 150.0;
        self.draw_y = (*self.x.lock().unwrap() + (elapsed as u32))%1080;
        self.draw_x = (*self.y.lock().unwrap() + (elapsed as u32))%1920;

        self.radius -= RADIUS_SHRINK_RATE * dt;
        if self.radius < 0.0 {
            self.radius = 0.0;
        }
        let hue = ((elapsed * HUE_CYCLE_RATE) % 360.0) as u16;
        let hsv_color = color::Hsv {
            h: hue,
            s: 1.0,
            v: 1.0,
        };
        self.color = hsv_color.into();
    }
}
