#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2009 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using Nuclex.Support.Plugins;
#if ENABLE_SERVICEMANAGER
namespace Nuclex.Support.Services {
  // Allow Dependency on Container
  //   public Foo(IServiceProvider serviceProvider)
  //   public Foo(IserviceLocator serviceLocator)
  //   public Foo(Container container)
  /// 
  ///   Inversion of Control container that manages the services of an application
  /// 
  /// 
  ///   
  ///     This is a very lightweight and simple inversion of control container that
  ///     relieves components of their duty to track down implementations for the services
  ///     they require to function. It will help with lazy initialization and prevent
  ///     components from becoming cross-linked balls of spaghetti references.
  ///   
  ///   
  ///     Here's a short list of the terms used throughout this container and their
  ///     specific meaning in this context.
  ///   
  ///   
  ///     
  ///       - 
  ///         Service
  ///         
  ///           Defined by an interface (service contract) and provided by a component
  ///           that implements the service contract. A service provides some kind of
  ///           utility to the application, for example it could provide access to
  ///           a data base or allow other components to control certain aspects of
  ///           the application.
  ///         
  ///       ///
- 
  ///         Contract
  ///         
  ///           Interface defining the behavior that a service implementation has to
  ///           follow. In order for a component to become a suitable candidate for
  ///           providing a specific service, it has to implement the service contract
  ///           interface and should rigorously follow its specifications.
  ///         
  ///       ///
- 
  ///         Component
  ///         
  ///           A component is simply a class that implements one or more service
  ///           contracts. The service manager will created instances of these classes
  ///           when all their dependencies can be provided for and an implementation
  ///           of their service contract is requested.
  ///         
  ///       ///
///   
  /// 
  public partial class ServiceManager : IServiceProvider {
#region class Contract
    /// Stores the settings for an individual contract
    private class Contract {
      /// 
      ///   Factory by which instances of the contract implementation can be created
      /// 
      public IAbstractFactory Factory;
      /// How instances of the implementation are to be managed
      public Instancing Instancing;
      /// Single global instance of the contract implementation
      /// 
      ///   Used only if  is set to Singleton
      /// 
      public object SingletonInstance;
      /// Thread-local instance of the contract implementation
      /// 
      ///   Used only if is set to InstancePerThread
      /// 
      public object ThreadLocalInstance {
        get {
          initializeThreadLocalData();
          return Thread.GetData(this.threadLocalDataSlot);
        }
        set {
          initializeThreadLocalData();
          Thread.SetData(this.threadLocalDataSlot, value);
        }
      }
      /// Initializes the thread-local data slot
      private void initializeThreadLocalData() {
        if(this.threadLocalDataSlot == null) {
          lock(this) {
            if(this.threadLocalDataSlot == null) {
              this.threadLocalDataSlot = Thread.AllocateDataSlot();
            }
          }
        }
      }
      /// Arguments to be passed to the component constructor
      private Dictionary arguments;
      /// Data slot for thread local storage
      /// 
      ///   We're using an explicit data slot because the ThreadStaticAttribute class
      ///   can only be used on static fields and also because this class is not
      ///   supported by the .NET Compact Framework.
      /// 
      private volatile LocalDataStoreSlot threadLocalDataSlot;
    }
    #endregion // class Contract
#if !XBOX360
    /// Initializes a new service manager
    /// 
    ///   This overload will automatically use a type lister that causes all types
    ///   in all loaded assemblies of the calling app domain to be considered
    ///   by the service manager for obtaining contract implementations.
    /// 
    public ServiceManager() : this(new AppDomainTypeLister()) { }
#endif // !XBOX360
    /// Initializes a new service manager
    /// 
    ///   Type lister providing the types considered by the service manager for
    ///   obtaining contract implementations.
    /// 
    public ServiceManager(ITypeLister typeLister) {
      this.typeLister = typeLister;
      this.contracts = new Dictionary();
    }
    /// 
    ///   Returns all available implementations for the specified contract
    /// 
    /// 
    ///   A new enumerator for the available contract implementations
    /// 
    public IEnumerable GetComponents() where ContractType : class {
      Type contractType = typeof(ContractType);
      foreach(Type checkedType in this.typeLister.GetTypes()) {
        bool isImplementationOfContract =
          (!checkedType.IsAbstract) &&
          contractType.IsAssignableFrom(checkedType);
        if(isImplementationOfContract) {
          yield return checkedType;
        }
      }
    }
    /// 
    ///   Returns all available implementations for the specified contract
    /// 
    /// 
    ///   If true, only services whose dependencies can be completely
    ///   satisfied by the container are returned.
    /// 
    /// 
    ///   A new enumerator for the available contract implementations
    /// 
    public IEnumerable GetComponents(bool completeOnly)
      where ContractType : class {
      if(completeOnly) {
        return filterCompleteComponents(GetComponents());
      } else {
        return GetComponents();
      }
    }
    /// 
    ///   Filters a list of components so only components whose dependencies can be
    ///   completely provided are enumerated
    /// 
    /// Enumerable type list that will be filtered
    /// 
    ///   Only those components whose dependencies can be completely provided
    /// 
    private IEnumerable filterCompleteComponents(IEnumerable types) {
      foreach(Type type in types) {
        bool isCandidate =
          (!type.IsValueType) &&
          (!type.IsAbstract) &&
          (type.IsPublic || type.IsNestedPublic);
        if(isCandidate) {
          ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.Public);
          
          // If a contract has been 
          Contract contract;
          if(this.contracts.TryGetValue(type, out contract)) {
            yield return type;
          } else {
          }
          yield return type;
        }
      }
    }
    /// 
    ///   Allows the adjustment of the container's behavior in regard to
    ///   the specified contract
    /// 
    /// 
    ///   Contract for which the behavior will be adjusted
    /// 
    /// 
    ///   A context object through which the behavior of the container can be
    ///   adjusted for the specified type
    /// 
    public ForContext For() where ContractType : class {
      return new ForContext(this);
    }
    /// 
    ///   Allows the adjustment of the container's behavior in regard to
    ///   the specified contract
    /// 
    /// 
    ///   Contract for which the behavior will be adjusted
    /// 
    /// 
    ///   A context object through which the behavior of the container can be
    ///   adjusted for the specified type
    /// 
    public ForContext For(Type contractType) {
      return new ForContext(this, contractType);
    }
    /// Retrieves the service of the specified type
    /// 
    ///   Contract for which the service will be retrieved
    /// 
    /// The service for the specified contract
    public ContractType GetService() where ContractType : class {
      return (ContractType)GetService(typeof(ContractType));
    }
    /// Retrieves the service of the specified type
    /// 
    ///   Contract for which the service will be retrieved
    /// 
    /// The service for the specified contract
    public object GetService(Type contractType) {
      Contract c = resolveContract(contractType);
      return c.Factory.CreateInstance(); // TODO: Honor the contract settings
    }
    /// 
    ///   Resolves all dependencies required to create a service for a contract
    /// 
    /// 
    ///   Type of contract for which to resolve the implementation
    /// 
    /// The settings for the contract including a valid factory
    private Contract resolveContract(Type contractType) {
      if(contractType.IsValueType) {
        throw new ArgumentException(
          "Contracts have to be interfaces or classes", "contractType"
        );
      }
      /*
            Contract contract;
            if(this.contracts.TryGetValue(contractType, out contract)) {
              return contract;
            }
      */
      throw new NotImplementedException();
    }
    /// Lists all types partaking in the dependency injection
    private ITypeLister typeLister;
    /// Dictionary with settings for each individual contract
    private Dictionary contracts;
  }
} // namespace Nuclex.Support.Services
#endif // ENABLE_SERVICEMANAGER