use core::borrow::Borrow;
use std::sync::Arc;

use rtdlib::tdjson;
use rtdlib::types::Function;

use crate::api::*;
use crate::tglog;

#[derive(Debug, Clone)]
pub struct Api {
  tdlib: Arc<tdjson::Tdlib>
}

impl Default for Api {
  fn default() -> Self {
    Api::new(tdjson::Tdlib::new())
  }
}

impl Api {
  pub fn new(tdlib: tdjson::Tdlib) -> Self {
    Self { tdlib: Arc::new(tdlib) }
  }

  #[doc(hidden)]
  pub fn tdlib(&self) -> &tdjson::Tdlib {
    self.tdlib.borrow()
  }

  pub fn send<Fnc: Function>(&self, fnc: Fnc) -> &Self {
    let json = fnc.to_json();
    info!(tglog::telegram(), "===> {}", json);
    self.tdlib.send(&json[..]);
    self
  }

  pub fn receive(&self, timeout: f64) -> Option<String> {
    let receive = self.tdlib.receive(timeout)
      .map(|v| rtdlib::tdkit::fill_json_struct(v));
    if receive.is_some() {
      info!(tglog::telegram(), "<=== {}", receive.clone().unwrap());
    }
    receive
  }

  pub fn execute<Fnc: Function>(&self, fnc: Fnc) -> Option<String> {
    let json = fnc.to_json();
    info!(tglog::telegram(), "===>>> {}", json);
    self.tdlib.execute(&json[..])
      .map(|v| rtdlib::tdkit::fill_json_struct(v))
  }

  {% for item in classes %}
  pub fn {{item.class_snake}}<C: AsRef<TG{{item.class_name}}>>(&self, {{item.class_snake}}: C) -> &Self {
    self.send({{item.class_snake}}.as_ref().td_origin().clone())
  }
  {% endfor %}

}
