use super::Format;
use std::io::{self, Write};

pub struct CssBuf {
    buf: Vec<u8>,
    format: Format,
    indent: usize,
}

impl CssBuf {
    pub fn new(format: Format) -> Self {
        Self {
            buf: Vec::new(),
            format,
            indent: 0,
        }
    }
    pub fn take(self) -> Vec<u8> {
        self.buf
    }
    pub(crate) fn len(&self) -> usize {
        self.buf.len()
    }
    pub(crate) fn format(&self) -> Format {
        self.format
    }
    pub(crate) fn indent_level(&self) -> usize {
        self.indent
    }

    pub fn start_block(&mut self) {
        self.add_one(" {\n", "{");
        self.indent += 2;
    }
    pub fn end_block(&mut self) {
        self.pop_nl();
        if self.format.is_compressed() && self.buf.last() == Some(&b';') {
            self.buf.pop();
        }
        self.indent -= 2;
        if self.buf.last() != Some(&b'{') {
            self.do_indent();
        }
        self.add_one("}\n", "}");
    }
    pub fn pop_nl(&mut self) {
        if self.buf.last() == Some(&b'\n') {
            self.buf.pop();
        }
    }

    pub fn do_indent(&mut self) {
        self.add_str(self.format.get_indent(self.indent));
    }
    pub(crate) fn do_indent_no_nl(&mut self) {
        let stuff = self.format.get_indent(self.indent);
        if stuff.len() > 1 {
            self.add_str(&stuff[1..]);
        }
    }

    pub fn add_str(&mut self, sub: &str) {
        self.buf.extend_from_slice(sub.as_bytes());
    }
    pub(crate) fn add_char(&mut self, ch: char) {
        let mut buf = [b'0'; 6];
        self.add_str(ch.encode_utf8(&mut buf))
    }
    pub fn add_one(&mut self, normal: &str, compressed: &str) {
        self.add_str(if self.format.is_compressed() {
            compressed
        } else {
            normal
        });
    }
    pub fn opt_nl(&mut self) {
        if !self.format.is_compressed()
            && !self.buf.is_empty()
            && !self.buf.ends_with(b"\n\n")
        {
            self.add_str("\n");
        }
    }
}

impl Write for CssBuf {
    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
        self.buf.extend_from_slice(data);
        Ok(data.len())
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}
