#![cfg_attr(
    all(not(debug_assertions), target_os = "windows"),
    windows_subsystem = "windows"
)]

use tauri::{AppHandle, Manager, async_runtime as rt};
use once_cell::sync::OnceCell;

mod config;
mod errors;
mod clientinfo;
mod ipc;
mod state;
mod server;
mod tray;

use crate::errors::*;
use state::AppState;


pub static APP: OnceCell<AppHandle> = OnceCell::new();

fn main() {
    let initial_state = match rt::block_on(state::AppState::load()) {
        Ok(state) => state,
        Err(e) => {eprintln!("{}", e); return;}
    };

    tauri::Builder::default()
        .manage(initial_state)
        .system_tray(tray::create())
        .on_system_tray_event(tray::handle_event)
        .invoke_handler(tauri::generate_handler![
            ipc::unlock,
            ipc::respond,
            ipc::get_session_status,
            ipc::save_credentials,
            ipc::get_config,
        ])
        .setup(|app| {
            APP.set(app.handle()).unwrap();
            let state = app.state::<AppState>();
            let config = state.config.read().unwrap();
            let addr = std::net::SocketAddrV4::new(config.listen_addr, config.listen_port);
            tauri::async_runtime::spawn(server::serve(addr, app.handle()));

            if !config.start_minimized {
                app.get_window("main")
                    .ok_or(RequestError::NoMainWindow)?
                    .show()?;
            }
            Ok(())
        })
        .build(tauri::generate_context!())
        .expect("error while running tauri application")
        .run(|app, run_event| match run_event {
            tauri::RunEvent::WindowEvent { label, event, .. } => match event {
                tauri::WindowEvent::CloseRequested { api, .. } => {
                    let _ = app.get_window(&label).map(|w| w.hide());
                    api.prevent_close();
                }
                _ => ()
            }
            _ => ()
        })
}


macro_rules! get_state {
    ($prop:ident as $name:ident) => {
        use tauri::Manager;
        let app = crate::APP.get().unwrap(); // as long as the app is running, this is fine
        let state = app.state::<crate::state::AppState>();
        let $name = state.$prop.read().unwrap(); // only panics if another thread has already panicked
    };
    (config.$prop:ident as $name:ident) => {
        use tauri::Manager;
        let app = crate::APP.get().unwrap();
        let state = app.state::<crate::state::AppState>();
        let config = state.config.read().unwrap();
        let $name = config.$prop;
    };

    (mut $prop:ident as $name:ident) => {
        use tauri::Manager;
        let app = crate::APP.get().unwrap();
        let state = app.state::<crate::state::AppState>();
        let $name = state.$prop.write().unwrap();
    };
    (mut config.$prop:ident as $name:ident) => {
        use tauri::Manager;
        let app = crate::APP.get().unwrap();
        let state = app.state::<crate::state::AppState>();
        let config = state.config.write().unwrap();
        let $name = config.$prop;
    }
}


pub(crate) use get_state;
