using System;

namespace Wayne.Lib
{
    /// <summary>
    /// Interface representing a service container. Dispose will dispose all disposable services.
    /// </summary>
    public interface IServiceContainer : IServiceLocator, IDisposable
    {
        /// <summary>
        /// Registers service of type TService in the container. A new instance of this type will be created by the container.
        /// </summary>
        /// <typeparam name="TService">Type of service.</typeparam>
        void RegisterService<TService>();

        /// <summary>
        /// Registers service of type TService in the container. A new instance of this type will be created by the container.
        /// </summary>
        /// <typeparam name="TService">Type of service.</typeparam>
        /// <param name="serviceId">A string to identfy this  entry.</param>
        void RegisterService<TService>(string serviceId);




        /// <summary>
        /// Registers service of type TServiceContract in the container. An instance of TServiceImplementation will be created by the container and registerd as TServiceContract.
        /// </summary>
        /// <typeparam name="TServiceContract">Contract that should be registered in the container, e.g. IFoo.</typeparam>
        /// <typeparam name="TServiceImplementation">Implementation of the contract, e.g. Foo implementing IFoo.</typeparam>
        void RegisterService<TServiceContract, TServiceImplementation>()
            where TServiceImplementation : TServiceContract
            where TServiceContract : class;

        /// <summary>
        /// Registers service of type TServiceContract in the container. An instance of TServiceImplementation will be created by the container and registerd as TServiceContract.
        /// </summary>
        /// <typeparam name="TServiceContract">Contract that should be registered in the container, e.g. IFoo.</typeparam>
        /// <typeparam name="TServiceImplementation">Implementation of the contract, e.g. Foo implementing IFoo.</typeparam>
        /// <param name="serviceId">A string to identfy this  entry.</param>
        void RegisterService<TServiceContract, TServiceImplementation>(string serviceId)
            where TServiceImplementation : TServiceContract
            where TServiceContract : class;





        /// <summary>
        /// Registers service of type TServiceContract in the container. The instance that must implement this service contract is passed as an argument.
        /// </summary>
        /// <typeparam name="TServiceContract">Contract that should be registerd in the container, e.g. IFoo.</typeparam>
        /// <param name="serviceInstance">Instance of service implementing TServiceContract, e.g. an instance of Foo where Foo implements IFoo.</param>
        void RegisterService<TServiceContract>(TServiceContract serviceInstance);

        /// <summary>
        /// Registers service of type TServiceContract in the container. The instance that must implement this service contract is passed as an argument.
        /// </summary>
        /// <typeparam name="TServiceContract">Contract that should be registerd in the container, e.g. IFoo.</typeparam>
        /// <param name="serviceInstance">Instance of service implementing TServiceContract, e.g. an instance of Foo where Foo implements IFoo.</param>
        /// <param name="serviceId"> A string to identfy this  entry.</param>
        void RegisterService<TServiceContract>(TServiceContract serviceInstance, string serviceId);



        /// <summary>
        /// Registers a service using a given a delegate that creates the service. This enables the user of this container
        /// to control the creation of the service and at the same time use dependencies from the container.
        /// Example using lambdas : serviceContainer.RegisterService(container => new Foo("myConstant", container.GetService&lt;IDependency&gt;()))
        /// </summary>
        /// <typeparam name="TServiceContract">Contract that should be registerd in the container, e.g. IFoo.</typeparam>
        /// <param name="serviceConstructor">Delegate that constructs the service.</param>
        void RegisterService<TServiceContract>(ObjectConstructor<IServiceLocator, TServiceContract> serviceConstructor);

        /// <summary>
        /// Registers a service using a given a delegate that creates the service. This enables the user of this container
        /// to control the creation of the service and at the same time use dependencies from the container.
        /// Example using lambdas : serviceContainer.RegisterService(container => new Foo("myConstant", container.GetService&lt;IDependency&gt;()))
        /// </summary>
        /// <typeparam name="TServiceContract">Contract that should be registerd in the container, e.g. IFoo.</typeparam>
        /// <param name="serviceConstructor">Delegate that constructs the service.</param>
        /// <param name="serviceId"> A string to identfy this  entry.</param>
        void RegisterService<TServiceContract>(ObjectConstructor<IServiceLocator, TServiceContract> serviceConstructor, string serviceId);


    }

    /// <summary>
    /// Delegate used by the service container to enable outside control of the construction of an object in the container.
    /// </summary>
    /// <typeparam name="TServiceLocator"></typeparam>
    /// <typeparam name="TReturnType"></typeparam>
    /// <param name="serviceLocator"></param>
    /// <returns></returns>
    public delegate TReturnType ObjectConstructor<TServiceLocator, TReturnType>(TServiceLocator serviceLocator);
}