// #![ feature( proc_macro_span ) ]
// #![ feature( type_name_of_val ) ]

// use test_tools::*;
// use proc_macro_tools as TheModule;
// #[ allow( unused_imports ) ]
// use test_tools::*;

use super::*;
// use qt::*;
// use proc_macro_tools::dependencies::*;

//

tests_impls!
{
  #[ test ]
  fn tree_export_str_basic()
  {

    let exp = r#"code : std :: collections :: HashMap < i32 , i32 > :
TokenStream [
    Ident {
        sym: std,
    },
    Punct {
        char: ':',
        spacing: Joint,
    },
    Punct {
        char: ':',
        spacing: Alone,
    },
    Ident {
        sym: collections,
    },
    Punct {
        char: ':',
        spacing: Joint,
    },
    Punct {
        char: ':',
        spacing: Alone,
    },
    Ident {
        sym: HashMap,
    },
    Punct {
        char: '<',
        spacing: Alone,
    },
    Ident {
        sym: i32,
    },
    Punct {
        char: ',',
        spacing: Alone,
    },
    Ident {
        sym: i32,
    },
    Punct {
        char: '>',
        spacing: Alone,
    },
]"#;
    let code = qt!( std::collections::HashMap< i32, i32 > );
    let got = TheModule::tree_export_str!( code );
    // println!( "{}", got );
    a_id!( got, exp );
    let got = TheModule::tree_print!( code );
    // println!( "{}", got );
    a_id!( got, exp );

  }

  //

  #[ test ]
  fn syn_err_basic()
  {

    // test.case( "basic" );
    let err = TheModule::syn_err!( "abc" );
    a_id!( err.to_string(), "abc" );

    // test.case( "with span" );
    let code = qt!( core::option::Option< i32 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let err = TheModule::syn_err!( tree_type, "abc" );
    a_id!( err.to_string(), "abc" );
    // a_id!( err.span(), syn::spanned::Spanned::span( &tree_type ) );

    // test.case( "with span and args" );
    let code = qt!( core::option::Option< i32 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let err = TheModule::syn_err!( tree_type, "abc{}{}", "def", "ghi" );
    a_id!( err.to_string(), "abcdefghi" );
    // a_id!( err.span(), syn::spanned::Spanned::span( &tree_type ) );

    // test.case( "without span" );
    let err = TheModule::syn_err!( _, "abc" );
    a_id!( err.to_string(), "abc" );

    // test.case( "without span, but with args" );
    let err = TheModule::syn_err!( _, "abc{}{}", "def", "ghi" );
    a_id!( err.to_string(), "abcdefghi" );

  }

  //

  #[ test ]
  fn type_container_kind_basic()
  {

    // test.case( "core::option::Option< i32 >" );
    let code = qt!( core::option::Option< i32 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_container_kind( &tree_type );
    a_id!( got, TheModule::ContainerKind::No );

    // test.case( "core::option::Option< Vec >" );
    let code = qt!( core::option::Option< Vec > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_container_kind( &tree_type );
    a_id!( got, TheModule::ContainerKind::No );

    // test.case( "alloc::vec::Vec< i32 >" );
    let code = qt!( alloc::vec::Vec< i32 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_container_kind( &tree_type );
    a_id!( got, TheModule::ContainerKind::Vector );

    // test.case( "alloc::vec::Vec" );
    let code = qt!( alloc::vec::Vec );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_container_kind( &tree_type );
    a_id!( got, TheModule::ContainerKind::Vector );

    // test.case( "std::vec::Vec< i32 >" );
    let code = qt!( std::vec::Vec< i32 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_container_kind( &tree_type );
    a_id!( got, TheModule::ContainerKind::Vector );

    // test.case( "std::vec::Vec" );
    let code = qt!( std::vec::Vec );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_container_kind( &tree_type );
    a_id!( got, TheModule::ContainerKind::Vector );

    // test.case( "std::Vec< i32 >" );
    let code = qt!( std::Vec< i32 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_container_kind( &tree_type );
    a_id!( got, TheModule::ContainerKind::Vector );

    // test.case( "std::Vec" );
    let code = qt!( std::Vec );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_container_kind( &tree_type );
    a_id!( got, TheModule::ContainerKind::Vector );

    // test.case( "not vector" );
    let code = qt!( std::SomeVector< i32, i32 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_container_kind( &tree_type );
    a_id!( got, TheModule::ContainerKind::No );

    // test.case( "hash map" );
    let code = qt!( std::collections::HashMap< i32, i32 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_container_kind( &tree_type );
    a_id!( got, TheModule::ContainerKind::HashMap );

    // test.case( "hash set" );
    let code = qt!( std::collections::HashSet< i32 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_container_kind( &tree_type );
    a_id!( got, TheModule::ContainerKind::HashSet );

  }

  //

  #[ test ]
  fn type_optional_container_kind_basic()
  {

    // test.case( "non optional not container" );
    let code = qt!( i32 );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_optional_container_kind( &tree_type );
    a_id!( got, ( TheModule::ContainerKind::No, false ) );

    // test.case( "optional not container" );
    let code = qt!( core::option::Option< i32 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_optional_container_kind( &tree_type );
    a_id!( got, ( TheModule::ContainerKind::No, true ) );

    // test.case( "optional not container" );
    let code = qt!( Option< i32 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_optional_container_kind( &tree_type );
    a_id!( got, ( TheModule::ContainerKind::No, true ) );


    // test.case( "optional vector" );
    let code = qt!( core::option::Option< Vec > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_optional_container_kind( &tree_type );
    a_id!( got, ( TheModule::ContainerKind::Vector, true ) );

    // test.case( "optional vector" );
    let code = qt!( Option< Vec > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_optional_container_kind( &tree_type );
    a_id!( got, ( TheModule::ContainerKind::Vector, true ) );

    // test.case( "non optional vector" );
    let code = qt!( std::Vec< i32 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_optional_container_kind( &tree_type );
    a_id!( got, ( TheModule::ContainerKind::Vector, false ) );


    // test.case( "optional vector" );
    let code = qt!( core::option::Option< std::collections::HashMap< i32, i32 > > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_optional_container_kind( &tree_type );
    a_id!( got, ( TheModule::ContainerKind::HashMap, true ) );

    // test.case( "optional vector" );
    let code = qt!( Option< HashMap > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_optional_container_kind( &tree_type );
    a_id!( got, ( TheModule::ContainerKind::HashMap, true ) );

    // test.case( "non optional vector" );
    let code = qt!( HashMap< i32, i32 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_optional_container_kind( &tree_type );
    a_id!( got, ( TheModule::ContainerKind::HashMap, false ) );


    // test.case( "optional vector" );
    let code = qt!( core::option::Option< std::collections::HashSet< i32, i32 > > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_optional_container_kind( &tree_type );
    a_id!( got, ( TheModule::ContainerKind::HashSet, true ) );

    // test.case( "optional vector" );
    let code = qt!( Option< HashSet > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_optional_container_kind( &tree_type );
    a_id!( got, ( TheModule::ContainerKind::HashSet, true ) );

    // test.case( "non optional vector" );
    let code = qt!( HashSet< i32, i32 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_optional_container_kind( &tree_type );
    a_id!( got, ( TheModule::ContainerKind::HashSet, false ) );

  }

  //

  #[ test ]
  fn type_rightmost_basic()
  {

    // test.case( "core::option::Option< i32 >" );
    let code = qt!( core::option::Option< i32 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();
    let got = TheModule::type_rightmost( &tree_type );
    a_id!( got, Some( "Option".to_string() ) );

  }

  //

  #[ test ]
  fn type_parameters_basic()
  {

    macro_rules! q
    {
      ( $( $Src : tt )+ ) =>
      {
        syn::parse2::< syn::Type >( qt!( $( $Src )+ ) ).unwrap()
      }
    }

    // test.case( "core::option::Option< i8, i16, i32, i64 >" );
    let code = qt!( core::option::Option< i8, i16, i32, i64 > );
    let tree_type = syn::parse2::< syn::Type >( code ).unwrap();

    let got : Vec< syn::Type > = TheModule::type_parameters( &tree_type, 0..=0 ).into_iter().cloned().collect();
    let exp = vec![ q!( i8 ) ];
    a_id!( got, exp );
    let got : Vec< syn::Type > = TheModule::type_parameters( &tree_type, 0..=1 ).into_iter().cloned().collect();
    let exp = vec![ q!( i8 ), q!( i16 ) ];
    a_id!( got, exp );
    let got : Vec< syn::Type > = TheModule::type_parameters( &tree_type, 0..=2 ).into_iter().cloned().collect();
    let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ) ];
    a_id!( got, exp );

    let got : Vec< syn::Type > = TheModule::type_parameters( &tree_type, 0..0 ).into_iter().cloned().collect();
    let exp : Vec< syn::Type > = vec![];
    a_id!( got, exp );
    let got : Vec< syn::Type > = TheModule::type_parameters( &tree_type, 0..1 ).into_iter().cloned().collect();
    let exp = vec![ q!( i8 ) ];
    a_id!( got, exp );
    let got : Vec< syn::Type > = TheModule::type_parameters( &tree_type, 0..2 ).into_iter().cloned().collect();
    let exp = vec![ q!( i8 ), q!( i16 ) ];
    a_id!( got, exp );

  }

  //

  // fn attr_pair_single( attr : &syn::Attribute ) -> Result< ( String, syn::Lit, syn::Meta ), syn::Error >

  // #[test]
  // fn attr_pair_single_basic() -> Result< (), syn::Error >
  // {
  //   use syn::spanned::Spanned;
  //
  //   // test.case( "basic" );
  //   let input = qt!
  //   {
  //     #[derive( Former )]
  //     pub struct Struct1
  //     {
  //       #[former( default = 31 )]
  //       pub int_1 : i32,
  //     }
  //   };
  //
  //   let ast = match syn::parse2::< syn::DeriveInput >( input )
  //   {
  //     Ok( syntax_tree ) => syntax_tree,
  //     Err( err ) => return Err( err ),
  //   };
  //
  //   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::Error::new( ast.span(), "Unknown format of data, expected syn::Fields::Named( ref fields_named )" ) ),
  //     },
  //     _ => return Err( syn::Error::new( ast.span(), "Unknown format of data, expected syn::Data::Struct( ref data_struct )" ) ),
  //   };
  //
  //   let attr = fields.first().ok_or_else( || err( "No field" ) )?.attrs.first().ok_or_else( || err( "No attr" ) )?;
  //
  //   let ( key, val, meta ) = TheModule::attr_pair_single( &attr )?;
  //   a_id!( key, "default".to_string() );
  //   a_id!( qt!( #val ).to_string(), "31".to_string() );
  //   let is = match meta
  //   {
  //     syn::Meta::List( _ ) => true,
  //     _ => false,
  //   };
  //   assert!( is );
  //
  //   return Ok( () );
  //
  //   fn err( src : &str ) -> syn::Error
  //   {
  //     syn::Error::new( proc_macro2::Span::call_site(), src )
  //   }
  // }

  //

  // #[test]
  // fn path_of() -> Result< (), syn::Error >
  // {
  //
  //   let input = qt!
  //   {
  //     This::is::path
  //   };
  //   let ast = match syn::parse2::< syn::Path >( input )
  //   {
  //     Ok( syntax_tree ) => syntax_tree,
  //     Err( err ) => return Err( err ),
  //   };
  //
  //   let got = proc_macro_tools::path_of( &ast );
  //   a_id!( got, "This::is::path" );
  //
  //   return Ok( () );
  // }
}

//

tests_index!
{
  tree_export_str_basic,
  syn_err_basic,
  type_container_kind_basic,
  type_optional_container_kind_basic,
  type_rightmost_basic,
  type_parameters_basic,
  // attr_pair_single_basic -> Result< (), syn::Error >,
  // path_of -> Result< (), syn::Error >,
}
