#![allow(dead_code)]

use std::mem::size_of;

use glc::{
    array_buffer::{ArrayBuffer, Target, Usage},
    misc::Bindable,
    shader::{Program, Type, Uniform},
    vector::{Vector2f, Vector4f},
    vertex_array::{DataType, VertexArray},
};
use space_crush_common::misc::LogResult;
use specs::World;

use crate::{constants::UNIFORM_BUFFER_INDEX_CAMERA, misc::WorldHelper, Error};

pub struct Geometry {
    vertex_array_quad: VertexArray,
    vertex_array_line: VertexArray,
    program_line: Program,
    location_line_color: gl::GLint,
}

impl Geometry {
    pub fn new(world: &mut World) -> Result<Self, Error> {
        let vertex_array_quad = create_array_quad()?;
        let vertex_array_line = create_array_line()?;

        let program_line = world.load_program(vec![
            (Type::Vertex, "resources/shader/line/vert.glsl"),
            (Type::Fragment, "resources/shader/line/frag.glsl"),
        ])?;
        program_line.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?;

        let location_line_color = program_line.uniform_location("uColor")?;

        Ok(Self {
            vertex_array_quad,
            vertex_array_line,
            program_line,
            location_line_color,
        })
    }

    pub fn render_quad(&self) {
        self.vertex_array_quad.bind();

        gl::draw_arrays(gl::TRIANGLE_FAN, 0, 4);

        self.vertex_array_quad.unbind();
    }

    pub fn render_lines(&mut self, c: Vector4f, points: &[Vector2f]) {
        self.vertex_array_line.buffers_mut()[0]
            .buffer_data(Usage::StaticDraw, points)
            .unwrap();
        self.vertex_array_line.bind();
        self.program_line.bind();
        self.program_line
            .uniform(self.location_line_color, Uniform::Vector4f(&c))
            .warn("Unable to set line color");

        gl::draw_arrays(gl::LINE_STRIP, 0, points.len() as _);

        self.program_line.unbind();
        self.vertex_array_line.unbind();
    }
}

fn create_array_quad() -> Result<VertexArray, Error> {
    let vertices = &[
        Vector2f::new(-0.5, -0.5),
        Vector2f::new(0.5, -0.5),
        Vector2f::new(0.5, 0.5),
        Vector2f::new(-0.5, 0.5),
    ];

    const STRIDE_POS: gl::GLsizei = size_of::<Vector2f>() as gl::GLsizei;
    const OFFSET_POS: gl::GLsizei = 0;

    let mut array_buffer = ArrayBuffer::new(Target::ArrayBuffer)?;
    array_buffer.buffer_data(Usage::StaticDraw, vertices)?;

    let vertex_array = VertexArray::builder()
        .bind_buffer(array_buffer)
        .vertex_attrib_pointer(0, 2, DataType::Float, false, STRIDE_POS, OFFSET_POS)?
        .build()?;

    Ok(vertex_array)
}

fn create_array_line() -> Result<VertexArray, Error> {
    const STRIDE_POS: gl::GLsizei = size_of::<Vector2f>() as gl::GLsizei;
    const OFFSET_POS: gl::GLsizei = 0;

    let array_buffer = ArrayBuffer::new(Target::ArrayBuffer)?;

    let vertex_array = VertexArray::builder()
        .bind_buffer(array_buffer)
        .vertex_attrib_pointer(0, 2, DataType::Float, false, STRIDE_POS, OFFSET_POS)?
        .build()?;

    Ok(vertex_array)
}
