pub mod utils;
pub mod rendering;
pub mod mode;
pub mod scope;
pub mod equirectangular;
pub mod sites;
pub mod command;
pub mod vcp;

use std::io::Cursor;
use std::ops::{Deref, DerefMut};
use js_sys::Uint8Array;
use log::info;
use wasm_bindgen::prelude::*;
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
use crate::command::{exec, should_newline};
use crate::mode::Mode;
use crate::scope::{Preferences, RenderState, ScopeState};

#[wasm_bindgen]
extern "C" {
    fn rescaleCanvas(canvas: &HtmlCanvasElement, ctx: &CanvasRenderingContext2d);
    fn loadar2(c: &str);
}

#[wasm_bindgen]
pub struct AbiScopeState(ScopeState);

#[wasm_bindgen]
pub fn __nxrd_browser_init() -> AbiScopeState {
    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
    utils::set_panic_hook();

    info!("initializing the scope");

    info!("finding canvas element");

    let document = web_sys::window().expect("window should exist").document().expect("document should exist");
    let canvas = document.get_element_by_id("canvas").expect("canvas element should exist");

    let canvas: web_sys::HtmlCanvasElement = canvas.dyn_into::<web_sys::HtmlCanvasElement>().map_err(|_| ()).expect("canvas is not a canvas");
    let context = canvas.get_context("2d").expect("failed to get canvas rendering context").expect("canvas rendering context doesn't exist").dyn_into::<web_sys::CanvasRenderingContext2d>().map_err(|_| ()).expect("2d context is not a 2d context");

    info!("finding form input element");

    let file = document.get_element_by_id("file").expect("file element should exist");
    let file: web_sys::HtmlInputElement = file.dyn_into::<web_sys::HtmlInputElement>().map_err(|_| ()).expect("file input is not an input");

    let scope_state = ScopeState {
        ar2: None,
        scope_mode: Mode::RadarInoperative,
        render_state: RenderState {
            canvas,
            context
        },
        file_input: file,
        lat: 30.48500, // jacksonville
        long: -81.70200, // jacksonville
        prefs: Preferences {
            fcs: 20
        },
        command_buf: String::new(),
        command_buf_response_mode: false,
        new_data_available: false
    };

    info!("nexrad-browser initialized successfully");

    AbiScopeState(scope_state)
}

#[wasm_bindgen]
pub fn render_abi(state: &mut AbiScopeState) {
    rendering::render(&mut state.0).unwrap()
}

#[wasm_bindgen]
pub fn load_ar2(buf: &JsValue, scope: &mut AbiScopeState) {
    let array = Uint8Array::new(buf);
    let rvec = array.to_vec();
    let mut cursor = Cursor::new(rvec);
    let loaded = nexrad2::parse_nx2_chunk(&mut cursor).unwrap();
    info!("new data chunk loaded");
    scope.0.ar2 = Some(loaded);
    scope.0.new_data_available = false;
}

#[wasm_bindgen]
pub fn new_file_available(scope: &mut AbiScopeState) {
    scope.0.new_data_available = true;
}

#[wasm_bindgen]
pub fn keydown(state: &mut AbiScopeState, key: String) {
    if state.0.command_buf_response_mode {
        state.0.command_buf = String::new();
        state.0.command_buf_response_mode = false;
    }

    if key == "Escape" {
        state.0.command_buf = String::new();
    } else if key == "Enter" {
        let cmd = state.0.command_buf.clone();
        exec(&mut state.0, cmd.replace('\n', " "));
        state.0.command_buf = String::new();
    } else if key == " " && should_newline(&mut state.0) {
        state.0.command_buf += "\n";
    } else if key == "Backspace" {
        if state.0.command_buf.is_empty() { return; }
        state.0.command_buf = state.0.command_buf[0..state.0.command_buf.len()-1].to_string();
    } else if key.len() == 1 {
        state.0.command_buf += &key.to_uppercase();
    }
}