Documentation
use super::RJson;
use core::ops;
use once_cell::sync::Lazy;
use std::collections::HashMap;

static RJSON_NULL: Lazy<RJson> = Lazy::new(|| RJson::new(Json::Null));

#[derive(Debug, Clone)]
pub enum Json {
    Null,
    Bool(bool),
    Number(serde_json::Number),
    String(String),
    Array(Vec<RJson>),
    Object(HashMap<String, RJson>),
}

impl From<serde_json::Value> for Json {
    fn from(value: serde_json::Value) -> Self {
        match value {
            serde_json::Value::Null => Json::Null,
            serde_json::Value::Bool(b) => Json::Bool(b),
            serde_json::Value::Number(n) => Json::Number(n),
            serde_json::Value::String(s) => Json::String(s),
            serde_json::Value::Array(a) => Json::Array(a.into_iter().map(|v| RJson::new(v)).collect()),
            serde_json::Value::Object(o) => Json::Object(o.into_iter().map(|(k, v)| (k, RJson::new(v))).collect()),
        }
    }
}
impl From<Json> for serde_json::Value {
    fn from(value: Json) -> Self {
        match &value {
            Json::Null => serde_json::Value::Null,
            Json::Bool(b) => serde_json::Value::Bool(*b),
            Json::Number(n) => serde_json::Value::Number(n.clone()),
            Json::String(s) => serde_json::Value::String(s.clone()),
            Json::Array(a) => {
                let vec = serde_json::Value::Array(
                    a.into_iter()
                        .map(|j| {
                            let j: Json = j.clone().into();
                            serde_json::Value::from(j)
                        })
                        .collect(),
                );
                vec
            }
            Json::Object(o) => serde_json::Value::Object(
                o.into_iter()
                    .map(|(k, j)| {
                        let j: Json = j.clone().into();
                        let v = serde_json::Value::from(j);
                        (k.clone(), v)
                    })
                    .collect(),
            ),
        }
    }
}

pub trait Index {
    #[doc(hidden)]
    fn index_into<'v>(&self, v: &'v Json) -> Option<&'v RJson>;
    #[doc(hidden)]
    fn index_into_mut<'v>(&self, v: &'v mut Json) -> Option<&'v mut RJson>;
    #[doc(hidden)]
    fn index_or_insert<'v>(&self, v: &'v mut Json) -> &'v mut RJson;
}
impl Index for usize {
    fn index_into<'v>(&self, v: &'v Json) -> Option<&'v RJson> {
        match v {
            Json::Array(vec) => vec.get(*self),
            _ => None,
        }
    }
    fn index_into_mut<'v>(&self, v: &'v mut Json) -> Option<&'v mut RJson> {
        match v {
            Json::Array(vec) => vec.get_mut(*self),
            _ => None,
        }
    }
    fn index_or_insert<'v>(&self, v: &'v mut Json) -> &'v mut RJson {
        match v {
            Json::Array(vec) => {
                let len = vec.len();
                vec.get_mut(*self).unwrap_or_else(|| panic!("cannot access index {} of JSON array of length {}", self, len))
            }
            _ => panic!("cannot access index {} of JSON {:?}", self, v),
        }
    }
}
impl Index for str {
    fn index_into<'v>(&self, v: &'v Json) -> Option<&'v RJson> {
        match v {
            Json::Object(map) => map.get(self),
            _ => None,
        }
    }
    fn index_into_mut<'v>(&self, v: &'v mut Json) -> Option<&'v mut RJson> {
        match v {
            Json::Object(map) => map.get_mut(self),
            _ => None,
        }
    }
    fn index_or_insert<'v>(&self, v: &'v mut Json) -> &'v mut RJson {
        if let Json::Null = v {
            *v = Json::Object(HashMap::new());
        }
        match v {
            Json::Object(map) => map.entry(self.to_owned()).or_insert(RJson::new(Json::Null)),
            _ => panic!("cannot access key {:?} in JSON {:?}", self, v),
        }
    }
}
impl Index for String {
    fn index_into<'v>(&self, v: &'v Json) -> Option<&'v RJson> {
        self[..].index_into(v)
    }
    fn index_into_mut<'v>(&self, v: &'v mut Json) -> Option<&'v mut RJson> {
        self[..].index_into_mut(v)
    }
    fn index_or_insert<'v>(&self, v: &'v mut Json) -> &'v mut RJson {
        self[..].index_or_insert(v)
    }
}
impl<T> Index for &T
where
    T: ?Sized + Index,
{
    fn index_into<'v>(&self, v: &'v Json) -> Option<&'v RJson> {
        (**self).index_into(v)
    }
    fn index_into_mut<'v>(&self, v: &'v mut Json) -> Option<&'v mut RJson> {
        (**self).index_into_mut(v)
    }
    fn index_or_insert<'v>(&self, v: &'v mut Json) -> &'v mut RJson {
        (**self).index_or_insert(v)
    }
}

impl<I> ops::Index<I> for Json
where
    I: Index,
{
    type Output = RJson;
    fn index(&self, index: I) -> &Self::Output {
        index.index_into(self).unwrap_or(&RJSON_NULL)
    }
}

impl<I> ops::IndexMut<I> for Json
where
    I: Index,
{
    fn index_mut(&mut self, index: I) -> &mut Self::Output {
        index.index_or_insert(self)
    }
}

impl Json {
    pub fn get_ptr_address(&self) -> usize {
        self as *const Json as usize
    }
    pub fn index<I: Index>(&self, index: &I) -> RJson {
        self[index].clone()
    }
    pub fn get<I: Index>(&self, index: &I) -> RJson {
        self.index(index)
    }
    pub fn set<I: Index, V: Into<RJson>>(&mut self, index: &I, value: V) {
        let value: RJson = value.into();
        let value_json_ptr = value.get_json_ptr_address();
        let old_value_json_ptr = self.index(&index).get_json_ptr_address();
        crate::syn::effect::Effect::replace_target_ptr(old_value_json_ptr, value_json_ptr);
        self[index] = value;
    }
}

impl Drop for Json {
    fn drop(&mut self) {
        log::trace!("drop inner json address {:?}", self.get_ptr_address());
        crate::syn::effect::Effect::remove_target_ptr(self.get_ptr_address());
    }
}