
// use quote::{ quote };
use proc_macro_tools::syn::{ DeriveInput };
use iter_tools::{ Itertools, process_results };
use proc_macro_tools::*;

pub type Result< T > = std::result::Result< T, syn::Error >;

///
/// Descripotr of a field.
///

#[allow( dead_code )]
struct FormerField< 'a >
{
  pub attrs : &'a Vec< syn::Attribute >,
  pub vis : &'a syn::Visibility,
  pub ident : &'a syn::Ident,
  pub colon_token : &'a Option< syn::token::Colon >,
  pub ty : &'a syn::Type,
  pub non_optional_ty : &'a syn::Type,
  pub is_optional : bool,
  pub type_container_kind : proc_macro_tools::ContainerKind,
}

///
/// Attribute to hold information about method to call after form.
///
/// `#[ perform = ( fn after1< 'a >() -> Option< &'a str > ) ]`
///

#[allow( dead_code )]
struct AttributeFormAfter
{
  paren_token : syn::token::Paren,
  signature : syn::Signature,
}

impl syn::parse::Parse for AttributeFormAfter
{
  fn parse( input : syn::parse::ParseStream< '_ > ) -> Result< Self >
  {
    let input2;
    Ok( Self
    {
      paren_token : syn::parenthesized!( input2 in input ),
      signature : input2.parse()?,
    })
  }
}

///
/// Attribute to hold information about default value.
///
/// `#[ default = 13 ]`
///

#[allow( dead_code )]
struct AttributeDefault
{
  // eq_token : syn::Token!{ = },
  paren_token : syn::token::Paren,
  expr : syn::Expr,
}

impl syn::parse::Parse for AttributeDefault
{
  fn parse( input : syn::parse::ParseStream< '_ > ) -> Result< Self >
  {
    let input2;
    Ok( Self
    {
      paren_token : syn::parenthesized!( input2 in input ),
      // eq_token : input.parse()?,
      expr : input2.parse()?,
    })
  }
}

///
/// Is type under Option.
///

fn is_optional( ty : &syn::Type ) -> bool
{
  proc_macro_tools::type_rightmost( ty ) == Some( "Option".to_string() )
}

///
/// Extract the first parameter of the type if such exist.
///

fn parameter_internal_first( ty : &syn::Type ) -> Result< &syn::Type >
{
  proc_macro_tools::type_parameters( ty, 0 ..= 0 )
  .first()
  .map( | e | *e )
  .ok_or_else( || syn_err!( ty, "Expects at least one parameter here:\n  {}", qt!{ #ty } ) )
}

///
/// Extract the first and the second parameters of the type if such exist.
///

fn parameter_internal_first_two( ty : &syn::Type ) -> Result< ( &syn::Type, &syn::Type ) >
{
  let on_err = ||
  {
    syn_err!( ty, "Expects at least two parameters here:\n  {}", qt!{ #ty } )
  };
  let result = proc_macro_tools::type_parameters( ty, 0 ..= 1 );
  let mut iter = result.iter();
  Ok
  ((
    iter.next().ok_or_else( on_err )?,
    iter.next().ok_or_else( on_err )?,
  ),)
}

///
/// Generate fields for initializer of a struct setting each field to `None`.
///
/// ### Sample of output
///
/// ```compile_fail
/// int_1 : core::option::Option::None,
/// string_1 : core::option::Option::None,
/// int_optional_1 : core::option::Option::None,
/// ```
///

#[inline]
fn field_none_map( field : &FormerField< '_ > ) -> proc_macro2::TokenStream
{
  let ident = Some( field.ident.clone() );
  let tokens = qt! { ::core::option::Option::None };
  let ty2 : syn::Type = syn::parse2( tokens ).unwrap();

  qt!
  {
    #ident : #ty2
  }
}

///
/// Generate field of the former for a field of the structure
///
/// ### Sample of output
///
/// ```compile_fail
/// pub int_1 : core::option::Option< i32 >,
/// pub string_1 : core::option::Option< String >,
/// pub int_optional_1 :  core::option::Option< i32 >,
/// pub string_optional_1 : core::option::Option< String >,
/// ```
///

#[inline]
fn field_optional_map( field : &FormerField< '_ > ) -> proc_macro2::TokenStream
{
  let ident = Some( field.ident.clone() );
  let ty = field.ty.clone();

  let ty2 = if is_optional( &ty )
  {
    qt! { #ty }
  }
  else
  {
    qt! { ::core::option::Option< #ty > }
  };

  qt!
  {
    pub #ident : #ty2
  }

}

///
/// Generate code converting a field of the former to the field of the structure.
///
/// ### Sample of output
///
/// ```compile_fail
/// let int_1 = if self.int_1.is_some()
/// {
///   self.int_1.take().unwrap()
/// }
/// else
/// {
///   let val : i32 = Default::default();
///   val
/// };
/// ```
///

#[inline]
fn field_form_map( field : &FormerField< '_ > ) -> Result< proc_macro2::TokenStream >
{
  let ident = field.ident;
  let ty = field.ty;
  let mut default = None;

  for attr in field.attrs.iter()
  {
    let key_ident = attr.path.get_ident()
    .ok_or_else( || syn_err!( attr, "Expects simple key of an attirbute, but got:\n  {}", qt!{ #attr } ) )?;
    let key_str = format!( "{}", key_ident );
    match key_str.as_ref()
    {
      "default" =>
      {
        let attr_default = syn::parse2::< AttributeDefault >( attr.tokens.clone() )?;
        default = Some( attr_default.expr );
      }
      _ =>
      {
        return Err( syn_err!( attr, "Unknown attribute {}", qt!{ #attr } ) );
      }
    }
  }

  let tokens = if field.is_optional
  {

    let _else = if default == None
    {
      qt!
      {
        ::core::option::Option::None
      }
    }
    else
    {
      let default_val = default.unwrap();
      qt!
      {
        ::core::option::Option::Some( ( #default_val ).into() )
      }
    };

    qt!
    {
      let #ident = if self.#ident.is_some()
      {
        ::core::option::Option::Some( self.#ident.take().unwrap() )
      }
      else
      {
        #_else
      };
    }

  }
  else
  {

    let _else = if default == None
    {
      qt!
      {
        let val : #ty = ::core::default::Default::default();
      }
    }
    else
    {
      let default_val = default.unwrap();
      qt!
      {
        let val : #ty = ( #default_val ).into();
      }
    };

    qt!
    {
      let #ident = if self.#ident.is_some()
      {
        self.#ident.take().unwrap()
      }
      else
      {
        #_else
        val
      };
    }

  };

  Ok( tokens )
}

///
/// Extract name of a field out.
///

#[inline]
fn field_name_map( field : &FormerField< '_ > ) -> syn::Ident
{
  let ident = field.ident.clone();
  ident
}

///
/// Generate a fomer setter for the field.
///
/// ### Sample of output
///
/// ```compile_fail
/// pub fn int_1< Src >( mut self, src : Src ) -> Self
/// where Src : Into< i32 >,
/// {
///   debug_assert!( self.int_1.is_none() );
///   self.int_1 = Some( src.into() );
///   self
/// }
/// ```
///

#[inline]
fn field_setter_map( field : &FormerField< '_ >, former_name_ident : &syn::Ident ) -> Result< proc_macro2::TokenStream >
{
  let ident = &field.ident;

  let tokens = match &field.type_container_kind
  {
    proc_macro_tools::ContainerKind::No =>
    {
      let non_optional_ty = &field.non_optional_ty;
      qt!
      {
        #[inline]
        pub fn #ident< Src >( mut self, src : Src ) -> Self
        where Src : ::core::convert::Into< #non_optional_ty >,
        {
          debug_assert!( self.#ident.is_none() );
          self.#ident = ::core::option::Option::Some( src.into() );
          self
        }
      }
    },
    proc_macro_tools::ContainerKind::Vector =>
    {
      let ty = &field.ty;
      let internal_ty = parameter_internal_first( ty )?;
      qt!
      {
        #[inline]
        pub fn #ident( mut self ) -> former::runtime::VectorFormer
        <
          #internal_ty,
          #ty,
          #former_name_ident,
          impl Fn( &mut #former_name_ident, ::core::option::Option< #ty > )
        >
        {
          let container = self.#ident.take();
          let on_end = | former : &mut #former_name_ident, container : ::core::option::Option< #ty > |
          {
            former.#ident = container;
          };
          former::runtime::VectorFormer::new( self, container, on_end )
        }
      }
    },
    proc_macro_tools::ContainerKind::HashMap =>
    {
      let ty = &field.ty;
      let ( k_ty, e_ty ) = parameter_internal_first_two( ty )?;
      qt!
      {
        #[inline]
        pub fn #ident( mut self ) -> former::runtime::HashMapFormer
        <
          #k_ty,
          #e_ty,
          #ty,
          #former_name_ident,
          impl Fn( &mut #former_name_ident, ::core::option::Option< #ty > )
        >
        {
          let container = self.#ident.take();
          let on_end = | former : &mut #former_name_ident, container : ::core::option::Option< #ty > |
          {
            former.#ident = container;
          };
          former::runtime::HashMapFormer::new( self, container, on_end )
        }
      }
    },
    proc_macro_tools::ContainerKind::HashSet =>
    {
      let ty = &field.ty;
      let internal_ty = parameter_internal_first( ty )?;
      qt!
      {
        #[inline]
        pub fn #ident( mut self ) -> former::runtime::HashSetFormer
        <
          #internal_ty,
          #ty,
          #former_name_ident,
          impl Fn( &mut #former_name_ident, ::core::option::Option< #ty > )
        >
        {
          let container = self.#ident.take();
          let on_end = | former : &mut #former_name_ident, container : ::core::option::Option< #ty > |
          {
            former.#ident = container;
          };
          former::runtime::HashSetFormer::new( self, container, on_end )
        }
      }
    },
  };

  Ok( tokens )
}

///
/// Generate documentation for the former.
///

fn doc_generate( name_ident : &syn::Ident ) -> ( String, String )
{

  let doc_former_mod = format!
  (
r#" Implementation of former for [{}].
"#,
    name_ident
  );

  let doc_example1 =
r#"
use former::Former;
#[derive( Former )]
pub struct Struct1
{
  #[default( 31 )]
  field1 : i32,
}
"#;

  let doc_former_struct = format!
  (
r#" Object to form [{}]. If field's values is not set then default value of the field is set.

For specifing custom default value use attribute `default`. For example:
```
{}
```
"#,
    name_ident, doc_example1
  );

  ( doc_former_mod, doc_former_struct )
}

//

pub fn former( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream >
{
  // let ast = parse_macro_input!( input as DeriveInput );

  let ast = match syn::parse::< DeriveInput >( input )
  {
    Ok( syntax_tree ) => syntax_tree,
    Err( err ) => return Err( err ),
  };

  let name_ident = &ast.ident;
  let generics = &ast.generics;
  let former_name = format!( "{}Former", name_ident );
  let former_name_ident = syn::Ident::new( &former_name, name_ident.span() );

  // use heck::ToSnakeCase;
  // let former_snake = name_ident.to_string().to_snake_case();
  // let former_mod = format!( "{}_former", former_snake );
  // let former_mod_ident = syn::Ident::new( &former_mod, name_ident.span() );

  /* structure attribute */

  let mut perform = qt!
  {
    return result;
  };
  let mut perform_output = qt!{ #name_ident #generics };
  let mut perform_generics = qt!{};
  for attr in ast.attrs.iter()
  {
    if let Some( ident ) = attr.path.get_ident()
    {
      let ident_string = format!( "{}", ident );
      if ident_string == "perform"
      {
        let attr_perform = syn::parse2::< AttributeFormAfter >( attr.tokens.clone() )?;
        let signature = &attr_perform.signature;
        let generics = &signature.generics;
        perform_generics = qt!{ #generics };
        let perform_ident = &signature.ident;
        let output = &signature.output;
        match output
        {
          syn::ReturnType::Type( _, boxed_type ) =>
          {
            perform_output = qt!{ #boxed_type };
          },
          _ => {},
        }
        perform = qt!
        {
          return result.#perform_ident();
        };
      }
    }
    else
    {
      return Err( syn_err!( "Unknown structure attribute:\n{}", qt!{ attr } ) );
    }
  }

  /* */

  let fields = match ast.data
  {
    syn::Data::Struct( ref data_struct ) => match data_struct.fields
    {
      syn::Fields::Named( ref fields_named ) =>
      {
        &fields_named.named
      },
      _ => return Err( syn_err!( ast, "Unknown format of data, expected syn::Fields::Named( ref fields_named )\n  {}", qt!{ #ast } ) ),
    },
    _ => return Err( syn_err!( ast, "Unknown format of data, expected syn::Data::Struct( ref data_struct )\n  {}", qt!{ #ast } ) ),
  };

  let former_fields : Vec< Result< FormerField< '_ > > > = fields.iter().map( | field |
  {
    let attrs = &field.attrs;
    let vis = &field.vis;
    let ident = field.ident.as_ref()
    .ok_or_else( || syn_err!( field, "Expected that each field has key, but some does not:\n  {}", qt!{ #field } ) )?;
    let colon_token = &field.colon_token;
    let ty = &field.ty;
    let is_optional = is_optional( &ty );
    let type_container_kind = proc_macro_tools::type_container_kind( &ty );
    let non_optional_ty : &syn::Type = if is_optional { parameter_internal_first( ty )? } else { ty };
    let former_field = FormerField { attrs, vis, ident, colon_token, ty, non_optional_ty, is_optional, type_container_kind };
    Ok( former_field )
  }).collect();

  let former_fields : Vec< _ > = process_results( former_fields, | iter | iter.collect() )?;

  let ( fields_none, fields_optional, fields_form, fields_names, fields_setter )
  : ( Vec< _ >, Vec< _ >, Vec< _ >, Vec< _ >, Vec< _ > )
  = former_fields.iter().map( | former_field |
  {(
    field_none_map( &former_field ),
    field_optional_map( &former_field ),
    field_form_map( &former_field ),
    field_name_map( &former_field ),
    field_setter_map( &former_field, &former_name_ident ),
  )}).multiunzip();

  let ( _doc_former_mod, doc_former_struct ) = doc_generate( &name_ident );
  let fields_setter : Vec< _ > = process_results( fields_setter, | iter | iter.collect() )?;
  let fields_form : Vec< _ > = process_results( fields_form, | iter | iter.collect() )?;

  let result = qt!
  {

    impl #generics #name_ident #generics
    {
      ///
      /// Make former, variation of builder pattern to form structure defining values of fields step by step.
      ///
      #[inline]
      pub fn former() -> #former_name_ident #generics
      {
        #former_name_ident
        {
          #( #fields_none, )*
        }
      }
    }

    // #[doc = #doc_former_mod]
    // mod #former_mod_ident
    // {
      // use;
      // use super::*;
      // use super::#name_ident;
      // #[cfg( feature = "in_wtools" )]
      // use wtools::former;

      #[doc = #doc_former_struct]
      #[derive( Debug )]
      pub struct #former_name_ident #generics
      {
        #(
          /// A field
          #fields_optional,
        )*
      }

      impl #generics #former_name_ident #generics
      {
        ///
        /// Finish setting options and call perform on formed entity.
        ///
        /// If `perform` defined then associated method is called and its result returned instead of entity.
        /// For example `perform()` of structure with : `#[ perform( fn after1< 'a >() -> Option< &'a str > )` returns `Option< &'a str >`.
        ///
        #[inline]
        pub fn perform #perform_generics ( self ) -> #perform_output
        {
          let result = self.form();
          #perform
        }

        ///
        /// Finish setting options and return formed entity.
        ///
        /// `perform` has no effect on method `form`, but change behavior and returned type of mehod `perform`.
        ///
        #[inline]
        pub fn form( mut self ) -> #name_ident #generics
        {
          #( #fields_form )*
          let result = #name_ident
          {
            #( #fields_names, )*
          };
          return result;
        }

        #(
          #fields_setter
        )*

      }

    // }
    // pub use #former_mod_ident::#former_name_ident;

  };

  Ok( result )
}

//
// = Input :
//
// #[derive( Debug, PartialEq )]
// pub struct Struct1
// {
//   pub int_1 : i32,
//   string_1 : String,
//   int_optional_1 : core::option::Option< i32 >,
//   string_optional_1 : Option< String >,
//   vec_1 : Vec< String >,
//   hashmap_strings_1 : std::collections::HashMap< String, String >,
//   hashset_strings_1 : std::collections::HashSet< String >,
// }

//
// = Output :
//
// impl Struct1
// {
//   pub fn former() -> Struct1Former
//   {
//     Struct1Former
//     {
//       int_1 : core::option::Option::None,
//       string_1 : core::option::Option::None,
//       int_optional_1 : core::option::Option::None,
//       string_optional_1 : core::option::Option::None,
//       vec_1 : core::option::Option::None,
//       hashmap_strings_1 : core::option::Option::None,
//       hashset_strings_1 : core::option::Option::None,
//     }
//   }
// }
//
// //
//
// #[derive( Debug )]
// pub struct Struct1Former
// {
//   pub int_1 : core::option::Option< i32 >,
//   pub string_1 : core::option::Option< String >,
//   pub int_optional_1 :  core::option::Option< i32 >,
//   pub string_optional_1 : core::option::Option< String >,
//   pub vec_1 : core::option::Option< Vec< String > >,
//   pub hashmap_strings_1 : core::option::Option< std::collections::HashMap< String, String > >,
//   pub hashset_strings_1 : core::option::Option< std::collections::HashSet< String > >,
// }
//
// //
//
// impl Struct1Former
// {
//   fn form( mut self ) -> Struct1
//   {
//
//     let int_1 = if self.int_1.is_some()
//     {
//       self.int_1.take().unwrap()
//     }
//     else
//     {
//       let val : i32 = Default::default();
//       val
//     };
//
//     let string_1 = if self.string_1.is_some()
//     {
//       self.string_1.take().unwrap()
//     }
//     else
//     {
//       let val : String = Default::default();
//       val
//     };
//
//     let int_optional_1 = if self.int_optional_1.is_some()
//     {
//       Some( self.int_optional_1.take().unwrap() )
//     }
//     else
//     {
//       None
//     };
//
//     let string_optional_1 = if self.string_optional_1.is_some()
//     {
//       Some( self.string_optional_1.take().unwrap() )
//     }
//     else
//     {
//       None
//     };
//
//     let vec_1 = if self.vec_1.is_some()
//     {
//       self.vec_1.take().unwrap()
//     }
//     else
//     {
//       let val : Vec< String > = Default::default();
//       val
//     };
//
//     let hashmap_strings_1 = if self.hashmap_strings_1.is_some()
//     {
//       self.hashmap_strings_1.take().unwrap()
//     }
//     else
//     {
//       let val : std::collections::HashMap< String, String > = Default::default();
//       val
//     };
//
//     let hashset_strings_1 = if self.hashset_strings_1.is_some()
//     {
//       self.hashset_strings_1.take().unwrap()
//     }
//     else
//     {
//       let val : std::collections::HashSet< String > = Default::default();
//       val
//     };
//
//     Struct1
//     {
//       int_1,
//       string_1,
//       int_optional_1,
//       string_optional_1,
//       vec_1,
//       hashmap_strings_1,
//       hashset_strings_1,
//     }
//
//   }
//
//   pub fn int_1< Src >( mut self, src : Src ) -> Self
//   where Src : core::convert::Into< i32 >,
//   {
//     debug_assert!( self.int_1.is_none() );
//     self.int_1 = Some( src.into() );
//     self
//   }
//
//   pub fn string_1< Src >( mut self, src : Src ) -> Self
//   where Src : core::convert::Into< String >,
//   {
//     debug_assert!( self.string_1.is_none() );
//     self.string_1 = Some( src.into() );
//     self
//   }
//
//   pub fn string_optional_1< Src >( mut self, src : Src ) -> Self
//   where Src : core::convert::Into< String >
//   {
//     debug_assert!( self.string_optional_1.is_none() );
//     self.string_optional_1 = Some( src.into() );
//     self
//   }
//
//   pub fn vec_1( mut self ) -> former::runtime::VectorFormer
//   <
//     String,
//     Vec< String >,
//     Struct1Former,
//     impl Fn( &mut Struct1Former, core::option::Option< Vec< String > > )
//   >
//   {
//     let container = self.vec_1.take();
//     let on_end = | former : &mut Struct1Former, container : core::option::Option< Vec< String > > |
//     {
//       former.vec_1 = container;
//     };
//     former::runtime::VectorFormer::new( self, container, on_end )
//   }
//
//   pub fn hashmap_strings_1( mut self ) -> former::runtime::HashMapFormer
//   <
//     String,
//     String,
//     std::collections::HashMap< String, String >,
//     Struct1Former,
//     impl Fn( &mut Struct1Former, core::option::Option< std::collections::HashMap< String, String > > )
//   >
//   {
//     let container = self.hashmap_strings_1.take();
//     let on_end = | former : &mut Struct1Former, container : core::option::Option< std::collections::HashMap< String, String > > |
//     {
//       former.hashmap_strings_1 = container;
//     };
//     former::runtime::HashMapFormer::new( self, container, on_end )
//   }
//
//   pub fn hashset_strings_1( mut self ) -> former::runtime::HashSetFormer
//   <
//     String,
//     std::collections::HashSet< String >,
//     Struct1Former,
//     impl Fn( &mut Struct1Former, core::option::Option< std::collections::HashSet< String > > )
//   >
//   {
//     let container = self.hashset_strings_1.take();
//     let on_end = | former : &mut Struct1Former, container : core::option::Option< std::collections::HashSet< String > > |
//     {
//       former.hashset_strings_1 = container;
//     };
//     former::runtime::HashSetFormer::new( self, container, on_end )
//   }
//
// }
//
