#[derive(Debug, Clone, PartialEq)]
pub enum JsonZero<'a> {
    Null,
    Bool(bool),
    Str(&'a [u8]),
    Num(f64),
    Array(Vec<JsonZero<'a>>),
    Object(Vec<(&'a [u8], JsonZero<'a>)>),
}

static JSON: &[u8] = include_bytes!("samples/sample.json");

use neure::err::Error;
use neure::neu::range;
use neure::prelude::*;

#[derive(Debug, Default)]
pub struct JsonParser;

impl JsonParser {
    pub fn parse(pat: &[u8]) -> Result<JsonZero<'_>, Error> {
        let parser = regex::rec_parser(|ctor| {
            let ws = u8::is_ascii_whitespace.many0();
            let sign = neu!((b'+', b'-')).opt();
            let digit = range(b'0'..=b'9').many1();
            let dec = b".".then(digit).pat();
            let num = sign.then(digit).then(dec.or(regex::empty()));
            let num = ctor::Adapter::dyn_box(num.pat().try_map(&Self::to_digit));

            let escape = neu!((b'\r', b'\t', b'\n', b'\\', b'\"'));
            let escape = b'\\'.then(escape);
            let cond = neu::regex_cond(regex::not(escape));
            let str_val = b'\"'
                .not()
                .many1()
                .set_cond(cond)
                .or(escape)
                .repeat(0..)
                .pat();
            let str = ctor::Adapter::dyn_box(str_val.enclose(b"\"", b"\""));
            let str = str.map(JsonZero::Str);

            let bool_t = regex::literal(b"true").map(|_| JsonZero::Bool(true));
            let bool_f = regex::literal(b"false").map(|_| JsonZero::Bool(false));
            let empty = regex::literal(b"empty").map(|_| JsonZero::Null);

            let ele = num.or(str.or(bool_t.or(bool_f.or(empty.or(ctor.clone())))));
            let ele = ctor::Adapter::rc(ele.suffix(ws).prefix(ws));

            let key = regex!((u8::is_ascii_alphabetic.or(u8::is_ascii_digit), b'_')+);
            let key = key.enclose(b"\"", b"\"");
            let key = key.suffix(ws).prefix(ws);
            let obj = key.sep_once(b":", ele.clone());
            let obj =
                ctor::Adapter::dyn_box(obj.sep(b",").enclose(b"{", b"}")).map(JsonZero::Object);

            let array = ele.clone().sep(b",").enclose(b"[", b"]");
            let array = array.map(JsonZero::Array);

            obj.or(array)
        });
        let mut ctx = BytesCtx::new(pat);

        ctx.ctor(&parser)
    }

    pub fn to_digit<'a>(val: &[u8]) -> Result<JsonZero<'a>, Error> {
        std::str::from_utf8(val)
            .map_err(|_| Error::Utf8Error)?
            .parse::<f64>()
            .map_err(|_| Error::FromStr)
            .map(JsonZero::Num)
    }
}

pub fn main() {
    tracing_subscriber::fmt::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .init();
    dbg!(JsonParser::parse(JSON).unwrap());
}
