use super::{Object, Value};
use crate::prelude::*;
use std::io;
use std::io::Write;
use value_trait::generator::{
    DumpGenerator, PrettyGenerator, PrettyWriterGenerator, WriterGenerator,
};
impl<'value> Writable for Value<'value> {
    #[cfg_attr(not(feature = "no-inline"), inline)]
    fn encode(&self) -> String {
        let mut g = DumpGenerator::new();
        let _r = g.write_json(self);
        g.consume()
    }
    #[cfg_attr(not(feature = "no-inline"), inline)]
    fn encode_pp(&self) -> String {
        let mut g = PrettyGenerator::new(2);
        let _r = g.write_json(self);
        g.consume()
    }
    #[cfg_attr(not(feature = "no-inline"), inline)]
    fn write<'writer, W>(&self, w: &mut W) -> io::Result<()>
    where
        W: 'writer + Write,
    {
        let mut g = WriterGenerator::new(w);
        g.write_json(self)
    }
    #[cfg_attr(not(feature = "no-inline"), inline)]
    fn write_pp<'writer, W>(&self, w: &mut W) -> io::Result<()>
    where
        W: 'writer + Write,
    {
        let mut g = PrettyWriterGenerator::new(w, 2);
        g.write_json(self)
    }
}
trait Generator: BaseGenerator {
    type T: Write;
    #[cfg_attr(not(feature = "no-inline"), inline)]
    fn write_object(&mut self, object: &Object) -> io::Result<()> {
        if object.is_empty() {
            self.write(b"{}")
        } else {
            let mut iter = object.iter();
            stry!(self.write(b"{"));
            let (key, value) = if let Some(v) = iter.next() {
                v
            } else {
                unreachable!();
            };
            self.indent();
            stry!(self.new_line());
            stry!(self.write_simple_string(key));
            stry!(self.write_min(b": ", b':'));
            stry!(self.write_json(value));
            for (key, value) in iter {
                stry!(self.write(b","));
                stry!(self.new_line());
                stry!(self.write_simple_string(key));
                stry!(self.write_min(b": ", b':'));
                stry!(self.write_json(value));
            }
            self.dedent();
            stry!(self.new_line());
            self.write(b"}")
        }
    }
    #[cfg_attr(not(feature = "no-inline"), inline)]
    fn write_json(&mut self, json: &Value) -> io::Result<()> {
        match *json {
            Value::Static(StaticNode::Null) => self.write(b"null"),
            Value::Static(StaticNode::I64(number)) => self.write_int(number),
            #[cfg(feature = "128bit")]
            Value::Static(StaticNode::I128(number)) => self.write_int(number),
            Value::Static(StaticNode::U64(number)) => self.write_int(number),
            #[cfg(feature = "128bit")]
            Value::Static(StaticNode::U128(number)) => self.write_int(number),
            Value::Static(StaticNode::F64(number)) => self.write_float(number),
            Value::Static(StaticNode::Bool(true)) => self.write(b"true"),
            Value::Static(StaticNode::Bool(false)) => self.write(b"false"),
            Value::String(ref string) => self.write_string(string),
            Value::Array(ref array) => {
                if array.is_empty() {
                    self.write(b"[]")
                } else {
                    let mut iter = <[Value]>::iter(array);
                    let item = if let Some(v) = iter.next() {
                        v
                    } else {
                        unreachable!();
                    };
                    stry!(self.write(b"["));
                    self.indent();
                    stry!(self.new_line());
                    stry!(self.write_json(item));
                    for item in iter {
                        stry!(self.write(b","));
                        stry!(self.new_line());
                        stry!(self.write_json(item));
                    }
                    self.dedent();
                    stry!(self.new_line());
                    self.write(b"]")
                }
            }
            Value::Object(ref object) => self.write_object(object),
        }
    }
}
trait FastGenerator: BaseGenerator {
    type T: Write;
    #[cfg_attr(not(feature = "no-inline"), inline)]
    fn write_object(&mut self, object: &Object) -> io::Result<()> {
        if object.is_empty() {
            self.write(b"{}")
        } else {
            let mut iter = object.iter();
            stry!(self.write(b"{\""));
            let (key, value) = if let Some(v) = iter.next() {
                v
            } else {
                unreachable!();
            };
            stry!(self.write_simple_str_content(key));
            stry!(self.write(b"\":"));
            stry!(self.write_json(value));
            for (key, value) in iter {
                stry!(self.write(b",\""));
                stry!(self.write_simple_str_content(key));
                stry!(self.write(b"\":"));
                stry!(self.write_json(value));
            }
            self.write(b"}")
        }
    }
    #[cfg_attr(not(feature = "no-inline"), inline)]
    fn write_json(&mut self, json: &Value) -> io::Result<()> {
        match *json {
            Value::Static(StaticNode::Null) => self.write(b"null"),
            Value::Static(StaticNode::I64(number)) => self.write_int(number),
            #[cfg(feature = "128bit")]
            Value::Static(StaticNode::I128(number)) => self.write_int(number),
            Value::Static(StaticNode::U64(number)) => self.write_int(number),
            #[cfg(feature = "128bit")]
            Value::Static(StaticNode::U128(number)) => self.write_int(number),
            Value::Static(StaticNode::F64(number)) => self.write_float(number),
            Value::Static(StaticNode::Bool(true)) => self.write(b"true"),
            Value::Static(StaticNode::Bool(false)) => self.write(b"false"),
            Value::String(ref string) => self.write_string(string),
            Value::Array(ref array) => {
                if array.is_empty() {
                    self.write(b"[]")
                } else {
                    let mut iter = <[Value]>::iter(array);
                    let item = if let Some(v) = iter.next() {
                        v
                    } else {
                        unreachable!();
                    };
                    stry!(self.write(b"["));
                    stry!(self.write_json(item));
                    for item in iter {
                        stry!(self.write(b","));
                        stry!(self.write_json(item));
                    }
                    self.write(b"]")
                }
            }
            Value::Object(ref object) => self.write_object(object),
        }
    }
}
impl FastGenerator for DumpGenerator {
    type T = Vec<u8>;
}
impl Generator for PrettyGenerator {
    type T = Vec<u8>;
}
impl<'writer, W> FastGenerator for WriterGenerator<'writer, W>
where
    W: Write,
{
    type T = W;
}
impl<'writer, W> Generator for PrettyWriterGenerator<'writer, W>
where
    W: Write,
{
    type T = W;
}
#[cfg(test)]
mod test {
    use super::Value;
    use crate::prelude::*;
    #[test]
    fn null() {
        assert_eq!(Value::Static(StaticNode::Null).encode(), "null");
    }
    #[test]
    fn bool_true() {
        assert_eq!(Value::Static(StaticNode::Bool(true)).encode(), "true");
    }
    #[test]
    fn bool_false() {
        assert_eq!(Value::Static(StaticNode::Bool(false)).encode(), "false");
    }
    #[test]
    fn obj() {
        let mut o = Value::object();
        o.insert("k", ()).expect("insert");
        assert_eq!(o.encode(), r#"{"k":null}"#);
    }
    fn assert_str(from: &str, to: &str) {
        assert_eq!(Value::String(from.into()).encode(), to);
    }
    #[test]
    fn string() {
        assert_str("this is a test", r#""this is a test""#);
        assert_str(r#"this is a test ""#, r#""this is a test \"""#);
        assert_str(r#"this is a test """#, r#""this is a test \"\"""#);
        assert_str(
            "this is a test a long test that should span the 32 byte boundary",
            r#""this is a test a long test that should span the 32 byte boundary""#,
        );
        assert_str(
            r#"this is a test a "long" test that should span the 32 byte boundary"#,
            r#""this is a test a \"long\" test that should span the 32 byte boundary""#,
        );
        assert_str(
            r#"this is a test a \"long\" test that should span the 32 byte boundary"#,
            r#""this is a test a \\\"long\\\" test that should span the 32 byte boundary""#,
        );
    }
}