use super::*;
use super::errors::*;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ProgramPipelineId(u32);

impl From<ProgramPipelineId> for u32 {
    fn from(program_id: ProgramPipelineId) -> Self {
        program_id.0
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PipelineShaderStage {
    Vertex,
    Fragment,
    Geometry,
    TessellationEvaluation,
    TessellationControl,
    Compute,
}

impl From<PipelineShaderStage> for u32 {
    fn from(shader_stage: PipelineShaderStage) -> Self {
        match shader_stage {
            PipelineShaderStage::Vertex => gl::VERTEX_SHADER_BIT,
            PipelineShaderStage::Fragment => gl::FRAGMENT_SHADER_BIT,
            PipelineShaderStage::Geometry => gl::GEOMETRY_SHADER_BIT,
            PipelineShaderStage::TessellationEvaluation => gl::TESS_EVALUATION_SHADER_BIT,
            PipelineShaderStage::TessellationControl => gl::TESS_CONTROL_SHADER_BIT,
            PipelineShaderStage::Compute => gl::COMPUTE_SHADER_BIT,
        }
    }
}

pub unsafe fn create_program_pipeline() -> ProgramPipelineId {
    let mut pipeline_id: u32 = 0;
    let pipeline_id_ptr: *mut u32 = &mut pipeline_id;

    gl::CreateProgramPipelines(1, pipeline_id_ptr);

    ProgramPipelineId(pipeline_id)
}

pub unsafe fn delete_program_pipeline(id: ProgramPipelineId) {
    let pipeline_id_ptr: *const u32 = &id.into();

    gl::DeleteProgramPipelines(1, pipeline_id_ptr);
}

pub unsafe fn use_program_stage(
    pipeline: ProgramPipelineId,
    stage: PipelineShaderStage,
    program: ShaderProgramId,
) -> Result<()> {
    gl::UseProgramStages(pipeline.into(), stage.into(), program.into());

    get_error(())
}

pub unsafe fn bind_program_pipeline(pipeline: ProgramPipelineId) {
    gl::BindProgramPipeline(pipeline.into());
}
