using System;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Web.Http;
using iiie.CacheControl.Business.CacheKey;
using iiie.CacheControl.Business.OutputCache;

namespace iiie.CacheControl.Business.HttpExtensions
{
    public class CacheOutputConfiguration
    {
        private readonly HttpConfiguration _configuration;

        private OutputCacheType _cacheType;

        public CacheOutputConfiguration(HttpConfiguration configuration, OutputCacheType type)
        {
            _configuration = configuration;
            _cacheType = type;
        }

        private static ICacheKeyGenerator TryActivateCacheKeyGenerator(Type generatorType)
        {
            var hasEmptyOrDefaultConstructor =
                generatorType.GetConstructor(Type.EmptyTypes) != null ||
                generatorType.GetConstructors(BindingFlags.Instance | BindingFlags.Public)
                .Any(x => x.GetParameters().All(p => p.IsOptional));
            return hasEmptyOrDefaultConstructor
                ? Activator.CreateInstance(generatorType) as ICacheKeyGenerator
                : null;
        }

        public ICacheKeyGenerator GetCacheKeyGenerator(HttpRequestMessage request, Type generatorType)
        {
            generatorType = generatorType ?? typeof(ICacheKeyGenerator);
            object cache;
            _configuration.Properties.TryGetValue(generatorType, out cache);

            var cacheFunc = cache as Func<ICacheKeyGenerator>;

            var generator = cacheFunc != null
                ? cacheFunc()
                : request.GetDependencyScope().GetService(generatorType) as ICacheKeyGenerator;

            return generator
                ?? TryActivateCacheKeyGenerator(generatorType)
                ?? new DefaultCacheKeyGenerator();
        }

        public IOutputCache GetCacheOutputProvider(HttpRequestMessage request, object outputCacheData)
        {
            object cache;
            _configuration.Properties.TryGetValue(typeof(IOutputCache), out cache);

            var cacheFunc = cache as Func<IOutputCache>;

            var cacheOutputProvider = cacheFunc != null ? cacheFunc() : request.GetDependencyScope().GetService(typeof(IOutputCache)) as IOutputCache;
            if (cacheOutputProvider == null)
            {
                if (_cacheType == OutputCacheType.DataBase)
                {
                    cacheOutputProvider = new DbOutputCache();
                    ((DbOutputCache) cacheOutputProvider).ConnectionString = outputCacheData as string;
                }
                else
                    cacheOutputProvider = new MemoryOutputCache();
            }
            return cacheOutputProvider;
        }
    }
}