Documentation
use crate::{
    traits::NotedownMeta,
    utils::{build_td, build_th, build_zola},
    Context, NotedownTarget, AST, GLOBAL_CONFIG,
};
use std::iter::repeat;

pub trait ToHTML {
    fn to_html(&self) -> String;
}

impl ToHTML for Context {
    fn to_html(&self) -> String {
        let head = self.meta.to_html();
        let post = self.ast.to_html();
        return format!("{}{}", head, post);
    }
}

impl ToHTML for NotedownMeta {
    fn to_html(&self) -> String {
        let ref cfg = GLOBAL_CONFIG.lock().unwrap();
        match cfg.target {
            NotedownTarget::Web => String::new(),
            NotedownTarget::VSCode => String::new(),
            NotedownTarget::Zola => build_zola(self),
        }
    }
}

impl ToHTML for AST {
    fn to_html(&self) -> String {
        let ref cfg = GLOBAL_CONFIG.lock().unwrap();
        match self {
            AST::Statements(e) => {
                let mut text = String::new();
                for node in e {
                    text += &node.to_html()
                }
                let trimmed: Vec<_> = text.lines().map(|s| s.trim()).collect();
                trimmed.join("\n")
            }
            AST::Header(e, level) => format!("<h{0}>{1}</h{0}>", level, e.to_html()),

            AST::Paragraph(p) => format!("<p>{}</p>", p.to_html()),
            AST::Text(v) => v.iter().map(|s| s.to_html()).collect::<Vec<String>>().join(""),
            AST::Bold(s) => format!("<b>{}</b>", s.to_html()),
            AST::Italic(s) => format!("<i>{}</i>", s.to_html()),
            AST::Underline(s) => format!("<u>{}</u>", s.to_html()),
            AST::Strikethrough(s) => format!("<del>{}</del>", s.to_html()),
            AST::Undercover(s) => format!("<span class=\"undercover\">{}</span>", s.to_html()),

            AST::Table { head, align, terms, column } => {
                let align_iter = align.iter().chain(repeat(&align[align.len() - 1]));
                let thead = {
                    let mut head = head.iter().chain(repeat(&AST::Space));
                    let mut align = align_iter.clone();
                    let mut thead = String::new();
                    for _ in 0..*column {
                        let h = head.next().unwrap().to_html();
                        let a = *align.next().unwrap();
                        thead.push_str(&format!("{}", build_th(&h, a)))
                    }
                    format!("<thead>{}</thead>", thead)
                };
                let mut trs = vec![];
                for term in terms {
                    let mut head = term.iter().chain(repeat(&AST::Space));
                    let mut align = align_iter.clone();
                    let mut thead = String::new();
                    for _ in 0..*column {
                        let h = head.next().unwrap().to_html();
                        let a = *align.next().unwrap();
                        thead.push_str(&format!("{}", build_td(&h, a)))
                    }
                    trs.push(format!("<tr>{}</tr>", thead))
                }
                format!("<table>{}<tbody>{}</tbody></table>", thead, trs.join(""))
            }
            AST::Quote { body, style } => {
                let quote = body.iter().map(|s| s.to_html()).collect::<Vec<String>>().join("");
                match style.as_str() {
                    "info" | "danger" | "warning" | "success" => {
                        format!("<blockquote class=\"fancyquote {}\">{}</blockquote>", style, quote)
                    }
                    _ => format!("<blockquote>{}</blockquote>", quote),
                }
            }
            AST::Ordered(v) => {
                let quote = v.iter().map(|s| format!("<li>{}</li>", s.to_html())).collect::<Vec<String>>().join("");
                format!("<ol>{}</ol>", quote)
            }
            AST::Orderless(v) => {
                let quote = v.iter().map(|s| format!("<li>{}</li>", s.to_html())).collect::<Vec<String>>().join("");
                format!("<ul>{}</ul>", quote)
            }

            AST::Command(s, keys, values) => format!("cmd: {}\narg: {:?}\nkvs: {:?}", s, keys, values),

            AST::None => String::from(""),
            AST::Space => String::from(" "),
            AST::Newline => String::from("</br>"),

            AST::Raw(s) => format!("<pre>{}</pre>", s),
            AST::Code(s) => format!("<code>{}</code>", s),
            AST::String(s) => format!("{}", s),
            AST::MathInline(s) => format!("<span class=\"math\">${}$</span> ", s),
            AST::MathDisplay(s) => format!("<p class=\"math\">$${}$$</p> ", s),

            _ => {
                let a = format!("HTML unimplemented AST::{:?}", self);
                println!("{}", a.split("(").next().unwrap_or("Unknown"));
                format!("{:?}", self);
                unreachable!()
            }
        }
    }
}