#[macro_use]
extern crate jzon;

use jzon::number::Number;
use jzon::{ parse, JsonValue, Null };

#[test]
fn parse_true() {
    assert_eq!(parse("true").unwrap(), true);
}

#[test]
fn parse_false() {
    assert_eq!(parse("false").unwrap(), false);
}

#[test]
fn parse_null() {
    assert!(parse("null").unwrap().is_null());
}

#[test]
fn parse_number() {
    assert_eq!(parse("3.141592653589793").unwrap(), 3.141592653589793);
}

#[test]
fn uncode_identifier() {
    assert!(parse("[C3A9] <=> [é]").is_err());
}

#[test]
fn parse_period_requires_digit() {
    assert!(parse("[1.]").is_err());
}

#[test]
fn parse_small_number() {
    assert_eq!(parse("0.05").unwrap(), 0.05);
}

#[test]
fn parse_very_long_float() {
    let parsed = parse("2.22507385850720113605740979670913197593481954635164564e-308").unwrap();

    // Handles convertion correctly
    assert_eq!(parsed.as_f64().unwrap(), 2.225073858507201e-308);

    // Exhausts u64
    assert_eq!(parsed, unsafe { Number::from_parts_unchecked(true, 2225073858507201136, -326) });
}

#[test]
fn parse_very_long_exponent() {
    let parsed = parse("1e999999999999999999999999999999999999999999999999999999999999").unwrap();

    assert_eq!(parsed, unsafe { Number::from_parts_unchecked(true, 1, 32767) });
}

#[test]
fn parse_integer() {
    assert_eq!(parse("42").unwrap(), 42);
}

#[test]
fn parse_negative_zero() {
    assert_eq!(parse("-0").unwrap(), JsonValue::from(-0f64));
}

#[test]
fn parse_negative_integer() {
    assert_eq!(parse("-42").unwrap(), -42);
}

#[test]
fn parse_number_with_leading_zero() {
    assert!(parse("01").is_err());
}

#[test]
fn parse_negative_number_with_leading_zero() {
    assert!(parse("-01").is_err());
}

#[test]
fn parse_number_with_e() {
    assert_eq!(parse("5e2").unwrap(), 500);
    assert_eq!(parse("5E2").unwrap(), 500);
}

#[test]
fn parse_number_with_positive_e() {
    assert_eq!(parse("5e+2").unwrap(), 500);
    assert_eq!(parse("5E+2").unwrap(), 500);
}

#[test]
fn parse_number_with_negative_e() {
    assert_eq!(parse("5e-2").unwrap(), 0.05);
    assert_eq!(parse("5E-2").unwrap(), 0.05);
}

#[test]
fn parse_number_with_invalid_e() {
    assert!(parse("0e").is_err());
}

#[test]
fn parse_large_number() {
    assert_eq!(parse("18446744073709551616").unwrap(), 18446744073709552000f64);
}

#[test]
fn parse_array() {
    assert_eq!(parse(r#"[10, "foo", true, null]"#).unwrap(), array![
        10,
        "foo",
        true,
        null
    ]);

    assert_eq!(parse("[]").unwrap(), array![]);

    assert_eq!(parse("[[]]").unwrap(), array![[]]);
}

#[test]
fn parse_object() {
    // Without trailing comma
    assert_eq!(parse(r#"

    {
        "foo": "bar",
        "num": 10
    }

    "#).unwrap(), object!{
        foo: "bar",
        num: 10
    });

    // Trailing comma in macro
    assert_eq!(parse(r#"

    {
        "foo": "bar",
        "num": 10
    }

    "#).unwrap(), object!{
        foo: "bar",
        num: 10,
    });
}

#[test]
fn parse_object_duplicate_fields() {
    assert_eq!(parse(r#"

    {
        "foo": 0,
        "bar": 1,
        "foo": 2
    }

    "#).unwrap(), object!{
        foo: 2,
        bar: 1
    });
}

#[test]
fn parse_object_with_array(){
    assert_eq!(parse(r#"

    {
        "foo": [1, 2, 3]
    }

    "#).unwrap(), object!{
        foo: [1, 2, 3]
    });
}

#[test]
fn parse_nested_object() {
    assert_eq!(parse(r#"

    {
        "l10n": [ {
            "product": {
                "inStock": {
                    "DE": "Lieferung innerhalb von 1-3 Werktagen"
                }
            }
        } ]
    }

    "#).unwrap(), object!{
        l10n: [ {
            product: {
                inStock: {
                    DE: "Lieferung innerhalb von 1-3 Werktagen"
                }
            }
        } ]
    });
}

#[test]
fn parse_and_index_from_object() {
    let data = parse("{ \"pi\": 3.14 }").unwrap();
    let ref pi = data["pi"];

    assert_eq!(pi, 3.14);
}

#[test]
fn parse_and_index_mut_from_object() {
    let mut data = parse(r#"

    {
        "foo": 100
    }

    "#).unwrap();

    assert_eq!(data["foo"], 100);

    data["foo"] = 200.into();

    assert_eq!(data["foo"], 200);
}

#[test]
fn parse_and_index_mut_from_null() {
    let mut data = parse("null").unwrap();

    assert!(data["foo"]["bar"].is_null());

    // test that data didn't coerce to object
    assert!(data.is_null());

    data["foo"]["bar"] = 100.into();

    assert!(data.is_object());
    assert_eq!(data["foo"]["bar"], 100);

    assert_eq!(data.dump(), r#"{"foo":{"bar":100}}"#);
}

#[test]
fn parse_and_index_from_array() {
    let data = parse(r#"[100, 200, false, null, "foo"]"#).unwrap();

    assert_eq!(data[0], Number::from(100));
    assert_eq!(data[1], 200);
    assert_eq!(data[2], false);
    assert_eq!(data[3], Null);
    assert_eq!(data[4], "foo");
    assert_eq!(data[5], Null);
}

#[test]
fn parse_and_index_mut_from_array() {
    let mut data = parse(r#"[100, 200, false, null, "foo"]"#).unwrap();

    assert!(data[3].is_null());
    assert!(data[5].is_null());

    data[3] = "modified".into();
    data[5] = "implicid push".into();

    assert_eq!(data[3], "modified");
    assert_eq!(data[5], "implicid push");
}

#[test]
fn parse_escaped_characters() {
    let data = parse(r#"

    "\r\n\t\b\f\\\/\""

    "#).unwrap();

    assert!(data.is_string());
    assert_eq!(data, "\r\n\t\u{8}\u{c}\\/\"");
}

#[test]
fn parse_escaped_unicode() {
    let data = parse(r#"

    "\u2764\ufe0f"

    "#).unwrap();

    assert_eq!(data, "❤️");
}

#[test]
fn parse_escaped_unicode_surrogate() {
    let data = parse(r#"

    "\uD834\uDD1E"

    "#).unwrap();

    assert_eq!(data, "𝄞");
}

#[test]
fn parse_escaped_unicode_surrogate_fail() {
    let err = parse(r#"

    "\uD834 \uDD1E"

    "#);

    assert!(err.is_err());
}

#[test]
fn parse_deeply_nested_arrays_and_objects() {
    let depth = 256;
    let mut text = String::new();
    for _ in 0..depth {
        text.push_str("[{\"a\":");
    }
    text.push_str("null");
    for _ in 0..depth {
        text.push_str("}]");
    }

    parse(&text).unwrap();

    assert!(true);
}

#[test]
fn parse_error_after_depth_limit() {
    let depth = 5000;
    let mut text = String::new();
    for _ in 0..depth {
        text.push_str("[{\"a\":");
    }
    text.push_str("null");
    for _ in 0..depth {
        text.push_str("}]");
    }

    assert_eq!(parse(&text), Err(jzon::Error::ExceededDepthLimit));
}

#[test]
fn does_not_panic_on_single_unicode_char() {
    let bytes = vec![0xC3, 0xA5];

    let string = String::from_utf8(bytes).unwrap();

    assert!(parse(&string).is_err());
}

#[test]
fn does_not_panic_on_single_zero() {
    let source = "0";

    parse(source).unwrap();
}

#[test]
fn does_not_panic_on_huge_numbers() {
    let mut string = String::from("8");

    for _ in 1..32787 {
        string.push('0');
    }

    let _ = jzon::parse(&string);
}
