extern crate serde;
extern crate serde_json;
extern crate crossterm;

use serde::{
    de::DeserializeOwned,
    Serialize
};

use crossterm::style::{
    Colorize,
    Styler
};

use std::{
    fmt::Write,
    fs
};

pub trait LoadFromJson {
    fn load_from_json(filepath: &str) -> Result<Self, String>
    where Self: Sized;
}

pub trait SaveToJson {
    fn save_to_json(&self, filepath: &str) -> Result<(), String>;
}

const APP_NAME: &str = "BoolNetEvo";
const APP_VER: &str = "0.3.1";
const APP_COPYR: &str = "Copyright (c) 2020 serhon <serhon@protonmail.com>";
const APP_DESC: &str = "Evolve populations of boolean networks to approximate bitstring functions and their (unknown) inverses.";
const APP_DISCL: &str = "The software is provided \"as is\", without warranty of any kind, express or implied, including
but not limited to the warranties of merchantability, fitness for a particular purpose and
noninfringement. In no event shall the authors or copyright holders be liable for any claim,
damages or other liability, whether in an action of contract, tort or otherwise, arising
from, out of or in connection with the software or the use or other dealings in the software.";

// The following 2 funcs are probably already in std... somewhere

pub fn hex2bytes(hex: &str) -> Result<Vec<u8>, String> {
    let hex: Vec<char> = hex.chars().collect();

    // Sanity checks
    for i in 0..hex.len() {
        if !hex[i].is_digit(0x10) {
            return Err(format!("Char {} is not a hex digit", i));
        }
    }
    if (hex.len() & 1) != 0 {
        return Err(format!("Number of hex digits must be even (2 per byte)"));
    }

    let mut bytes = vec![0u8; hex.len() >> 1];
    for i in 0..bytes.len() {
        match (hex[(i << 1)].to_digit(0x10), hex[(i << 1) + 1].to_digit(0x10)) {
            (Some(high), Some(low)) => {
                bytes[i] = ((high as u8) << 4) | (low as u8);
            },
            _ => return Err(format!("Error parsing input byte {}", i)) // redundant perhaps due to is_digit() check...
        };
    }
    Ok(bytes)
}

pub fn bytes2hex(bytes: &[u8]) -> Result<String, String> {
    let mut hex = String::new();
    for b in bytes.iter() {
        write!(&mut hex, "{:02x}", b).map_err(|e| e.to_string())?;
    }
    Ok(hex)
}

impl<T: Sized + DeserializeOwned> LoadFromJson for T {
    fn load_from_json(filepath: &str) -> Result<Self, String> {
        let json = fs::read(&filepath).map_err(|e| e.to_string())?;
        Ok(serde_json::from_slice::<Self>(&json).map_err(|e| e.to_string())?)
    }
}

impl<T: Serialize> SaveToJson for T {
    fn save_to_json(&self, filepath: &str) -> Result<(), String>
    where Self: Serialize {
        let json = serde_json::to_vec_pretty(self).map_err(|e| e.to_string())?;
        fs::write(&filepath, &json).map_err(|e| e.to_string())?;
        Ok(())
    }
}

/// Show general description, disclaimer etc.
pub fn about() {
    println!("{} {}{} {}", APP_NAME.green().bold(), "v".green(), APP_VER.green(), APP_COPYR.dark_green());
    println!("{}", APP_DESC.green());
    println!("{}", APP_DISCL.dark_green());
}
