Documentation
use std::{collections::HashMap, io::Read};

use futures::FutureExt;
use stap::{just, many0, many1, tag, Anchor, Cursor, Input, InputRef, Parsing};

async fn skip_whitespace(iref: &mut InputRef<'_, u8>) {
    many0(iref, |&c| c.is_ascii_whitespace()).await;
}

async fn null(iref: &mut InputRef<'_, u8>) -> Result<(), ()> {
    tag(iref, b"null").await.map(|_| ())
}

async fn bool(iref: &mut InputRef<'_, u8>) -> Result<bool, ()> {
    if tag(iref, b"true").await.is_ok() {
        Ok(true)
    } else if tag(iref, b"false").await.is_ok() {
        Ok(false)
    } else {
        Err(())
    }
}

async fn number(iref: &mut InputRef<'_, u8>) -> Result<f64, ()> {
    let mut anchor = Anchor::new(iref);

    let _ = just(&mut anchor, b'-').await;
    many1(&mut anchor, |&c| c.is_ascii_digit()).await?;

    if just(&mut anchor, b'.').await.is_ok() {
        many0(&mut anchor, |&c| c.is_ascii_digit()).await;
    }

    if just(&mut anchor, b'e').await.is_ok() || just(&mut anchor, b'E').await.is_ok() {
        let _ = just(&mut anchor, b'-').await;
        let _ = many1(&mut anchor, |&c| c.is_ascii_digit()).await;
    }

    let range = anchor.range();
    anchor.forget();

    iref.scope_cursor(move |c| {
        let s = std::str::from_utf8(&c.buf()[range]).unwrap();
        let n: f64 = s.parse().unwrap();
        Ok(n)
    })
}

// TODO: unescape string
async fn string(iref: &mut InputRef<'_, u8>) -> Result<String, ()> {
    just(iref, b'"').await?;

    let mut last = b'"';
    let range = many0(iref, |&c| {
        let ret = last == b'\\' || c != b'"';
        last = c;

        ret
    })
    .await;

    just(iref, b'"').await?;

    iref.scope_cursor(move |c| {
        let s = std::str::from_utf8(&c.buf()[range]).unwrap();
        let s = s.to_string();
        Ok(s)
    })
}

async fn array(iref: &mut InputRef<'_, u8>) -> Result<Vec<Json>, ()> {
    just(iref, b'[').await?;

    let mut array = Vec::new();

    let mut first = true;

    loop {
        skip_whitespace(iref).await;

        if just(iref, b']').await.is_ok() {
            break;
        }

        if !first {
            just(iref, b',').await?;
            skip_whitespace(iref).await;
        }

        first = false;

        let value = Box::pin(json(iref)).await?;

        array.push(value);
    }

    Ok(array)
}

async fn object(iref: &mut InputRef<'_, u8>) -> Result<HashMap<String, Json>, ()> {
    just(iref, b'{').await?;

    let mut map = HashMap::new();

    let mut first = true;

    loop {
        skip_whitespace(iref).await;

        if just(iref, b'}').await.is_ok() {
            break;
        }

        if !first {
            just(iref, b',').await?;
            skip_whitespace(iref).await;
        }

        first = false;

        let key = string(iref).await?;

        skip_whitespace(iref).await;

        just(iref, b':').await?;

        skip_whitespace(iref).await;

        let value = Box::pin(json(iref)).await?;

        map.insert(key, value);
    }

    Ok(map)
}

async fn json(iref: &mut InputRef<'_, u8>) -> Result<Json, ()> {
    if let Ok(()) = null(iref).await {
        return Ok(Json::Null);
    }

    if let Ok(b) = bool(iref).await {
        return Ok(Json::Bool(b));
    }

    if let Ok(n) = number(iref).await {
        return Ok(Json::Number(n));
    }

    if let Ok(s) = string(iref).await {
        return Ok(Json::String(s));
    }

    if let Ok(a) = array(iref).await {
        return Ok(Json::Array(a));
    }

    if let Ok(o) = object(iref).await {
        return Ok(Json::Object(o));
    }

    Err(())
}

#[derive(Debug, PartialEq)]
#[allow(dead_code)]
enum Json {
    Null,
    Bool(bool),
    String(String),
    Number(f64),
    Array(Vec<Json>),
    Object(HashMap<String, Json>),
}

fn main() {
    println!("Please input a JSON string\nIt will exit when parsing has succeeded or failed:");

    let mut input = Input::new(Cursor {
        buf: Vec::new(),
        index: 0,
    });

    let mut parsing = Parsing::new(&mut input, |mut iref| {
        async move { json(&mut iref).await }.boxed_local()
    });

    let mut buf = [0; 4096];
    let mut stdin = std::io::stdin().lock();
    while !parsing.poll() {
        let n = stdin.read(&mut buf).unwrap();

        if n == 0 {
            break;
        }

        parsing.cursor_mut().buf.extend_from_slice(&buf[..n]);
    }

    dbg!(parsing.into_result());
}

#[test]
fn test_json() {
    let example =
        r#"{"a": "b", "c": {"d": "e"}, "f": 114514, "g": [1, "2"], "h": true, "i": null}"#;

    let mut input = Input::new(Cursor {
        buf: example.as_bytes().to_vec(),
        index: 0,
    });

    let mut parsing = Parsing::new(&mut input, |mut iref| {
        async move { json(&mut iref).await }.boxed_local()
    });

    assert!(parsing.poll());

    let expected = Json::Object(
        vec![
            ("a".to_string(), Json::String("b".to_string())),
            (
                "c".to_string(),
                Json::Object(
                    vec![("d".to_string(), Json::String("e".to_string()))]
                        .into_iter()
                        .collect(),
                ),
            ),
            ("f".to_string(), Json::Number(114514f64)),
            (
                "g".to_string(),
                Json::Array(vec![Json::Number(1f64), Json::String("2".to_string())]),
            ),
            ("h".to_string(), Json::Bool(true)),
            ("i".to_string(), Json::Null),
        ]
        .into_iter()
        .collect(),
    );

    assert_eq!(parsing.into_result().unwrap(), Ok(expected));
}