#region License

/*
 * Copyright © 2002-2008 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#endregion

#if (!NET_1_0 && !MONO)

#region Imports

using System;
using System.Configuration;
using System.Diagnostics;
using System.EnterpriseServices;
using System.IO;
using System.Reflection;
using System.Xml;
using Spring.Context;
using Spring.Context.Support;
using Spring.Core.IO;
using Spring.Objects.Factory.Config;
using Spring.Util;
using ConfigXmlDocument = Spring.Util.ConfigXmlDocument;

#endregion

namespace Spring.EnterpriseServices
{
    /// <summary>
    /// This class supports <see cref="ServicedComponent"/>s exported using <see cref="EnterpriseServicesExporter"/>.
    /// and must never be used directly.
    /// </summary>
    /// <author>Erich Eichinger</author>
    public class ServicedComponentHelper
    {
        private static bool isInitialized;
        private static string componentDirectory;
        private static IApplicationContext _appContext;

        static ServicedComponentHelper()
        {
            isInitialized = false;
        }

        ///<summary>
        /// Reads in the 'xxx.spring-context.xml' configuration file associated with the specified <paramref name="componentType"/>.
        /// See <see cref="EnterpriseServicesExporter"/> for an in-depth description on how to export and configure COM+ components.
        ///</summary>
        public static void EnsureComponentContextRegistryInitialized(Type componentType)
        {
            if (isInitialized) return;

            lock (typeof(ServicedComponentHelper))
            {
                if (isInitialized) return;

                isInitialized = true;

                // this is to ensure, that assemblies placed next to the component assembly can be loaded
                // even when they are not strong named.
                AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
                FileInfo componentAssemblyFile = new FileInfo(componentType.Assembly.Location);
                componentDirectory = componentAssemblyFile.Directory.FullName;
                // switch to component assembly's directory (affects resolving relative paths during context instantiation!)
                Environment.CurrentDirectory = componentDirectory;
                // read in config file if any
                FileInfo configFile = new FileInfo(componentAssemblyFile.FullName);
                if (configFile.Exists)
                {
                    bool isRunningOutOfProcess = IsRunningOutOfProcess();

#if NET_2_0
                    ExeConfigurationSystem comConfig = new ExeConfigurationSystem(configFile.FullName);

                    if (isRunningOutOfProcess)
                    {
                        Trace.WriteLine(string.Format("configuring COM OutProc Server '{0}' using '{1}'", componentAssemblyFile.FullName, componentAssemblyFile.FullName + ".config"));

                        // make the config "global"
                        ConfigurationUtils.SetConfigurationSystem(comConfig, true);
                        _appContext = ContextRegistry.GetContext();
                        if (_appContext == null)
                        {
                            throw ConfigurationUtils.CreateConfigurationException("Spring-exported COM components require <spring/context> section");
                        }

                    }
                    else
                    {
                        Trace.WriteLine(string.Format("configuring COM InProc Server '{0}' using section <spring/context> from file '{1}'", componentAssemblyFile.FullName, componentAssemblyFile.FullName + ".config"));
                        _appContext = (IApplicationContext)comConfig.GetSection("spring/context");
                        if (_appContext == null)
                        {
                            throw ConfigurationUtils.CreateConfigurationException("Spring-exported COM components require <spring/context> section in configuration file");
                        }
                    }
#else
                    _appContext = (IApplicationContext) ConfigurationReader.GetSection(new FileSystemResource(configFile.FullName + ".config"),"spring/context");
                    if (_appContext == null)
                    {
                        throw ConfigurationUtils.CreateConfigurationException("Spring-exported COM components require <spring/context> section in configuration file");
                    }
#endif
                }
                else
                {
                    Trace.WriteLine(string.Format("No configuration file '{0}' for COM component '{1}' found - bypassing configuration", componentAssemblyFile.FullName + ".config", componentAssemblyFile.FullName));
                }
            }
        }

        private static bool IsRunningOutOfProcess()
        {
            // TODO: checkout a prob. better way to find out, whether we are executing as a com server or library
            return AppDomain.CurrentDomain.SetupInformation.ApplicationName == "dllhost.exe";
        }
        private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            string name = args.Name.Split(',')[0];
            Assembly assembly = Assembly.LoadFrom(Path.Combine(componentDirectory, name + ".dll"));
            return assembly;
        }

        ///<summary>
        /// Called by a <see cref="ServicedComponent"/> exported by <see cref="EnterpriseServicesExporter"/> 
        /// to obtain a reference to the service it proxies.
        ///</summary>
        public static object GetObject(ServicedComponent sender, string targetName)
        {
            EnsureComponentContextRegistryInitialized(sender.GetType());
            if (_appContext == null)
            {
                throw ConfigurationUtils.CreateConfigurationException("Spring-exported COM components require <spring/context> section in configuration file");
            }
            return _appContext.GetObject(targetName);
        }
    }
}

#endif