use roast2d::prelude::*;
use std::f32::consts::FRAC_PI_6;

const MAP_RADIUS: i32 = 4;
const HEX_RADIUS: f32 = 48.0;
const HEX_SIDES: u32 = 6;
const HEX_ROTATION: f32 = FRAC_PI_6;
const SQRT_3: f32 = 1.732_050_8;
const FILL_Z: f32 = 0.1;
const OUTLINE_Z: f32 = 0.2;
const BACKGROUND_COLOR: Color = Color::rgb(0.05, 0.06, 0.09);

struct HexTile {
    axial: IVec2,
    color: Color,
}

impl HexTile {
    fn center(&self) -> Vec2 {
        axial_to_world(self.axial)
    }
}

struct HexScene {
    tiles: Vec<HexTile>,
    hovered: Option<usize>,
    title: Text,
    legend: Text,
    coord_text: Text,
}

impl HexScene {
    fn new() -> Self {
        Self {
            tiles: generate_tiles(),
            hovered: None,
            title: Text::new("Roast2d Hex Map".into(), 24.0, WHITE),
            legend: Text::new("q, r, s coordination".into(), 16.0, LIGHT_GRAY),
            coord_text: Text::new("Move mouse to choice Hex".into(), 18.0, LIGHT_GRAY),
        }
    }

    fn draw_tile(&self, g: &mut Engine, tile: &HexTile, hovered: bool) {
        let mut fill_color = tile.color;
        if hovered {
            fill_color.r = (fill_color.r + 0.2).min(1.0);
            fill_color.g = (fill_color.g + 0.2).min(1.0);
            fill_color.b = (fill_color.b + 0.1).min(1.0);
        }
        g.draw_poly(
            tile.center().extend(FILL_Z),
            HEX_SIDES,
            HEX_RADIUS,
            HEX_ROTATION,
            fill_color.with_a(if hovered { 0.95 } else { 0.7 }),
        );

        let outline_color = if hovered {
            YELLOW
        } else {
            Color::rgb(0.2, 0.28, 0.35)
        };
        let thickness = if hovered { 4.0 } else { 2.0 };
        let z = if hovered { OUTLINE_Z + 0.2 } else { OUTLINE_Z };
        g.draw_poly_lines(
            tile.center().extend(z),
            HEX_SIDES,
            HEX_RADIUS,
            HEX_ROTATION,
            thickness,
            outline_color,
        );
    }
}

impl Scene for HexScene {
    fn update(&mut self, g: &mut Engine) {
        let previous = self.hovered;
        let mouse = g.input().mouse_pos();
        let axial = world_to_axial(mouse);
        self.hovered = self
            .tiles
            .iter()
            .enumerate()
            .find(|(_, tile)| tile.axial == axial)
            .map(|(index, _)| index);

        if previous != self.hovered {
            self.coord_text = if let Some(index) = self.hovered {
                let axial = self.tiles[index].axial;
                let s = -axial.x - axial.y;
                Text::new(
                    format!("Hex: q={} r={} s={}", axial.x, axial.y, s),
                    18.0,
                    WHITE,
                )
            } else {
                Text::new("Move mouse to choice Hex".into(), 18.0, LIGHT_GRAY)
            };
        }
    }

    fn draw(&mut self, g: &mut Engine) {
        let view = g.view_size();
        g.draw_rect(
            BACKGROUND_COLOR,
            view,
            None,
            Transform::new(Vec3::new(0.0, 0.0, -0.9)),
        );

        let hovered = self.hovered;
        for (index, tile) in self.tiles.iter().enumerate() {
            self.draw_tile(g, tile, hovered == Some(index));
        }

        if let Some(index) = hovered {
            let tile = &self.tiles[index];
            let text = Text::new(format!("{}, {}", tile.axial.x, tile.axial.y), 14.0, WHITE);
            g.draw_text(
                &text,
                Some(Vec2::new(0.5, 0.5)),
                Transform::new(tile.center().extend(0.35)),
            );
        }

        g.with_ui(|g| {
            let view = g.view_size();
            let half = view * 0.5;
            let title_pos = Vec3::new(-half.x + 20.0, half.y - 20.0, 0.95);
            let legend_pos = Vec3::new(title_pos.x, title_pos.y - 28.0, title_pos.z);
            let coord_pos = Vec3::new(legend_pos.x, legend_pos.y - 28.0, legend_pos.z);

            g.draw_text(
                &self.title,
                Some(Vec2::new(0.0, 1.0)),
                Transform::new(title_pos),
            );
            g.draw_text(
                &self.legend,
                Some(Vec2::new(0.0, 1.0)),
                Transform::new(legend_pos),
            );
            g.draw_text(
                &self.coord_text,
                Some(Vec2::new(0.0, 1.0)),
                Transform::new(coord_pos),
            );
        });
    }
}

fn generate_tiles() -> Vec<HexTile> {
    let mut tiles = Vec::new();
    for q in -MAP_RADIUS..=MAP_RADIUS {
        let r_min = (-MAP_RADIUS).max(-q - MAP_RADIUS);
        let r_max = MAP_RADIUS.min(-q + MAP_RADIUS);
        for r in r_min..=r_max {
            let axial = IVec2::new(q, r);
            let dist = hex_distance(axial, IVec2::ZERO) as f32 / MAP_RADIUS as f32;
            let height = 1.0 - dist;
            let color = Color::rgb(0.18 + 0.3 * height, 0.3 + 0.25 * height, 0.2 + 0.2 * height);
            tiles.push(HexTile { axial, color });
        }
    }
    tiles
}

fn axial_to_world(axial: IVec2) -> Vec2 {
    let q = axial.x as f32;
    let r = axial.y as f32;
    let x = HEX_RADIUS * SQRT_3 * (q + 0.5 * r);
    let y = HEX_RADIUS * 1.5 * r;
    Vec2::new(x, y)
}

fn hex_distance(a: IVec2, b: IVec2) -> i32 {
    let dq = (a.x - b.x).abs();
    let dr = (a.y - b.y).abs();
    let ds = (a.x + a.y - b.x - b.y).abs();
    (dq + dr + ds) / 2
}

fn world_to_axial(pos: Vec2) -> IVec2 {
    let q = ((SQRT_3 / 3.0) * pos.x - (1.0 / 3.0) * pos.y) / HEX_RADIUS;
    let r = (2.0 / 3.0 * pos.y) / HEX_RADIUS;
    cube_round(q, r)
}

fn cube_round(q: f32, r: f32) -> IVec2 {
    let s = -q - r;
    let mut rq = q.round();
    let mut rr = r.round();
    let rs = s.round();

    let q_diff = (rq - q).abs();
    let r_diff = (rr - r).abs();
    let s_diff = (rs - s).abs();

    if q_diff > r_diff && q_diff > s_diff {
        rq = -rr - rs;
    } else if r_diff > s_diff {
        rr = -rq - rs;
    }

    IVec2::new(rq as i32, rr as i32)
}

fn setup(g: &mut Engine) {
    g.set_scale_mode(ScaleMode::Fixed {
        width: 960,
        height: 720,
    });
    g.set_scene(HexScene::new());
}

fn main() {
    env_logger::init();
    App::default()
        .title("Roast2d Hex Map".to_string())
        .vsync(true)
        .run(setup)
        .unwrap();
}
