mod line;
mod line_block;
mod parts;

use super::{ToDo, ToDoData};
use crate::config::Styles;
use anyhow::Result;
use line::Lines;
use line_block::LineBlock;
use std::str::FromStr;
use todo_txt::task::Simple as Task;
use tui::style::Style;

#[derive(pest_derive::Parser)]
#[grammar = "./todo/parser/grammar.pest"]
struct ParserGrammar;

#[derive(Debug)]
pub struct Parser {
    lines: Lines,
    styles: Styles,
}

impl Parser {
    pub fn new(value: &str, styles: Styles) -> Result<Self> {
        let lines = Lines::from_str(value)?;
        log::debug!("Loaded parser: {:#?}", lines);
        Ok(Parser { lines, styles })
    }

    pub fn fill(&self, task: &Task, todo: &ToDo) -> Vec<Vec<(String, Style)>> {
        self.lines
            .iter()
            .filter_map(|line| line.fill(task, todo, &self.styles))
            .collect()
    }
}

#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use super::*;
    use crate::config::Conf;

    #[test]
    fn fill() -> Result<()> {
        let mut todo = ToDo::default();
        todo.new_task("Project +project asdf").unwrap();
        todo.new_task("Some task 2").unwrap();
        todo.new_task("Some task 3").unwrap();
        let task = Task::from_str("task")?;
        let style = Styles::from_reader(
            r#"
[projects_style]
fg = "Green"

[contexts_style]
fg = "Red"

[hashtags_style]
fg = "Yellow"

[custom_category_style."+custom"]
fg = "Blue"
        "#
            .as_bytes(),
        )
        .unwrap();

        let lines = Lines::from_str(
            "[Done +project to be @splitted here #hashtag and some +custom project](! ^black)",
        )?;

        use tui::style::Color;
        assert_eq!(
            lines[0][0].fill(&task, &todo, &style),
            Some(vec![
                (String::from("Done "), Style::default().bg(Color::Black)),
                (String::from("+project"), Style::default().fg(Color::Green)),
                (String::from(" to be "), Style::default().bg(Color::Black)),
                (String::from("@splitted"), Style::default().fg(Color::Red)),
                (String::from(" here "), Style::default().bg(Color::Black)),
                (String::from("#hashtag"), Style::default().fg(Color::Yellow)),
                (
                    String::from(" and some "),
                    Style::default().bg(Color::Black)
                ),
                (String::from("+custom"), Style::default().fg(Color::Blue)),
                (String::from(" project"), Style::default().bg(Color::Black)),
            ])
        );

        Ok(())
    }

    #[test]
    fn fill_base() -> Result<()> {
        let parser = Parser::new("some text", Styles::default())?;
        let mut todo = ToDo::default();
        todo.new_task("task").unwrap();
        todo.new_task("x done task").unwrap();

        let task = Task::from_str("task").unwrap();

        assert_eq!(
            parser.fill(&task, &todo),
            vec![vec![(String::from("some text"), Style::default())]]
        );

        todo.set_active(ToDoData::Pending, 0);
        assert_eq!(
            parser.fill(&task, &todo),
            vec![vec![(String::from("some text"), Style::default())]]
        );

        Ok(())
    }

    #[test]
    fn fill_counts() -> Result<()> {
        let parser = Parser::new("Done: $done Pending: $pending", Styles::default())?;
        let mut todo = ToDo::default();
        todo.new_task("task").unwrap();
        todo.new_task("x done task").unwrap();
        todo.set_active(ToDoData::Pending, 0);
        let task = Task::from_str("task").unwrap();

        assert_eq!(
            parser.fill(&task, &todo),
            vec![vec![(String::from("Done: 1 Pending: 1"), Style::default())]]
        );

        Ok(())
    }
}
