mod browser;
mod input;

use cocotte::eyre::Result;
use crossterm::event::{Event, EventStream, KeyCode, KeyEvent, KeyModifiers};
use dirs::home_dir;
use futures::StreamExt;
use std::path::PathBuf;
use std::time::Duration;

#[derive(Debug, Clone)]
pub enum EventEnum {
    Tick,
    Setup,
    Key(KeyEvent),
}

#[derive(Debug, Default, Clone, PartialEq)]
pub enum Focus {
    #[default]
    Directories,
    Files,
}

#[derive(Default)]
pub struct AppState {
    pub directory_filter: String,
    pub file_filter: String,
    pub focus: Focus,
    pub current_directory: PathBuf,
}

impl AppState {
    pub fn new() -> Self {
        Self {
            current_directory: home_dir().unwrap_or_default(),
            ..Default::default()
        }
    }
}

struct EventPump {
    reader: EventStream,
    interval: tokio::time::Interval,
}

impl EventPump {
    fn new(fps: f32) -> Self {
        let period = Duration::from_secs_f32(1.0 / fps);
        Self {
            reader: EventStream::new(),
            interval: tokio::time::interval(period),
        }
    }

    async fn next(&mut self) -> Option<EventEnum> {
        tokio::select! {
            _ = self.interval.tick() => Some(EventEnum::Tick),
            Some(Ok(ev)) = self.reader.next() => {
                if let Event::Key(key) = ev {
                    Some(EventEnum::Key(key))
                } else {
                    None
                }
            }
        }
    }
}

cocotte::define_sub_apps! {
    event = EventEnum;
    state = AppState;
    Input(input::Input) => input::Input::new(),
    Browser(browser::Browser) => browser::Browser::new(),
}

#[tokio::main]
async fn main() -> Result<()> {
    let mut event_pump = EventPump::new(2.0);
    let mut app = make_app()?;
    let mut app_state = AppState::new();

    app.handle_input(&mut EventEnum::Setup, &mut app_state);

    while let Some(mut app_event) = event_pump.next().await {
        if let EventEnum::Key(key_event) = &app_event
            && key_event.modifiers.contains(KeyModifiers::CONTROL)
            && (key_event.code == KeyCode::Char('c') || key_event.code == KeyCode::Char('d'))
        {
            break;
        }
        app.handle_input(&mut app_event, &mut app_state);
        app.draw(&mut app_state)?;
    }

    Ok(())
}
