pub mod arg;
pub mod cli;
pub mod command;
pub mod util;

use command::command_view;

use clap::{ArgMatches, Command, CommandFactory};
use cli::MockApp;
use xilem_html::{document_body, elements as el, App, View, ViewExt, ViewMarker};

include!(concat!(env!("OUT_DIR"), "/docs.rs"));

pub struct AppState {
    input: String,
    command: Command,
    arg_matches: Result<ArgMatches, clap::Error>,
}

impl Default for AppState {
    fn default() -> Self {
        // Initialise with placeholder values for debugging
        let input = String::from("pijul --name hi two --amount 5");
        let command = MockApp::command();
        let arg_matches = command
            .clone()
            .try_get_matches_from(input.clone().split_ascii_whitespace());

        Self {
            input,
            command,
            arg_matches,
        }
    }
}

impl AppState {
    fn update_command(&mut self, new_command: String) {
        self.input = new_command;

        let cli_command = MockApp::command();
        let args_iter = self.input.split(' ');

        // TODO: should be able to incrementally parse eventually
        self.arg_matches = cli_command.try_get_matches_from(args_iter);
    }
}

fn sidebar(state: &AppState) -> impl View<AppState> + ViewMarker {
    let arg_matches = state.arg_matches.as_ref().ok();
    el::div(command_view(&state.command, arg_matches)).attr("id", "sidebar")
}

fn app_logic(state: &mut AppState) -> impl View<AppState> {
    let input = el::input(())
        .attr("placeholder", "Enter command here")
        .attr("value", state.input.clone())
        .attr("autofocus", true)
        .attr("id", "command_input");

    el::div((
        input.on_input(|state: &mut AppState, event| state.update_command(event.target().value())),
        sidebar(&state),
    ))
}

pub fn main() {
    console_error_panic_hook::set_once();
    tracing_wasm::set_as_global_default();
    let app = App::new(AppState::default(), app_logic);
    app.run(&document_body());
}