﻿using System;
using System.Diagnostics.Contracts;
using Microsoft.Practices.ServiceLocation;
using OpenCube.Nbt.Mapping.Converters;

namespace OpenCube.Nbt.Mapping
{
	/// <summary>
	/// Factory for creating NBT Mappers
	/// </summary>
	public sealed class NbtMapperFactory
		: INbtMapperFactory
	{
		private readonly IBuildInConverterFactory _converterFactory;
		private readonly IServiceLocator _container;

		/// <summary>
		/// Initializes a new instance of the <see cref="NbtMapperFactory"/> class. Creating any required injections as need.
		/// </summary>
		/// <remarks>
		/// This constructor is for using the API without DI.
		/// </remarks>
		public NbtMapperFactory()
			: this(new BuildInConverterFactory(), ServiceLocator.IsLocationProviderSet ? ServiceLocator.Current : new NullServiceLocator())
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="NbtMapperFactory"/> class.
		/// </summary>
		/// <param name="converterFactory">The factory for building the build in converters.</param>
		/// <param name="container">The service locator for finding other converters.</param>
		public NbtMapperFactory(IBuildInConverterFactory converterFactory, IServiceLocator container)
		{
			Contract.Requires<ArgumentNullException>(converterFactory != null);
			Contract.Requires<ArgumentNullException>(container != null);
			_converterFactory = converterFactory;
			_container = container;
		}

		/// <summary>
		/// Create a mapper for the provided model
		/// </summary>
		/// <typeparam name="TModel">The type of model to create a NBT Mapper for.</typeparam>
		/// <returns>The created NBT Mapper.</returns>
		public INbtMapper<TModel> Create<TModel>()
			where TModel : class, new()
		{
			return new NbtMapper<TModel>(_converterFactory, _container);
		}

		/// <summary>
		/// Create a mapper for the provided model
		/// </summary>
		/// <param name="modelType">The type of model to create a NBT Mapper for.</param>
		/// <returns>The created NBT mapper.</returns>
		public INbtMapper Create(Type modelType)
		{
			Type genType = typeof(NbtMapper<>).MakeGenericType(modelType);
			return (INbtMapper)Activator.CreateInstance(genType, _converterFactory, _container);
		}
	}
}
