#[macro_use]
extern crate scribe;



use std::env;
use std::ffi::OsString;
use std::path::PathBuf;



pub const BUILD_DIR: &'static str = "build";

pub const ERRNO_GENERATOR_FILENAME: &'static str = "errno.c";
pub const ERRNO_GENERATOR_OUTPUT_FILENAME: &'static str = "errno.gen";

pub const GENERATED_ERRNO_FILENAME: &'static str = "errno_values.rs";



fn determine_base_dir() -> PathBuf
{
   // Get the cargo manifest directory.
   match ::std::env::var_os("CARGO_MANIFEST_DIR")
   {
      Some(dir) =>
      {
         PathBuf::from(dir)
      }

      None =>
      {
         PathBuf::new()
      }
   }
}

fn determine_output_dir() -> PathBuf
{
   let output_dir: PathBuf;

   // Get the output directory for the build.
   match ::std::env::var_os("OUT_DIR")
   {
      Some(dir) =>
      {
         output_dir = PathBuf::from(dir);
      }

      None =>
      {
         // Try to just get the current directory.
         match env::current_dir()
         {
            Ok(dir) =>
            {
               output_dir = PathBuf::from(dir);
            }

            Err(error) =>
            {
               // Then just default to "".
               warn!("{}", error);
               output_dir = PathBuf::new();
            }
         }
      }
   }

   output_dir
}

fn determine_build_dir() -> PathBuf
{
   let mut build_dir: PathBuf;

   build_dir = determine_base_dir();
   build_dir.push(BUILD_DIR);

   build_dir
}

fn determine_c_compiler() -> OsString
{
   match ::std::env::var_os("CC")
   {
      Some(compiler) =>
      {
         OsString::from(compiler)
      }

      None =>
      {
         OsString::from("gcc")
      }
   }
}

fn build_output_file(filename: &str) -> PathBuf
{
   let mut output_dir: PathBuf;

   output_dir = determine_output_dir();
   output_dir.push(filename);

   output_dir
}

fn build_src_file(filename: &str) -> PathBuf
{
   let mut build_dir: PathBuf;

   build_dir = determine_build_dir();
   build_dir.push(filename);

   build_dir
}

fn compile(src_files: &[PathBuf], output_name: &str) -> PathBuf
{
   let c_compiler: OsString;
   let mut output_file: PathBuf;
   let mut initial_command: ::std::process::Command;
   let command: &mut ::std::process::Command;
   let last_command: &mut ::std::process::Command;
   let final_command: &mut ::std::process::Command;

   c_compiler = determine_c_compiler();
   output_file = build_output_file(output_name);

   initial_command = ::std::process::Command::new(c_compiler);
   command = initial_command.args(src_files);
   last_command = command.arg("-o");
   final_command = last_command.arg(output_file.clone());

   match final_command.output()
   {
      Ok(output) =>
      {
         if output.status.success() == false
         {
            error!("{:?}", output);
         }
      }

      Err(error) =>
      {
         error!("{}", error);
      }
   }

   output_file
}

fn run(executable: PathBuf, args: &[PathBuf])
{
   let mut initial_command: ::std::process::Command;
   let final_command: &mut ::std::process::Command;

   initial_command = ::std::process::Command::new(executable);
   final_command = initial_command.args(args);

   match final_command.output()
   {
      Ok(output) =>
      {
         if output.status.success() == false
         {
            error!("{:?}", output);
         }
      }

      Err(error) =>
      {
         error!("{}", error);
      }
   }
}

fn compile_and_run(output_name: &str, src_files: &[PathBuf], args: &[PathBuf])
{
   let executable: PathBuf;

   executable = compile(src_files, output_name);
   run(executable, args);
}



pub fn main()
{
   let mut errno_file: PathBuf;
   let mut gen_file: PathBuf;
   let mut gen_errno_file: PathBuf;

   errno_file = build_src_file(ERRNO_GENERATOR_FILENAME);
   gen_file = build_output_file(ERRNO_GENERATOR_OUTPUT_FILENAME);
   gen_errno_file = build_output_file(GENERATED_ERRNO_FILENAME);

   compile_and_run(ERRNO_GENERATOR_OUTPUT_FILENAME,
                   &[errno_file], &[gen_errno_file]);
}