use crate::types::{Function, Object, RObject, RTDType};
use crate::{tdkit, types};

macro_rules! sfrom_json {
  ($rt:ident, $stn:ident) => {
    |json| -> Option<Box<$rt>> {
      match serde_json::from_str::<Box<types::$stn>>(&tdkit::fill_json_struct(json)[..]) {
        Ok(t) => Some(t),
        Err(e) => {
          eprintln!("{:?}", e);
          None
        }
      }
    }
  }
}

impl <'a, Fnc: Function> Function for &'a Fnc {}

impl <'a, Fnc: Function> Function for &'a mut Fnc {}

impl <'a, Obj: Object> Object for &'a Obj {}

impl <'a, Obj: Object> Object for &'a mut Obj {}

impl <'a, RObj: RObject> RObject for &'a RObj {
  fn td_name(&self) -> &'static str {
    (*self).td_name()
  }

  fn td_type(&self) -> RTDType {
    (*self).td_type()
  }

  fn to_json(&self) -> String {
    (*self).to_json()
  }
}

impl <'a, RObj: RObject> RObject for &'a mut RObj {
  fn td_name(&self) -> &'static str {
    (**self).td_name()
  }

  fn td_type(&self) -> RTDType {
    (**self).td_type()
  }

  fn to_json(&self) -> String {
    (**self).to_json()
  }
}

impl Object {
  pub fn from_json<S: AsRef<str>>(json: S) -> Option<Box<Object>> {
    let json = json.as_ref();
    let struct_name = tdkit::extra_struct(json);
    if struct_name.is_none() {
      eprintln!("JSON FAIL => {}", json);
      return None;
    }
    let struct_name = struct_name.unwrap();
    let ret: Option<Box<Object>> = match &struct_name[..] {
      {% for item in sp.objects %}{% if not item.is_trait %}"{{item.clz_name}}" => sfrom_json!(Object, {{item.clz_name}})(json),
      {% endif %}{% endfor %}
      _ => None
    };
    ret
  }
}

impl Function {
  pub fn from_json<S: AsRef<str>>(json: S) -> Option<Box<Function>> {
    let json = json.as_ref();
    let struct_name = tdkit::extra_struct(json);
    if struct_name.is_none() {
      eprintln!("JSON FAIL => {}", json);
      return None;
    }
    let struct_name = struct_name.unwrap();
    let ret: Option<Box<Function>> = match &struct_name[..] {
      {% for item in sp.functions %}{% if not item.is_trait %}"{{item.clz_name}}" => sfrom_json!(Function, {{item.clz_name}})(json),
      {% endif %}{% endfor %}
      _ => None
    };
    ret
  }
}

