/*
|-------------------------------------------------------------------------------|
|	Copyright © Computer Ease Limited								            |
|	Address: 1/9 Bloack-A Lalmatia, Dhaka-1207, Bangladesh			            |
|	Email: info@celimited.com, cease@bol-online.com, web: www.celimited.com     |
|	Unauthorized copy or distribution is strictly prohibited		            |
|	Special thanks to Asif Ahamed Siddiqui									    |
|	Author: S. M. Russel,  Last modified date: 18/04/2009						|
|-------------------------------------------------------------------------------|
*/

using System;
using System.Reflection;
using System.Collections;
using Ease.Core.Utility;
using System.Collections.Generic;
using System.Security.Permissions;
using System.Collections.Specialized;

namespace Ease.Core.Model
{
    #region Framework: Service Factory

    /// <summary>
    /// ServiceFactory Class proIDes functionality to Create Service Objects as Remote objects or In-Process objects depending on a configuratio Setting.
    /// This key wtritten in appSettings section and default key 'serviceHandler'  
    /// </summary>
    internal enum CreateMode
    {
        Local,
        Remote
    }

    public class ServiceFactory
    {
        private string _asmFullName;
        private ListDictionary _assemblies;
        private Dictionary<string, object> _cachedTypes;

        /// <summary>
        /// Constructor for ServiceFactory
        /// </summary>
        /// <param name="sectionKey">
        ///		Define sectionKey . Use the follwoing format :-
        ///		For Remote: "Application server-url:port/ServiceAssembly;ServiceAssembly"
        ///		Example   : value="100.100.100.29:49500/Services;Services.Core;Sales.ProductHierarchy"
        ///		For Local : "ServiceAssembly/Datasource;ServiceAssembly/Datasource".
        ///		Example   : value="Services.dll/TELDMS;Services.Core.dll/SalesCore;Sales.ProductHierarchy.dll/SalesMktHierarchy"
        /// </param>
        public ServiceFactory(string sectionKey)
        {
            if (_cachedTypes == null) _cachedTypes = new Dictionary<string, object>();

            return;
        }

        /// <summary>
        /// Create service of parameter type which belongs current service 
        /// assembly (Only one service assembly is used).
        /// </summary>
        /// <typeparam name="T">Any interface</typeparam>
        /// <param name="type">Type of interface from business object which is implemented in service assembly.</param>
        /// <returns>Return appropriate service if found 
        /// otherwise throw an exception 'Error Creating Service'.
        /// </returns>
        public T CreateService<T>(Type type)
        {
            object service = null;
            try
            {
                string serviceName = type.Name.StartsWith("I") ? type.Name.Substring(1) : type.Name;

                #region Individual objects are configured

                lock (_cachedTypes)
                {
                    string serverEP = type.Assembly.FullName;
                    serverEP = serverEP.Substring(0, serverEP.IndexOf('.'));
                    serverEP = serverEP + ".DA";
                    string endPoint = serverEP + ".dll";
                    string targetAssembly = type.Assembly.FullName.Replace("BO", "DA") + "." + serviceName;

                    if (_cachedTypes.ContainsKey(targetAssembly) == false)
                    {
                        Assembly assembly = Assembly.Load(serverEP);
                        foreach (Type svcType in assembly.GetTypes())
                        {
                            // if (svcType.IsMarshalByRef && svcType.IsSubclassOf(typeof(ServiceTemplate)))
                            if (svcType.IsSubclassOf(typeof(ServiceTemplate)))
                            {
                                string key = assembly.FullName + "." + svcType.Name;
                                if (!_cachedTypes.ContainsKey(key))
                                    _cachedTypes.Add(key, svcType);
                            }
                        }
                    }

                    Type implType = (Type)_cachedTypes[targetAssembly];
                    service = implType.InvokeMember("", BindingFlags.CreateInstance, null, null, null);
                    implType.InvokeMember("SetEndPoint",
                        BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, service,
                        new object[] { endPoint });
                }

                #endregion
            }
            catch (Exception e)
            {
                throw new Exception(string.Format("Error Creating Service for type: {0}", type.Name), e);
            }

            if (service == null)
                throw new Exception(string.Format("Error Creating Service for type: {0}", type.Name));

            return (T)service;
        }

        /// <summary>
        /// Create service of parameter type which belongs to nsOfService namespance
        /// (use multiple service assembly).
        /// </summary>
        /// <typeparam name="T">Any interface</typeparam>
        /// <param name="type">Type of interface from business object which is implemented in service assembly.</param>
        /// <param name="nsOfService">Type belongs to which namespace. </param>
        /// <returns>Return appropriate service if found 
        /// otherwise throw an exception 'Error Creating Service'.
        /// </returns>
        public T CreateService<T>(Type type, string nsOfService)
        {
            object service = null;
            try
            {
                string serviceName = type.Name.StartsWith("I") ? type.Name.Substring(1) : type.Name;

                #region Individual objects are configured

                //Checking mode for current type


                string serverEP = type.Assembly.FullName;
                serverEP = serverEP.Substring(0, serverEP.IndexOf('.'));
                serverEP = serverEP + ".DA";
                string EndPoint = serverEP + ".dll";
                // serverEP = svcParts[0];
                serverEP = serverEP.Substring(0, serverEP.Length - 4);
                Assembly assembly = Assembly.Load(serverEP);
                if (!_assemblies.Contains(serverEP))
                {
                    _assemblies.Add(serverEP, assembly.FullName);
                    foreach (Type svcType in assembly.GetTypes())
                    {
                        //if (svcType.IsMarshalByRef && svcType.IsSubclassOf(typeof(ServiceTemplate)))
                        if (svcType.IsSubclassOf(typeof(ServiceTemplate)))
                        {
                            string key = assembly.FullName + "." + svcType.Name;
                            if (!_cachedTypes.ContainsKey(key))
                                _cachedTypes.Add(key, svcType);
                        }
                    }
                }

                string asmFullName = _assemblies[nsOfService].ToString();
                Type implType = (Type)_cachedTypes[asmFullName + "." + serviceName];
                service = implType.InvokeMember("", BindingFlags.CreateInstance, null, null, null);
                implType.InvokeMember("SetEndPoint",
                    BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, service,
                    new object[] { EndPoint });

                #endregion
            }
            catch (Exception e)
            {
                throw new Exception(string.Format("Error Creating Service for type: {0}", type.Name), e);
            }

            if (service == null)
                throw new Exception(string.Format("Error Creating Service for type: {0}", type.Name));

            return (T)service;
        }
    }

    #endregion
}