extern crate regex;
extern crate separator;
extern crate time;

use std;
use std::fs::File;
use std::io::prelude::*;
use std::io::{Error, ErrorKind};

use self::regex::Regex;
use self::separator::Separatable;
use self::time::precise_time_ns;

#[derive(Debug)]
pub struct Measure {
    pub min: u64,
    pub max: u64,
    time: u64,
    last: u64,
    data: Vec<u64>,
    pub name: String,
}

impl Measure {
    pub fn new(name: String) -> Self {
        Measure {
            min: u64::max_value(),
            max: 0u64,
            time: 0u64,
            last: 0u64,
            data: Vec::with_capacity(1_000_000),
            name: name,
        }
    }

    pub fn start(&mut self) {
        self.last = precise_time_ns();
    }

    pub fn pause(&mut self) {
        if self.last == 0 {
            #[cfg(debug_assertions)]
            println!("WARNING: {:?} pause called without start!", self);

            return;
        }

        self.time += self.time_diff();
    }

    pub fn stop(&mut self) {
        if self.last == 0 {
            #[cfg(debug_assertions)]
            println!("WARNING: {:?} stop called without start!", self);

            return;
        }

        self.time += self.time_diff();
        self.data.push(self.time);

        if self.time < self.min {
            self.min = self.time;
        }

        if self.time > self.max {
            self.max = self.time;
        }

        self.time = 0u64;
        self.last = 0u64;
    }

    fn time_diff(&mut self) -> u64 {
        let current_time = precise_time_ns();
        let time_diff = current_time - self.last;
        self.last = current_time;

        time_diff
    }

    fn write_data(&self) -> std::io::Result<()> {
        let re = Regex::new(r"[^\w\-.]")
            .or(Err(Error::new(ErrorKind::Other, "Create filename regex failed.")))?;

        let file_name = format!("measure_{}.txt", self.name);
        let file_name = re.replace_all(file_name.as_str(), "_").to_string();
        println!("{}: Write data to {}", self.name, file_name);

        let mut file = File::create(file_name)?;

        for value in &self.data {
            file.write_fmt(format_args!("{}\n", value))?;
        }

        Ok(())
    }
}

impl Drop for Measure {
    fn drop(&mut self) {
        println!("{}:\n\tmin: {} ns\n\tmax: {} ns",
                 self.name,
                 self.min.separated_string(),
                 self.max.separated_string());

        if let Err(e) = self.write_data() {
            println!("{}: Write measure data failed: {}", self.name, e);
        }
    }
}
