use std::fmt::{Display};
use std::io::{self, Write};
use std::iter::repeat;


/// An `XMLWriter` is a wrapper over a `Write` value that provides
/// helper functions for generating (mostly) well-formed XML.
pub struct XMLWriter<'a>{
    writer: &'a mut Write,
    indent: usize,
}

impl<'a> XMLWriter<'a> {
    /// Create a new XMLWriter and add an XML version/encoding header
    /// on top
    pub fn start(buf: &'a mut Write) -> io::Result<XMLWriter<'a>> {
        writeln!(buf, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>")?;
        Ok(XMLWriter { writer: buf, indent: 0, })
    }

    fn indent(&mut self) -> io::Result<()> {
        write!(self.writer, "{}", repeat(' ').take(self.indent).collect::<String>())?;
        Ok(())
    }

    /// create an complete tag with the given attributes
    pub fn tag(&mut self, name: &str, attrs: &[(&str, &Display)]) -> io::Result<()> {
        self.indent()?;
        write!(self.writer, "<{}", name)?;
        for &(k, v) in attrs {
            write!(self.writer, " {}=\"{}\"", k, v)?;
        }
        writeln!(self.writer, "/>")?;
        Ok(())
    }

    /// create an open tag with the given attributes; you must close
    /// it manually later
    pub fn open(&mut self, name: &str, attrs: &[(&str, &Display)]) -> io::Result<()> {
        self.indent()?;
        write!(self.writer, "<{}", name)?;
        for &(k, v) in attrs {
            write!(self.writer, " {}=\"{}\"", k, v)?;
        }
        writeln!(self.writer, ">")?;
        Ok(())
    }

    /// close a tag with the given attributes
    pub fn close(&mut self, name: &str) -> io::Result<()> {
        self.indent()?;
        write!(self.writer, "</{}>", name)?;
        Ok(())
    }

    /// create an open-close tag pair with the given attributes,
    /// calling the provided function in order to fill in the interior
    /// tags as well
    pub fn block<F>(
        &mut self,
        name: &str,
        attrs: &[(&str, &Display)],
        cb: F
    ) -> io::Result<()>
    where F: FnOnce(&mut XMLWriter<'a>) -> io::Result<()>
    {
        self.indent()?;
        write!(self.writer, "<{}", name)?;
        for &(k, v) in attrs {
            write!(self.writer, " {}=\"{}\"", k, v)?;
        }
        writeln!(self.writer, ">")?;
        self.indent += 2;
        cb(self)?;
        self.indent -= 2;
        write!(self.writer, "</{}>", name)?;
        Ok(())
    }

}
