Documentation
use crate::{Context, Value, AST};
use std::collections::{HashMap, VecDeque};

macro_rules! must_arg {
    ($kvs:ident, $name:literal, $args:ident, $attr:ident) => {
        match $kvs.remove($name) {
            Some(v) => {
                $attr.push(format!(concat!($name, "={:?}"), v.trim()));
            }
            None => match $args.pop_front() {
                None => return None,
                Some(v) => $attr.push(format!(concat!($name, "={:?}"), v.trim())),
            },
        }
    };
}

macro_rules! required_arg {
    ($kvs:ident, $name:literal, $args:ident, $index:literal) => {
        match $kvs.get($name) {
            Some(v) => v,
            None => match $args.get($index) {
                None => {
                    return None;
                }
                Some(i) => i,
            },
        }
    };
}

macro_rules! named_arg {
    ($kvs:ident, $name:literal, $args:ident, $attr:ident) => {
        match $kvs.remove($name) {
            Some(v) => $attr.push(format!(concat!($name, "={:?}"), v.trim())),
            None => {
                if let Some(v) = $args.pop_front() {
                    $attr.push(format!(concat!($name, "={:?}"), v.trim()))
                }
            }
        }
    };
}

pub fn meting_js(server: &str, args: &Vec<Value>, kvs: &HashMap<String, Value>) -> Option<String> {
    let ty = required_arg!(kvs, "type", args, 0);
    let id = required_arg!(kvs, "id", args, 1);
    let mut args = vec![];
    args.push(format!("type={}", ty));
    args.push(format!("id={}", id));
    if let Some(v) = kvs.get("autoplay") {
        args.push(format!("autoplay={:?}", v))
    }
    if let Some(v) = kvs.get("fixed") {
        args.push(format!("fixed={:?}", v))
    }
    if let Some(v) = kvs.get("mini") {
        args.push(format!("mini={:?}", v))
    }
    if let Some(v) = kvs.get("order") {
        args.push(format!("order={:?}", v))
    }
    return Some(format!("<meting-js server={:?} {}></meting-js>", server, args.join(" ")));
}

pub fn fancy_quote(ctx: &mut Context, _args: &Vec<Value>, mut kvs: HashMap<String, Value>) -> AST {
    let by = match kvs.remove("body") {
        Some(v) => v,
        None => Value::from(""),
    };
    let ty = match kvs.remove("type") {
        Some(v) => v,
        None => Value::from(""),
    };
    match ctx.parse_program(by.as_str()) {
        AST::Statements(body) => AST::Quote { body, style: ty.to_string() },
        _ => AST::None,
    }
}

pub fn image_insert(_: &Context, mut args: VecDeque<Value>, mut kvs: HashMap<String, Value>) -> Option<String> {
    let mut attr = vec![];
    must_arg!(kvs, "src", args, attr);
    let link = match kvs.remove("link") {
        Some(v) => Some(format!("href={:?}", v.trim())),
        None => match args.pop_front() {
            Some(v) => Some(format!("href={:?}", v.trim())),
            None => None,
        },
    };
    named_arg!(kvs, "alt", args, attr);
    for (k, v) in kvs {
        attr.push(format!("{}={:?}", k, v.trim()))
    }
    let img = format!("<img {}>", attr.join(" "));
    match link {
        None => Some(img),
        Some(h) => Some(format!("<a {}>{}</a>", h, img)),
    }
}

pub fn link_insert(mut args: VecDeque<Value>, mut kvs: HashMap<String, Value>) -> Option<String> {
    let link = match kvs.remove("href") {
        Some(v) => v.trim().to_string(),
        None => match args.pop_front() {
            None => return None,
            Some(v) => v.trim().to_string(),
        },
    };
    let alt = match kvs.remove("alt") {
        Some(v) => v.trim().to_string(),
        None => match args.pop_front() {
            Some(v) => v.trim().to_string(),
            None => link.clone(),
        },
    };
    let mut attr = vec![format!("href={:?}", link)];
    for (k, v) in kvs {
        attr.push(format!("{}={:?}", k, v.trim()))
    }
    return Some(format!("<a href={:?}>{}</a>", attr.join(" "), alt));
}