use ansi_term::{Colour::*, Style};
use crossterm::{
    ExecutableCommand, QueueableCommand, cursor, queue,
    style::Print,
    terminal::{self, Clear, disable_raw_mode, enable_raw_mode},
};
use std::io::{self, Write};

mod arg;
mod controls;
mod dance;
mod json;
mod list;

use crate::arg::Args;
use crate::list::*;

#[allow(unused)]
#[derive(PartialEq)]
enum State {
    Normal,
    Rename,
    Dance,
    Quit,
}

#[allow(dead_code)]
struct Dance {
    state: State,
    sout: std::io::StderrLock<'static>,
    loc_list: LocList,
    a: Args,
    size: (u16, u16),
    entry_buff: String,
}

impl Dance {
    fn new() -> io::Result<Self> {
        let size = terminal::size()?;
        let stderr = io::stderr();
        let sout = Box::leak(Box::new(stderr)).lock();

        Ok({
            Self {
                loc_list: LocList::new(),
                sout,
                a: arg::parse_args(),
                state: State::Normal,
                entry_buff: String::new(),
                size,
            }
        })
    }

    fn setup(&mut self) -> io::Result<()> {
        enable_raw_mode()?;
        self.sout.execute(cursor::Hide)?;
        for _ in 0..8 {
            queue!(self.sout, terminal::ScrollUp(1), cursor::MoveUp(1),)?;
        }
        self.sout.execute(cursor::SavePosition)?;
        Ok(())
    }

    fn cleanup(&mut self) -> io::Result<()> {
        self.sout.execute(cursor::Show)?;
        self.sout.execute(cursor::RestorePosition)?;
        for _ in 0..8 {
            queue!(
                self.sout,
                cursor::MoveDown(1),
                Clear(terminal::ClearType::CurrentLine)
            )?;
        }
        self.sout.execute(cursor::RestorePosition)?;

        disable_raw_mode()?;
        Ok(())
    }

    fn write_hints(&mut self, down: u16) -> io::Result<()> {
        self.sout.queue(cursor::MoveDown(down))?;
        let hl: Style = Style::new().fg(Blue);
        write!(
            self.sout,
            "{s} dance • {d} delete • {r} rename alias • {n} nav • {c} cancel",
            s = hl.paint("↵"),
            d = hl.paint("D"),
            r = hl.paint("R"),
            n = hl.paint("↑/↓"),
            c = hl.paint("esc")
        )?;
        Ok(())
    }

    fn write_entry(&mut self, down: u16) -> io::Result<()> {
        self.sout.queue(cursor::MoveToColumn(0))?;
        self.sout.queue(cursor::MoveDown(down))?;
        let placeholder: Style = Style::new().fg(RGB(70, 70, 90));
        let entry: Style = Style::default();
        self.sout.queue(Clear(terminal::ClearType::UntilNewLine))?;
        write!(
            self.sout,
            ": {e}",
            e = {
                if self.entry_buff.is_empty() {
                    if self.state == State::Normal {
                        placeholder.paint("search")
                    } else {
                        placeholder.paint("rename")
                    }
                } else {
                    entry.paint(&self.entry_buff)
                }
            }
        )?;
        Ok(())
    }

    fn write_list(&mut self) -> io::Result<()> {
        for _ in self.loc_list.items.iter() {
            queue!(
                self.sout,
                cursor::MoveDown(1),
                Clear(terminal::ClearType::CurrentLine)
            )?;
        }

        queue!(self.sout, cursor::RestorePosition, cursor::MoveDown(3),)?;

        for (i, item) in self.loc_list.filtered_items.iter().enumerate() {
            if i > 5 {
                break;
            }

            let path: Style = Style::new().fg(RGB(70, 70, 90));
            let mut st: Style = Style::default();
            if item.selected {
                st = Style::new().fg(Black).on(Blue);
            }

            queue!(
                self.sout,
                cursor::MoveToColumn(0),
                Print(&st.paint(&item.alias)),
                cursor::MoveToColumn(item.alias.chars().count() as u16 + 1),
                Print(&path.paint(&item.path.display().to_string())),
                cursor::MoveDown(1),
            )?;
        }
        Ok(())
    }
}

fn main() -> io::Result<()> {
    // if let Some(config_dir) = dirs::config_dir() {
    //     println!("{}", config_dir.join(NAME).display());
    // }
    let mut d: Dance = Dance::new()?;
    d.setup()?;
    d.get_json()?;
    d.loc_list.sort();

    if !d.a.no_hints {
        d.write_hints(1)?;
    }

    // initial write
    {
        d.loc_list.apply_filter(&d.entry_buff);
        d.write_entry(1)?;
        d.write_list()?;
        d.sout.queue(cursor::RestorePosition)?;
        d.sout.flush()?;
    }

    // main drawing/logic loop
    while d.state != State::Quit {
        if d.state == State::Normal {
            if d.controls()? {
                d.write_entry(2)?;
                d.write_list()?;
                d.sout.queue(cursor::RestorePosition)?;
                d.sout.flush()?;
            }
        } else if d.state == State::Rename {
            todo!("rename")
        } else if d.state == State::Dance {
            if let Some(sel) = d.loc_list.filtered_items.iter().position(|i| i.selected)
            {
                d.cleanup()?;
                d.execute(d.loc_list.filtered_items[sel].clone())?;
                return Ok(());
            } else {
                d.state = State::Normal;
            }
        }
    }

    d.cleanup()?;
    Ok(())
}
