use proc_macro2::{Ident, Span, TokenStream, TokenTree};
use template_quote::quote;

struct X;

impl template_quote::ToTokens for X {
	fn to_tokens(&self, tokens: &mut TokenStream) {
		tokens.extend(Some(TokenTree::Ident(Ident::new("X", Span::call_site()))));
	}
}

#[test]
fn test_repetition_simple() {
	let primes = &[X, X, X, X];

	assert_eq!("X X X X", quote!(#(#primes)*).to_string());

	assert_eq!("X , X , X , X ,", quote!(#(#primes,)*).to_string());

	assert_eq!("X , X , X , X", quote!(#(#primes),*).to_string());
}

#[test]
fn test_repetition_two_vars() {
	let foo = vec!["a", "b"];
	let bar = vec![true, false];

	let tokens = quote! {
		#(#foo: #bar),*
	};

	let expected = r#""a" : true , "b" : false"#;
	assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_repetition_nested() {
	let nested = vec![vec!['a', 'b', 'c'], vec!['x', 'y', 'z']];

	let tokens = quote! {
		#(
			#(#nested)*
		),*
	};

	let expected = "'a' 'b' 'c' , 'x' 'y' 'z'";
	assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_var_name_conflict() {
	// The implementation of `#(...),*` uses the variable `__i` but it should be
	// fine, if a little confusing when debugging.
	let __i = vec!['a', 'b'];
	let tokens = quote! {
		#(#__i),*
	};
	let expected = "'a' , 'b'";
	assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_repetition_same_iter_twice() {
	let a = 1..=3;
	let b = quote! {
		#(#a #a)*
	};
	assert_eq!("1i32 1i32 2i32 2i32 3i32 3i32", b.to_string());
}

#[test]
fn test_repetition_iterators() {
	let a = vec!["a", "b", "c"].into_iter();
	let b = 2..6;
	let c = [5, 6, 7].into_iter().map(|_| X);
	let q = quote! {
		#(#a #b #c)*
	};
	assert_eq!("\"a\" 2i32 X \"b\" 3i32 X \"c\" 4i32 X", q.to_string());
}

#[test]
fn test_repetition_slices() {
	let a = vec!["a", "b", "c"];
	let b = &[2, 3, 4, 5];
	let c = [5, 6, 7];

	let q = quote! {
		#(#a #b #c)*
	};
	assert_eq!(
		"\"a\" 2i32 5i32 \"b\" 3i32 6i32 \"c\" 4i32 7i32",
		q.to_string()
	);
}

#[test]
fn test_repetition_totokens() {
	let i = 1..3; // Iterator just to avoid infinite loop
	let a = 5;
	let b = "a";
	let c = X;

	let q = quote! {
		#(#i #a #b #c)*
	};
	assert_eq!("1i32 5i32 \"a\" X 2i32 5i32 \"a\" X", q.to_string());
}

#[test]
fn test_repetition_without_consuming() {
	// The following types should not be consumed by `quote!`
	let a = [1, 2, 3];
	let b = vec!['a', 'b', 'c'];
	let c = 5.0;

	let q1 = quote! {
		#(#a #b #c)* #(#a #b #c)* // Usable in two different patterns
	};
	let q2 = quote! {
		#(#a #b #c)* // Usable in a different `quote!`
	};
	let d = (a[0], b[0], c); // Still usable in the following code

	assert_eq!(
		"1i32 'a' 5f64 2i32 'b' 5f64 3i32 'c' 5f64 1i32 'a' 5f64 2i32 'b' 5f64 3i32 'c' 5f64",
		q1.to_string()
	);
	assert_eq!("1i32 'a' 5f64 2i32 'b' 5f64 3i32 'c' 5f64", q2.to_string());
	assert_eq!(1, d.0);
	assert_eq!('a', d.1);
	assert_eq!(5.0, d.2);
}

#[test]
fn test_repetition_trait_name_collision() {
	let a = 0..3; // Avoid infinite loop
	let x = X;

	let q = quote! {
		#(#a #x)*
	};

	assert_eq!("0i32 X 1i32 X 2i32 X", q.to_string());
}

#[test]
#[ignore] // TODO(#7) trait fn collision
fn test_repetition_trait_fn_collision() {

	// trait Repeat {
	//     fn __template_quote__as_repeat(self) -> !;
	// }
	// impl Repeat for X {
	//     fn __template_quote__as_repeat(self) -> ! { panic!("Wrong trait
	// called.") 	// } }

	// let a = 0..3; // Avoid infinite loop
	// let x = X;

	// let q = quote! {
	//     #(#a #x)*
	// };

	// assert_eq!("0i32 X 1i32 X 2i32 X", q.to_string());
}

#[test]
fn test_repetition_impl_fn_collision() {
	struct R;
	impl quote::ToTokens for R {
		fn to_tokens(&self, tokens: &mut TokenStream) {
			tokens.extend(quote!(X));
		}
	}

	let a = 0..3; // Avoid infinite loop
	let r = R;

	let q = quote! {
		#(#a #r)*
	};

	assert_eq!("0i32 X 1i32 X 2i32 X", q.to_string());
}
