use super::*;
use frame_support::{
	storage::KeyPrefixIterator,
	traits::{tokens::nonfungibles::*, Get},
	BoundedSlice,
};
use sp_runtime::{DispatchError, DispatchResult};
use sp_std::prelude::*;
impl<T: Config<I>, I: 'static> Inspect<<T as SystemConfig>::AccountId> for Pallet<T, I> {
	type ItemId = T::ItemId;
	type CollectionId = T::CollectionId;
	fn owner(
		collection: &Self::CollectionId,
		item: &Self::ItemId,
	) -> Option<<T as SystemConfig>::AccountId> {
		Item::<T, I>::get(collection, item).map(|a| a.owner)
	}
	fn collection_owner(collection: &Self::CollectionId) -> Option<<T as SystemConfig>::AccountId> {
		Collection::<T, I>::get(collection).map(|a| a.owner)
	}
	fn attribute(
		collection: &Self::CollectionId,
		item: &Self::ItemId,
		key: &[u8],
	) -> Option<Vec<u8>> {
		if key.is_empty() {
			ItemMetadataOf::<T, I>::get(collection, item).map(|m| m.data.into())
		} else {
			let key = BoundedSlice::<_, _>::try_from(key).ok()?;
			Attribute::<T, I>::get((collection, Some(item), key)).map(|a| a.0.into())
		}
	}
	fn collection_attribute(collection: &Self::CollectionId, key: &[u8]) -> Option<Vec<u8>> {
		if key.is_empty() {
			CollectionMetadataOf::<T, I>::get(collection).map(|m| m.data.into())
		} else {
			let key = BoundedSlice::<_, _>::try_from(key).ok()?;
			Attribute::<T, I>::get((collection, Option::<T::ItemId>::None, key)).map(|a| a.0.into())
		}
	}
	fn can_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> bool {
		match (Collection::<T, I>::get(collection), Item::<T, I>::get(collection, item)) {
			(Some(cd), Some(id)) if !cd.is_frozen && !id.is_frozen => true,
			_ => false,
		}
	}
}
impl<T: Config<I>, I: 'static> Create<<T as SystemConfig>::AccountId> for Pallet<T, I> {
	fn create_collection(
		collection: &Self::CollectionId,
		who: &T::AccountId,
		admin: &T::AccountId,
	) -> DispatchResult {
		Self::do_create_collection(
			collection.clone(),
			who.clone(),
			admin.clone(),
			T::CollectionDeposit::get(),
			false,
			Event::Created {
				collection: collection.clone(),
				creator: who.clone(),
				owner: admin.clone(),
			},
		)
	}
}
impl<T: Config<I>, I: 'static> Destroy<<T as SystemConfig>::AccountId> for Pallet<T, I> {
	type DestroyWitness = DestroyWitness;
	fn get_destroy_witness(collection: &Self::CollectionId) -> Option<DestroyWitness> {
		Collection::<T, I>::get(collection).map(|a| a.destroy_witness())
	}
	fn destroy(
		collection: Self::CollectionId,
		witness: Self::DestroyWitness,
		maybe_check_owner: Option<T::AccountId>,
	) -> Result<Self::DestroyWitness, DispatchError> {
		Self::do_destroy_collection(collection, witness, maybe_check_owner)
	}
}
impl<T: Config<I>, I: 'static> Mutate<<T as SystemConfig>::AccountId> for Pallet<T, I> {
	fn mint_into(
		collection: &Self::CollectionId,
		item: &Self::ItemId,
		who: &T::AccountId,
	) -> DispatchResult {
		Self::do_mint(collection.clone(), *item, who.clone(), |_| Ok(()))
	}
	fn burn(
		collection: &Self::CollectionId,
		item: &Self::ItemId,
		maybe_check_owner: Option<&T::AccountId>,
	) -> DispatchResult {
		Self::do_burn(collection.clone(), *item, |_, d| {
			if let Some(check_owner) = maybe_check_owner {
				if &d.owner != check_owner {
					return Err(Error::<T, I>::NoPermission.into())
				}
			}
			Ok(())
		})
	}
}
impl<T: Config<I>, I: 'static> Transfer<T::AccountId> for Pallet<T, I> {
	fn transfer(
		collection: &Self::CollectionId,
		item: &Self::ItemId,
		destination: &T::AccountId,
	) -> DispatchResult {
		Self::do_transfer(collection.clone(), *item, destination.clone(), |_, _| Ok(()))
	}
}
impl<T: Config<I>, I: 'static> InspectEnumerable<T::AccountId> for Pallet<T, I> {
	type CollectionsIterator = KeyPrefixIterator<<T as Config<I>>::CollectionId>;
	type ItemsIterator = KeyPrefixIterator<<T as Config<I>>::ItemId>;
	type OwnedIterator =
		KeyPrefixIterator<(<T as Config<I>>::CollectionId, <T as Config<I>>::ItemId)>;
	type OwnedInCollectionIterator = KeyPrefixIterator<<T as Config<I>>::ItemId>;
	fn collections() -> Self::CollectionsIterator {
		CollectionMetadataOf::<T, I>::iter_keys()
	}
	fn items(collection: &Self::CollectionId) -> Self::ItemsIterator {
		ItemMetadataOf::<T, I>::iter_key_prefix(collection)
	}
	fn owned(who: &T::AccountId) -> Self::OwnedIterator {
		Account::<T, I>::iter_key_prefix((who,))
	}
	fn owned_in_collection(
		collection: &Self::CollectionId,
		who: &T::AccountId,
	) -> Self::OwnedInCollectionIterator {
		Account::<T, I>::iter_key_prefix((who, collection))
	}
}