use crate::{Value, Session, evaluate};
use std::collections::HashMap;

pub fn sample0() -> String {
"\"\\\"\"".to_string()
}

pub fn sample1() -> String {
"
[
    # 주석 잘 들어가지?
    [230602, \"카공\", \"설입 탐탐\", 3200],
    # 주석 잘 들어가지?
    [230602, \"저녁\", \"손으로 피자\", 15000], # trailing comma 잘 들어가지?,
]
".to_string()
}

pub fn sample2() -> String {
    "
[
    -20, 100, 200, 300, 400, 4000, 40000,
    5000000, 2_000_000_000, 4311810368,
    -2_000_000_000_000,
    3_1415_9265_35_8979_3238_4626_4338,
    \"Very Very Very Long String including 한글s, alphabets and numbers...\"
]
".to_string()
}

pub fn sample3() -> String {
    "{{[] : []} : {[] : []}, 3.1415926535: -200}".to_string()
}

pub fn sample4() -> String {
    // read_file("categories.json")
    String::from("[]")
}

pub fn sample5() -> String {
    "
{
    {}: {}, []: [\"\\~\"], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    {}: {}, []: [], 0: 0, true: true, false: false, None: None,
    '': '', \"\": \"\",
    []: [
        [],
        [0],
        [0, 1],
        [0, 1, 2],
        [0, 1, 2, 3, ],
        [0, 1, 2, 3, 4],
        [0, 1, 2, 3, 4, 5],
        [0, 1, 2, 3, 4, 5, 6],
        [0, 1, 2, 3, 4, 5, 6, 7],
        [0, 1, 2, 3, 4, 5, 6, 7, 8],
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
        \"1\", \"12\", \"123\",
        \"1234\", \"12345\", \"123456\",
        \"1234567\", \"12345678\", \"123456789\",
        \"123456789a\", \"123456789ab\", \"123456789abc\",
        \"123456789abcd\", \"123456789abcde\", \"123456789abcde\",
        \"123456789abcdef\", \"123456789abcdefg\",
        \"\\\"\", \"\'\\\'\"
    ]
}".to_string()
}

fn read_file(p: &str) -> String {
    let s = std::fs::read(p).unwrap();

    String::from_utf8_lossy(&s).to_string()
}

#[test]
fn parse_sample1() {
    let mut session = Session::new();
    let v = evaluate(&sample1(), &mut session).unwrap();
    let v2 = Value::from(
        vec![
            vec![
                230602.into(), Value::new_string("카공", &mut session), Value::new_string("설입 탐탐", &mut session), 3200.into()
            ],
            vec![
                230602.into(), Value::new_string("저녁", &mut session), Value::new_string("손으로 피자", &mut session), 15000.into()
            ],
        ]
    );

    assert_eq!(v, v2);
}

#[test]
fn parse_sample3() {
    let mut session = Session::new();
    let v = evaluate(&sample3(), &mut session).unwrap();
    let mut h: HashMap<Vec<u32>, Vec<u32>> = HashMap::new();
    h.insert(vec![], vec![]);
    let h: Value = h.into();

    let mut h2 = HashMap::new();
    h2.insert(h.clone(), h);
    h2.insert(Value::number_from_string("3.1415926535").unwrap(), (-200).into());

    assert_eq!(<HashMap<Value, Value> as Into<Value>>::into(h2), v);
}

#[test]
fn round_trips() {
    let mut session = Session::new();

    value_roundtrip(sample0(), &mut session);
    println!("sample 0 okay!");
    value_roundtrip(sample1(), &mut session);
    println!("sample 1 okay!");
    value_roundtrip(sample2(), &mut session);
    println!("sample 2 okay!");
    value_roundtrip(sample3(), &mut session);
    println!("sample 3 okay!");
    value_roundtrip(sample4(), &mut session);
    println!("sample 4 okay!");
    value_roundtrip(sample5(), &mut session);
    println!("sample 5 okay!");
}

fn value_roundtrip(s: String, session: &mut Session) {
    std::env::set_var("RUST_BACKTRACE", "1");
    println!("this sample: {s}");
    let v1 = evaluate(&s, session).unwrap();
    let v2 = format!("{}", v1.to_string(session));
    let v3 = evaluate(&v2, session).unwrap();
    let v4 = format!("{}", v3.to_string(session));

    assert_eq!(v1, v3);
    assert_eq!(v2, v4);

    let mut buffer = vec![];
    v1.to_bin(&mut buffer, session);
    let v5 = Value::from_bin(&buffer, &mut 0, session).unwrap();
    let mut buffer = vec![];
    v3.to_bin(&mut buffer, session);
    let v6 = Value::from_bin(&buffer, &mut 0, session).unwrap();

    assert_eq!(v1, v5);
    assert_eq!(v3, v6);
}