use super::errors::*;

use std::path::Path;
use std::fs::File;
use std::io::prelude::*;

use raw::{create_shader_program, delete_shader_program, get_shader_program_info_log,
          ShaderProgramId, ShaderType};

#[derive(Debug)]
pub struct Shader {
    id: ShaderProgramId,
    pub shader_type: ShaderType,
}

impl Shader {
    pub fn new<T: AsRef<str>>(shader_type: ShaderType, sources: &[T]) -> Result<Shader> {
        let id: ShaderProgramId;
        let log: String;

        unsafe {
            id = create_shader_program(shader_type, sources).chain_err(
                || "Could not create shader program",
            )?;
            log = get_shader_program_info_log(id).chain_err(
                || "Could not get program compilation log",
            )?;
        }

        if log.len() <= 1 {
            Ok(Shader {
                id: id,
                shader_type: shader_type,
            })
        } else {
            Err(log.into())
        }
    }

    pub fn from_files(shader_type: ShaderType, filepaths: &[&Path]) -> Result<Shader> {
        let mut sources: Vec<String> = Vec::new();

        for filepath in filepaths {
            let mut file = match File::open(&filepath) {
                Ok(file) => file,
                Err(e) => return Err(format!("Could not open shader file : {}", e).into()),
            };

            let mut source = String::new();
            match file.read_to_string(&mut source) {
                Ok(_) => {}
                Err(e) => return Err(format!("Could not read shader file! : {}", e).into()),
            };

            sources.push(source);
        }

        Shader::new(shader_type, sources.as_slice())
    }

    pub fn from_file(shader_type: ShaderType, filepath: &Path) -> Result<Shader> {
        let mut file = match File::open(&filepath) {
            Ok(file) => file,
            Err(e) => return Err(format!("Could not open shader file : {}", e).into()),
        };

        let mut source = String::new();
        match file.read_to_string(&mut source) {
            Ok(_) => {}
            Err(e) => return Err(format!("Could not read shader file! : {}", e).into()),
        };

        Shader::new(shader_type, &[source.as_str()])
    }

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

impl Drop for Shader {
    fn drop(&mut self) {
        unsafe {
            delete_shader_program(self.id);
        }
    }
}
