using System;
using System.Threading;
using System.Security.Principal;
using System.Collections.Specialized;
using System.Configuration;
using System.Web;
namespace Csla
{
  /// 
  /// Provides consistent context information between the client
  /// and server DataPortal objects. 
  /// 
  public static class ApplicationContext
  {
    #region User
    /// 
    /// Get or set the current 
    /// object representing the user's identity.
    /// 
    /// 
    /// This is discussed in Chapter 5. When running
    /// under IIS the HttpContext.Current.User value
    /// is used, otherwise the current Thread.CurrentPrincipal
    /// value is used.
    /// 
    public static IPrincipal User
    {
      get
      {
        if (HttpContext.Current == null)
          return Thread.CurrentPrincipal;
        else
          return HttpContext.Current.User;
      }
      set
      {
        if (HttpContext.Current != null)
          HttpContext.Current.User = value;
        Thread.CurrentPrincipal = value;
      }
    }
    #endregion
    #region LocalContext
    private const string _localContextName = "Csla.LocalContext";
    /// 
    /// Returns the application-specific context data that
    /// is local to the current AppDomain.
    /// 
    /// 
    /// 
    /// The return value is a HybridDictionary. If one does
    /// not already exist, and empty one is created and returned.
    /// 
    /// Note that data in this context is NOT transferred to and from
    /// the client and server.
    /// 
    /// 
    public static HybridDictionary LocalContext
    {
      get
      {
        HybridDictionary ctx = GetLocalContext();
        if (ctx == null)
        {
          ctx = new HybridDictionary();
          SetLocalContext(ctx);
        }
        return ctx;
      }
    }
    private static HybridDictionary GetLocalContext()
    {
      if (HttpContext.Current == null)
      {
        LocalDataStoreSlot slot = Thread.GetNamedDataSlot(_localContextName);
        return (HybridDictionary)Thread.GetData(slot);
      }
      else
        return (HybridDictionary)HttpContext.Current.Items[_localContextName];
    }
    private static void SetLocalContext(HybridDictionary localContext)
    {
      if (HttpContext.Current == null)
      {
        LocalDataStoreSlot slot = Thread.GetNamedDataSlot(_localContextName);
        Thread.SetData(slot, localContext);
      }
      else
        HttpContext.Current.Items[_localContextName] = localContext;
    }
    #endregion
    #region Client/Global Context
    private static object _syncClientContext = new object();
    private const string _clientContextName = "Csla.ClientContext";
    private const string _globalContextName = "Csla.GlobalContext";
    /// 
    /// Returns the application-specific context data provided
    /// by the client.
    /// 
    /// 
    /// 
    /// The return value is a HybridDictionary. If one does
    /// not already exist, and empty one is created and returned.
    /// 
    /// Note that data in this context is transferred from
    /// the client to the server. No data is transferred from
    /// the server to the client.
    /// 
    /// This property is thread safe in a Windows client
    /// setting and on an application server. It is not guaranteed
    /// to be thread safe within the context of an ASP.NET
    /// client setting (i.e. in your ASP.NET UI).
    /// 
    /// 
    public static HybridDictionary ClientContext
    {
      get
      {
        lock (_syncClientContext)
        {
          HybridDictionary ctx = GetClientContext();
          if (ctx == null)
          {
            ctx = new HybridDictionary();
            SetClientContext(ctx);
          }
          return ctx;
        }
      }
    }
    /// 
    /// Returns the application-specific context data shared
    /// on both client and server.
    /// 
    /// 
    /// 
    /// The return value is a HybridDictionary. If one does
    /// not already exist, and empty one is created and returned.
    /// 
    /// Note that data in this context is transferred to and from
    /// the client and server. Any objects or data in this context
    /// will be transferred bi-directionally across the network.
    /// 
    /// 
    public static HybridDictionary GlobalContext
    {
      get
      {
        HybridDictionary ctx = GetGlobalContext();
        if (ctx == null)
        {
          ctx = new HybridDictionary();
          SetGlobalContext(ctx);
        }
        return ctx;
      }
    }
    internal static HybridDictionary GetClientContext()
    {
      if (HttpContext.Current == null)
      {
        if (ApplicationContext.ExecutionLocation == ExecutionLocations.Client)
          lock (_syncClientContext)
            return (HybridDictionary)AppDomain.CurrentDomain.GetData(_clientContextName);
        else
        {
          LocalDataStoreSlot slot =
            Thread.GetNamedDataSlot(_clientContextName);
          return (HybridDictionary)Thread.GetData(slot);
        }
      }
      else
        return (HybridDictionary)
          HttpContext.Current.Items[_clientContextName];
    }
    internal static HybridDictionary GetGlobalContext()
    {
      if (HttpContext.Current == null)
      {
        LocalDataStoreSlot slot = Thread.GetNamedDataSlot(_globalContextName);
        return (HybridDictionary)Thread.GetData(slot);
      }
      else
        return (HybridDictionary)HttpContext.Current.Items[_globalContextName];
    }
    private static void SetClientContext(HybridDictionary clientContext)
    {
      if (HttpContext.Current == null)
      {
        if (ApplicationContext.ExecutionLocation == ExecutionLocations.Client)
          lock (_syncClientContext)
            AppDomain.CurrentDomain.SetData(_clientContextName, clientContext);
        else
        {
          LocalDataStoreSlot slot = Thread.GetNamedDataSlot(_clientContextName);
          Thread.SetData(slot, clientContext);
        }
      }
      else
        HttpContext.Current.Items[_clientContextName] = clientContext;
    }
    internal static void SetGlobalContext(HybridDictionary globalContext)
    {
      if (HttpContext.Current == null)
      {
        LocalDataStoreSlot slot = Thread.GetNamedDataSlot(_globalContextName);
        Thread.SetData(slot, globalContext);
      }
      else
        HttpContext.Current.Items[_globalContextName] = globalContext;
    }
    internal static void SetContext(
      HybridDictionary clientContext, 
      HybridDictionary globalContext)
    {
      SetClientContext(clientContext);
      SetGlobalContext(globalContext);
    }
    /// 
    /// Clears all context collections.
    /// 
    public static void Clear()
    {
      SetContext(null, null);
      SetLocalContext(null);
    }
    #endregion
    #region Config Settings
    /// 
    /// Returns the authentication type being used by the
    /// CSLA .NET framework.
    /// 
    /// 
    /// 
    /// 
    /// This value is read from the application configuration
    /// file with the key value "CslaAuthentication". The value
    /// "Windows" indicates CSLA .NET should use Windows integrated
    /// (or AD) security. Any other value indicates the use of
    /// custom security derived from BusinessPrincipalBase.
    /// 
    public static string AuthenticationType
    {
      get { return ConfigurationManager.AppSettings["CslaAuthentication"]; }
    }
    /// 
    /// Returns the channel or network protocol
    /// for the DataPortal server.
    /// 
    /// Fully qualified assembly/type name of the proxy class.
    /// 
    /// 
    /// 
    /// This value is read from the application configuration
    /// file with the key value "CslaDataPortalProxy". 
    /// 
    /// The proxy class must implement Csla.Server.IDataPortalServer.
    /// 
    /// The value "Local" is a shortcut to running the DataPortal
    /// "server" in the client process.
    /// 
    /// Other built-in values include:
    /// 
    /// - 
    /// Csla,Csla.DataPortalClient.RemotingProxy
    /// Use .NET Remoting to communicate with the server
    /// ///
- 
    /// Csla,Csla.DataPortalClient.EnterpriseServicesProxy
    /// Use Enterprise Services (DCOM) to communicate with the server
    /// ///
- 
    /// Csla,Csla.DataPortalClient.WebServicesProxy
    /// Use Web Services (asmx) to communicate with the server
    /// ///
/// Each proxy type does require that the DataPortal server be hosted using the appropriate
    /// technology. For instance, Web Services and Remoting should be hosted in IIS, while
    /// Enterprise Services must be hosted in COM+.
    /// 
    /// 
    public static string DataPortalProxy
    {
      get
      {
        string result = ConfigurationManager.AppSettings["CslaDataPortalProxy"];
        if (string.IsNullOrEmpty(result))
          result = "Local";
        return result;
      }
    }
    /// 
    /// Returns the URL for the DataPortal server.
    /// 
    /// 
    /// 
    /// 
    /// This value is read from the application configuration
    /// file with the key value "CslaDataPortalUrl". 
    /// 
    public static Uri DataPortalUrl
    {
      get { return new Uri(ConfigurationManager.AppSettings["CslaDataPortalUrl"]); }
    }
    /// 
    /// Enum representing the locations code can execute.
    /// 
    public enum ExecutionLocations
    {
      /// 
      /// The code is executing on the client.
      /// 
      Client,
      /// 
      /// The code is executing on the application server.
      /// 
      Server
    }
    #endregion
    #region In-Memory Settings
    private static ExecutionLocations _executionLocation = 
      ExecutionLocations.Client;
    /// 
    /// Returns a value indicating whether the application code
    /// is currently executing on the client or server.
    /// 
    public static ExecutionLocations ExecutionLocation
    {
      get { return _executionLocation; }
    }
    internal static void SetExecutionLocation(ExecutionLocations location)
    {
      _executionLocation = location;
    }
    #endregion
  }
}