#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate rocket;
// #[macro_use]
extern crate rocket_contrib;
#[macro_use]
extern crate lazy_static;
extern crate serde_json;

// use serde_json;
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::io::BufWriter;
use std::sync::Mutex;
use std::{thread, time};
use log::*;
use env_logger;

/// Fucking egun is a mess. It does not even use css and is built using tables. This is an attempt to parse it.
mod query;
use query::*;
mod server;
const DBFILE: &str = "accounts.json";


lazy_static! {
    static ref ACCOUNTS: Mutex<HashMap<String, Account>> = Mutex::new(HashMap::new());
}


pub fn run_update() {

    debug!(">>> UPD");

    let accounts_unlocked = ACCOUNTS.lock().unwrap();
    let accounts_cl = accounts_unlocked.clone();
    drop(accounts_unlocked);

    let client = reqwest::blocking::Client::builder()
    // .max_idle_per_host(200)
    // .timeout(Some(core::time::Duration::from_secs(10)))
    .build()
    .unwrap();

    let mut threads = vec![];

    for (account_name, account) in accounts_cl {
        let client = client.clone();
        let t = thread::spawn(move || {
            debug!("ACCT {}", account_name);
            
            let updated_account = account.updated(&client);
            let mut accounts = ACCOUNTS.lock().unwrap();
            accounts.insert(account_name.to_string(), updated_account);
        });
        threads.push(t);
    }

    debug!("Joining threads");
    for t in threads {
        t.join().unwrap();
    }
    debug!("Done.");


}


fn daemon(interval: u64) {
    loop {
      
        run_update();

        // commit to disk
        let accounts_unlocked = ACCOUNTS.lock().unwrap();
        let writer = BufWriter::new(File::create(DBFILE).unwrap());
        serde_json::to_writer_pretty(writer, &*accounts_unlocked).unwrap();
        drop(accounts_unlocked);

        info!("Waiting {} secs for next update", interval);
        let pause = time::Duration::from_secs(interval);
        thread::sleep(pause);

    }
}

fn main() {
    
    std::env::set_var("RUST_LOG", "info");
    let _ = env_logger::init();

    match File::open(DBFILE) {
        Ok(f) => {
            match serde_json::from_reader::<_, HashMap<String, Account>>(BufReader::new(f)) {
                Ok(accounts) => {
                    {
                        info!("ACCOUNT DATA LOADED");
                        // Lock once, shove loaded ccounts in
                        let mut accounts_unlocked = ACCOUNTS.lock().unwrap();
                        *accounts_unlocked = accounts;
                    }
                    info!("START DAEMON");

                    // start daemon before running web server
                    info!("START WEBSERVER");

                    thread::spawn(move || {
                        daemon(200);
                    });
                    // run server
                    server::run();
                },
                Err(e) => error!("Parsing account da'a has failed: {:?}", e)
            }
        }
        Err(e) => error!("Opening accounts file failed. {:?}", e),
    }
}
