﻿//Source : https://github.com/nhibernate/nhibernate-core/blob/master/src/NHibernate/Context/ReflectiveHttpContext.cs

using System;
using System.Collections;
using System.Linq.Expressions;
using System.Reflection;
using System.Security.Principal;

namespace Toolbox.Core.Context
{
    /// <summary>
    /// This class allows access to the HttpContext without referring to HttpContext at compile time.
    /// The accessors are cached as delegates for performance.
    /// </summary>
    public static class ReflectiveHttpContext
    {
        static ReflectiveHttpContext()
        {
            CreateCurrentHttpContextGetter();
            CreateHttpContextItemsGetter();
            CreateHttpContextUserGetter();
        }

        public static bool IsActive {
            get
            {
                return HttpContextType != null
                && HttpContextCurrentGetter() != null;
            } 
        }

        public static IDictionary HttpContextCurrentItems
        {
            get { return HttpContextItemsGetter(HttpContextCurrentGetter()); }
        }

        public static IPrincipal User { get { return HttpContextUserGetter(HttpContextCurrentGetter()); } }

        private static Func<object> HttpContextCurrentGetter { get; set; }

        private static Func<object, IDictionary> HttpContextItemsGetter { get; set; }

        private static Func<object, IPrincipal> HttpContextUserGetter { get; set; } 

        private static Type HttpContextType
        {
            get
            {
                return
                    Type.GetType(string.Format("System.Web.HttpContext, System.Web, Version={0}, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", Environment.Version));
            }
        }

        private static void CreateCurrentHttpContextGetter()
        {
            var currentProperty = HttpContextType.GetProperty("Current", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
            var propertyExpression = Expression.Property(null, currentProperty);
            var convertedExpression = Expression.Convert(propertyExpression, typeof(object));
            HttpContextCurrentGetter = (Func<object>)Expression.Lambda(convertedExpression).Compile();
        }

        private static void CreateHttpContextItemsGetter()
        {
            var contextParam = Expression.Parameter(typeof(object), "context");
            var convertedParam = Expression.Convert(contextParam, HttpContextType);
            var itemsProperty = Expression.Property(convertedParam, "Items");
            HttpContextItemsGetter = (Func<object, IDictionary>)Expression.Lambda(itemsProperty, contextParam).Compile();
        }

        private static void CreateHttpContextUserGetter()
        {
            var contextParam = Expression.Parameter(typeof(object), "context");
            var convertedParam = Expression.Convert(contextParam, HttpContextType);
            var itemsProperty = Expression.Property(convertedParam, "User");
            HttpContextUserGetter = (Func<object, IPrincipal>)Expression.Lambda(itemsProperty, contextParam).Compile();
        }
    }
}