Documentation
use proc_macro2::Span;
use quote::{
  quote, 
  ToTokens
};
use syn::{
  parse::{
    ParseStream, 
    Parse
  },
  Result,
  DeriveInput, 
  Ident, 
  Data::{
    Struct, 
    Enum, 
    Union
  }, 
  DataStruct, 
  FieldsNamed
};

use super::StructField;

pub struct Object{
  name: Option<Ident>,
  struct_fields: Vec<StructField>,
}

impl Object{
  pub fn new() -> Self{
    Self::default()
  }

  pub fn handle_input(&mut self, input: DeriveInput) {
    self.handle_object_name(&input);
    self.handle_object_data(&input);
  }

  fn handle_object_name(&mut self, input: &DeriveInput){
    self.name = Some(input.ident.clone());
  } 

  fn handle_object_data(&mut self, input: &DeriveInput){
    let struct_fields: Vec<StructField> = match &input.data {
      Struct(struct_object) => Self::handle_struct_object(struct_object),
      Enum(_enum_object) => Vec::new(),
      Union(_union_object) => Vec::new(),
    };
    self.struct_fields = struct_fields;
  }
  
  fn handle_struct_object(input: &DataStruct) -> Vec<StructField>{
    match &input.fields{
      syn::Fields::Named(named_fields) => Self::handle_named_fields(named_fields),
      syn::Fields::Unnamed(_) => Vec::new(),
      syn::Fields::Unit => Vec::new(),
    }
  }

  fn handle_named_fields(named_fields: &FieldsNamed) -> Vec<StructField> {
    let mut struct_fields: Vec<StructField> = Vec::new();
    for field in &named_fields.named{
      let struct_field = StructField::parse(field);
      match struct_field{
        Some(struct_field) => struct_fields.push(struct_field),
        None => (),
      }
    
    };
    struct_fields
  }
}

impl Parse for Object {
  fn parse(input: ParseStream) -> Result<Self> {
    let parsed: DeriveInput = input.parse()?;
    let mut object = Object::new();
    object.handle_input(parsed);
    Ok(object)
  }
}

impl ToTokens for Object{
  fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
    let name = self.name.as_ref().unwrap();
    let fields= Ident::new("__regi_n", Span::call_site());
    // let fields = self.struct_fields.clone();

    let struct_fields_iter = self.struct_fields.iter().map(|struct_field|{
      let name = struct_field.name.to_string();
      
      quote!{ 
        #fields.push(format!("\"{}\":\"{}\"", #name, &self.#struct_field));
      }
    });
    
    tokens.extend(
      quote!{
        impl #name{
          fn get_fields(&self) -> std::vec::Vec::<String>{
            let mut #fields: std::vec::Vec::<String> = std::vec::Vec::new();
            #(#struct_fields_iter)*;    
  
            #fields
          }
        }

        impl Object for &#name{
          fn stringify(&self) -> String{
            let string = format!("{{{}}}", self.get_fields().join(","));
            string
          }
        }
        impl Object for #name{
          fn stringify(&self) -> String{
            let string = format!("{{{}}}", self.get_fields().join(","));
            string
          }
        }
      })
  }
}

impl Default for Object{
    fn default() -> Self {
      Self { 
        name: None,
        struct_fields: Vec::new()
      }
    }
}