use types::*;

pub trait Show {
    fn show(&self, buf: &mut String);
}

impl Show for Stmt {
    fn show(&self, buf: &mut String) {
        match *self {
            Stmt::Let(ref p, _, _, ref e) => {
                p.show(buf);
                buf.push_str(" = ");
                e.show(buf);
            }
            Stmt::Expr(ref e) =>
                e.show(buf),
        }
        buf.push_str(";\n");
    }
}


impl Show for Pat {
    fn show(&self, buf: &mut String) {
        match *self {
            Pat::Var(ref i) => buf.push_str(i),
            Pat::Tuple(ref ps) => {
                buf.push_str("(");
                for (n, t) in ps.iter().enumerate() {
                    if n > 0 {
                        buf.push_str(", ");
                    }
                    t.show(buf);
                }
                buf.push_str(")");
            }
        }
    }
}


impl Show for Value {
    fn show(&self, buf: &mut String) {
        match *self {
            Value::Unit => buf.push_str("()"),
            Value::Lit(ref l) => l.show(buf),
            Value::Tuple(ref ts) => {
                buf.push_str("(");
                for (n, t) in ts.iter().enumerate() {
                    if n > 0 {
                        buf.push_str(", ");
                    }
                    t.show(buf);
                }
                buf.push_str(")");
            }
            Value::Closure(ref p, ref e, _) => {
                buf.push_str("(\\");
                p.show(buf);
                buf.push_str(".");
                e.show(buf);
                buf.push_str(")");
            }
        }
    }
}

impl Show for Literal {
    fn show(&self, buf: &mut String) {
        match *self {
            Literal::Int(i) =>
                buf.push_str(&format!("{}", i)),
        }
    }
}

impl Show for Expr {
    fn show(&self, buf: &mut String) {
        match *self {
            Expr::Var(ref v) => buf.push_str(v),
            Expr::Lit(ref l) => l.show(buf),
            Expr::Fn(ref p, ref e) => {
                buf.push_str("fn ");
                p.show(buf);
                buf.push_str(" => ");
                e.show(buf);
            }
            Expr::App(ref e1, ref e2) => {
                buf.push_str("(");
                e1.show(buf);
                buf.push_str(")(");
                e2.show(buf);
                buf.push_str(")");
            }
            Expr::Do(ref ss) => {
                buf.push_str("do\n");
                for s in ss.iter() {
                    s.show(buf);
                }
                buf.push_str("end\n");
            }
            Expr::Tuple(ref ts) => {
                buf.push_str("(");
                for (n, t) in ts.iter().enumerate() {
                    if n > 0 {
                        buf.push_str(", ");
                    }
                    t.show(buf);
                }
                buf.push_str(")");
            }
        }
    }
}

pub fn show<S: Show>(s: &S) -> String {
    let mut buf = String::new();
    s.show(&mut buf);
    buf
}
