mod common;
mod analysis;
use analysis::*;
use common::*;
use structopt::StructOpt;
use std::path::PathBuf;

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

#[derive(Debug, StructOpt)]
#[structopt(name = "srp analysis", about = "Example:
    cargo run -- -e         
    cargo run -- -e -o analysis_1.json
    cargo run -- -e -i tasks_1.json
    cargo run -- -e -i tasks_2.json -o analysis_2.json")]
struct Opt {
    #[structopt(short, long)]
    exact: bool,

    #[structopt(short, long)]
    output_analysis: Option<PathBuf>,

    #[structopt(short, long)]
    input_tasks: Option<PathBuf>,

}

fn main() -> std::io::Result<()> {

    let opt = Opt::from_args();
    // println!("{:?}", opt);

    /* SAVES DOWN THE TASKS SET 1 and 2 to json
    let tasks_1 = &taskset_1();
    let tasks_2 = &taskset_2();
    let tasks_1_json: String = serde_json::to_string(&tasks_1).unwrap();
    let tasks_2_json: String = serde_json::to_string(&tasks_2).unwrap();
    write_file_string(tasks_1_json, "tasks_1.json".to_string());
    write_file_string(tasks_2_json, "tasks_2.json".to_string());
    */
    
    // Default task set
    let mut tasks: Tasks = taskset_1();

    // Tasks input, if no input a default task input taskset_1 is used
    match opt.input_tasks {
        Some(file) => {
            let mut file = File::open(file)?;
            let mut contents = String::new();
            file.read_to_string(&mut contents)?;
            tasks = serde_json::from_str(&contents).unwrap();
            println!("\nTask set is loaded from {:?}", file);
        }
        None => println!("\nA default task set is loaded!")
    };

    // Print out tasks
    println!("---------------------------- Tasks ----------------------------\n");
    for t in &tasks {
        println!("Task {} | Prio {} | Deadline {} | Inter_arrival {} |", 
                t.id, t.prio, t.deadline, t.inter_arrival);
        println!("{:?} \n", t.trace);
    }
    println!("----------------------------------------------------------------\n");

    // let (ip, tr) = pre_analysis(&tasks);
    // println!("{:?}", ip);
    // println!("{:?}", tr);

    // Calculate and print load factor for the CPU
    let load_f = load_factor(&tasks);
    println!("\nCPU load: {}\n", load_f);

    // Prints out if exact or apporoximated preemptions calculation
    if opt.exact {
        println!("Exact preemption calculation: YES\n");
    } else {
        println!("Exact preemption calculation: NO\n");
    }

    // Makes the analysis
    let analysis = analysis(&tasks, opt.exact);

    // Prints out the results of the analysis
    println!("---------------------------- Results of the analysis ----------------------------\n");
    for a in &analysis {
        println!("Task {:?} | Response time {} | WCET {} | Blocking time {} | Preemption time {} |\n", 
                a.task.id, a.response_time, a.wcet, a.blocking_time, a.preemption_time);
    }
    println!("---------------------------------------------------------------------------------\n");

    // Checks if the analysis is schedulable and prints out the results
    let schedule_check = schedulable_check(&analysis);
    if schedule_check && !(load_f > 1.0) {
        println!("The taskset is schedulable: YES\n")
    } else {
        println!("The taskset is schedulable: NO\n");
        println!("Reasons:");
        if load_f > 1.0 {
            println!("- The CPU is overloaded!\n");
        }
        if schedule_check {
            println!("- Deadline miss!\n")
        }
    }

    // Output analysis saved as json?
    let analysis_json: String = serde_json::to_string(&analysis).unwrap();
    match opt.output_analysis {
        Some(file) => {
            match write_file(analysis_json, file) {
                Ok(()) => (),
                Err(err) => panic!(err), 
            }
            println!("The Analysis is saved: YES\n");
        }
        None => println!("The Analysis is saved: NO\n"),
    };
    Ok(())

    
}

fn write_file_string(contents: String, file_name: String) -> std::io::Result<()> {
    let mut file = File::create(file_name)?;
    file.write_all(contents.as_bytes())?;
    Ok(())
}

fn write_file(contents: String, file_name: PathBuf) -> std::io::Result<()> {
    let mut file = File::create(file_name)?;
    file.write_all(contents.as_bytes())?;
    Ok(())
}

fn taskset_1() -> Tasks {
    let t1 = Task {
        id: "T1".to_string(),
        prio: 1,
        deadline: 100,
        inter_arrival: 100,
        trace: Trace {
            id: "T1".to_string(),
            start: 0,
            end: 10,
            inner: vec![],
        },
    };

    let t2 = Task {
        id: "T2".to_string(),
        prio: 2,
        deadline: 200,
        inter_arrival: 200,
        trace: Trace {
            id: "T2".to_string(),
            start: 0,
            end: 30,
            inner: vec![
                Trace {
                    id: "R1".to_string(),
                    start: 10,
                    end: 20,
                    inner: vec![Trace {
                        id: "R2".to_string(),
                        start: 12,
                        end: 16,
                        inner: vec![],
                    }],
                },
                Trace {
                    id: "R1".to_string(),
                    start: 22,
                    end: 28,
                    inner: vec![],
                },
            ],
        },
    };

    let t3 = Task {
        id: "T3".to_string(),
        prio: 3,
        deadline: 50,
        inter_arrival: 50,
        trace: Trace {
            id: "T3".to_string(),
            start: 0,
            end: 30,
            inner: vec![Trace {
                id: "R2".to_string(),
                start: 10,
                end: 20,
                inner: vec![],
            }],
        },
    };
    let tasks: Tasks = vec![t1, t2, t3];
    return tasks;
}

fn taskset_2() -> Tasks {
    let t1 = Task {
        id: "T1".to_string(),
        prio: 1,
        deadline: 200,
        inter_arrival: 200,
        trace: Trace {
            id: "T1".to_string(),
            start: 0,
            end: 15,
            inner: vec![
            Trace {
                id: "R3".to_string(),
                start: 2,
                end: 8,
                inner: vec![],
            },
            Trace {
                id: "R1".to_string(),
                start: 9,
                end: 13,
                inner: vec![],
            },
            ],
            
        },
    };
    
    let t2 = Task {
        id: "T2".to_string(),
        prio: 2,
        deadline: 200,
        inter_arrival: 200,
        trace: Trace {
            id: "T2".to_string(),
            start: 0,
            end: 37,
            inner: vec![
                Trace {
                    id: "R1".to_string(),
                    start: 10,
                    end: 18,
                    inner: vec![Trace {
                        id: "R2".to_string(),
                        start: 12,
                        end: 16,
                        inner: vec![],
                    }],
                },
                Trace {
                    id: "R1".to_string(),
                    start: 22,
                    end: 25,
                    inner: vec![],
                },
            ],
        },
    };
    
    let t3 = Task {
        id: "T3".to_string(),
        prio: 3,
        deadline: 100,
        inter_arrival: 100,
        trace: Trace {
            id: "T3".to_string(),
            start: 0,
            end: 20,
            inner: vec![Trace {
                id: "R3".to_string(),
                start: 8,
                end: 20,
                inner: vec![],
            }],
        },
    };
    let tasks: Tasks = vec![t1, t2, t3];
    return tasks;
}