use std::fs;
use std::path::PathBuf;

use serde::Deserialize;

// Makes all these structs public and deserializable
macro_rules! toml_struct {
    (struct $name:ident {$($field:ident: $t:ty,)*}) => {
        #[derive(Deserialize, Clone)]
        pub struct $name {
            $(pub $field: $t),*
        }
    }
}

toml_struct!{
    struct Params {
        global_flow: GlobalFlow,
        statements: Statments,
        types: Types,
        dev: Dev,
    }
}

toml_struct!{
    struct GlobalFlow {
        gen_decl: f64,
        gen_subroutine: f64,
        gen_typedef: f64,
        end_generation: f64,
    }
}

toml_struct!{
    struct Statments {
        gen_assign: GenAssign,
        gen_decl: GenDecl,
        gen_global_decl: GenGlobalDecl,
        gen_if_statement: GenIfStatement,
        gen_loop: GenLoop,
    }
}

toml_struct!{
    struct GenAssign {
        gen_boolean: f64,
        gen_character: f64,
        gen_integer: f64,
        gen_real: f64,
        gen_tuple: f64,
    }
}

toml_struct!{
    struct GenDecl {
        gen_boolean: f64,
        gen_character: f64,
        gen_integer: f64,
        gen_real: f64,
        gen_tuple: f64,
        qual_const: f64,
        qual_var: f64,
    }
}

toml_struct!{
    struct GenGlobalDecl {
        gen_boolean: f64,
        gen_character: f64,
        gen_integer: f64,
        gen_real: f64,
        gen_tuple: f64,
    }
}

toml_struct!{
    struct GenIfStatement {
        gen_statement: f64,
        end_gen_statement: f64,
        fork_elif: f64,
        fork_else: f64,
        fork_end: f64,
    }
}

toml_struct!{
    struct GenLoop {
        gen_inf_loop: f64,
        gen_cond_loop: f64,
        gen_statement: f64,
        end_gen_statement: f64,
    }
}

toml_struct!{
    struct Types {
        gen_boolean: GenBoolean,
        gen_integer: GenInteger,
        gen_real: GenReal,
    }
}

toml_struct!{
    struct GenBoolean {
        expr_and: f64,
        expr_or: f64,
        expr_not: f64,
        expr_gt: f64,
        expr_ge: f64,
        expr_lt: f64,
        expr_le: f64,
        expr_eq: f64,
        expr_eq_real: f64,
        expr_eq_char: f64,
        expr_eq_tuple: f64,
        expr_ne: f64,
        expr_ne_real: f64,
        expr_ne_char: f64,
        expr_ne_tuple: f64,
        get_instant: f64,
    }
}

toml_struct!{
    struct GenInteger {
        expr_add: f64,
        expr_sub: f64,
        expr_mul: f64,
        expr_div: f64,
        expr_rem: f64,
        expr_exp: f64,
        expr_pos: f64,
        expr_neg: f64,
        get_instant: f64,
        max_depth: u32,
    }
}

toml_struct!{
    struct GenReal {
        expr_add: f64,
        expr_sub: f64,
        expr_mul: f64,
        expr_div: f64,
        expr_rem: f64,
        expr_exp: f64,
        expr_pos: f64,
        expr_neg: f64,
        gen_integer: f64,
        get_instant: f64,
        max_depth: u32,
    }
}

toml_struct!{
    struct Dev {
        float_gen_distro_pos_stddiv: f32,
        float_gen_distro_neg_stddiv: f32,
        int_gen_distro_pos_stddiv: f32,
        int_gen_distro_neg_stddiv: f32,
    }
}

impl Params {
    pub fn parse(p: &PathBuf) -> Self {
        let file_string = fs::read_to_string(p)
            .expect("Failed to read parameters file into string");

        let params: Params = toml::from_str(&file_string)
            .expect("Failed to parse TOML from parameters file");

        params
    }
}