use super::{Window, WndId};
use crate::tui::{Frame, GEvent, RetVal, ReturnValue, GENERAL_EVENT_CHANNEL};
use anyhow::Result as AResult;
use crossterm::event::KeyCode;
use std::any::Any;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::{Modifier, Style};
use tui::widgets::{Block, Clear, List, ListItem, ListState, Paragraph};

#[derive(Default)]
pub struct ConfirmWnd {
    pub id: WndId,
    msg: String,
    variants: Vec<String>,
    state: ListState,
}

impl ConfirmWnd {
    pub fn new_yn(msg: impl Into<String>, variants: Option<Vec<String>>) -> Self {
        let default = vec!["Yes".to_string(), "No".to_string()];
        let variants = match variants {
            Some(v) if !v.is_empty() => v,
            _ => default,
        };
        let mut this = Self {
            msg: msg.into(),
            variants,
            ..Default::default()
        };
        this.state.select(Some(0));
        this
    }

    pub fn on_right(&mut self) {
        let selected = self.state.selected().unwrap_or(0);
        self.state
            .select(Some((selected + 1).rem_euclid(self.variants.len())));
    }

    pub fn on_left(&mut self) {
        let selected = self.state.selected().unwrap_or(0);
        let vars_len = self.variants.len();
        self.state
            .select(Some((selected + vars_len - 1).rem_euclid(vars_len)));
    }
}

impl RetVal for ConfirmWnd {
    fn retval(&self) -> Box<dyn Any + Send> {
        let value = self
            .variants
            .get(self.state.selected().unwrap())
            .unwrap()
            .to_owned();
        Box::new(value)
    }
}

#[async_trait]
impl Window for ConfirmWnd {
    async fn handle_kbd(&mut self, k: KeyCode) -> AResult<()> {
        match k {
            KeyCode::Right => self.on_right(),
            KeyCode::Left => self.on_left(),
            KeyCode::Enter | KeyCode::Esc => self.close(false).await,
            _ => (),
        }
        Ok(())
    }

    async fn handle_update(&mut self) -> AResult<()> {
        Ok(())
    }

    async fn handle_close(&mut self) -> bool {
        true
    }

    fn draw(&mut self, f: &mut Frame) {
        let size = f.size();
        let rect = centered_rect(60, 40, size);
        f.render_widget(Clear, rect);

        let chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints(vec![Constraint::Percentage(70), Constraint::Percentage(30)])
            .split(rect);
        let msg = Paragraph::new(self.msg.as_ref());
        f.render_widget(msg, chunks[0]);

        let options = self
            .variants
            .iter()
            .map(AsRef::as_ref)
            .map(ListItem::new)
            .collect::<Vec<ListItem>>();
        let list =
            List::new(options).highlight_style(Style::default().add_modifier(Modifier::BOLD));
        f.render_stateful_widget(list, chunks[1], &mut self.state);
    }
}

pub async fn confirm_wnd(msg: impl Into<String>) -> bool {
    let wnd = ConfirmWnd::new_yn(msg.into(), None);
    GENERAL_EVENT_CHANNEL.send(GEvent::CreateWnd(wnd.into_shared()));
    *ReturnValue::get().await
}

fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
    let popup_layout = Layout::default()
        .direction(Direction::Vertical)
        .constraints(
            [
                Constraint::Percentage((100 - percent_y) / 2),
                Constraint::Percentage(percent_y),
                Constraint::Percentage((100 - percent_y) / 2),
            ]
            .as_ref(),
        )
        .split(r);

    Layout::default()
        .direction(Direction::Horizontal)
        .constraints(
            [
                Constraint::Percentage((100 - percent_x) / 2),
                Constraint::Percentage(percent_x),
                Constraint::Percentage((100 - percent_x) / 2),
            ]
            .as_ref(),
        )
        .split(popup_layout[1])[1]
}