//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
namespace System.ServiceModel.ComIntegration
{
    using System;
    using System.Runtime;
    using System.Runtime.InteropServices;
    using System.ServiceModel.Channels;
    using System.Threading;
    using System.Runtime.Versioning;

    class ProxySupportWrapper
    {
        internal delegate int DelegateDllGetClassObject([In, MarshalAs(UnmanagedType.LPStruct)] Guid clsid, [In, MarshalAs(UnmanagedType.LPStruct)] Guid iid, ref IClassFactory ppv);

        const string fileName = @"ServiceMonikerSupport.dll";
        const string functionName = @"DllGetClassObject";

        static readonly Guid ClsidProxyInstanceProvider = new Guid("(BF0514FB-6912-4659-AD69-B727E5B7ADD4)");

        // Double-checked locking pattern requires volatile for read/write synchronization
        volatile SafeLibraryHandle monikerSupportLibrary;

        // Double-checked locking pattern requires volatile for read/write synchronization
        volatile DelegateDllGetClassObject getCODelegate;

        internal ProxySupportWrapper()
        {
            monikerSupportLibrary = null;
            getCODelegate = null;
        }

        ~ProxySupportWrapper()
        {
            if (null != monikerSupportLibrary)
            {
                monikerSupportLibrary.Close();
                monikerSupportLibrary = null;
            }
        }

        [ResourceConsumption(ResourceScope.Process)]
        internal IProxyProvider GetProxyProvider()
        {
            if (null == monikerSupportLibrary)
            {
                lock (this)
                {
                    if (null == monikerSupportLibrary)
                    {
                        getCODelegate = null;
                        using (RegistryHandle regKey = RegistryHandle.GetCorrectBitnessHKLMSubkey((IntPtr.Size == 8), ServiceModelInstallStrings.WinFXRegistryKey))
                        {
                            string file = regKey.GetStringValue(ServiceModelInstallStrings.RuntimeInstallPathName).TrimEnd('\0') + "\\" + fileName;
                            SafeLibraryHandle tempLibrary = UnsafeNativeMethods.LoadLibrary(file);
                            tempLibrary.DoNotFreeLibraryOnRelease();

                            monikerSupportLibrary = tempLibrary;
                            if (monikerSupportLibrary.IsInvalid)
                            {
                                monikerSupportLibrary.SetHandleAsInvalid();
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ServiceMonikerSupportLoadFailed(file));
                            }
                        }
                    }
                }
            }

            if (null == getCODelegate)
            {
                lock (this)
                {
                    if (null == getCODelegate)
                    {
                        try
                        {
                            IntPtr procaddr = UnsafeNativeMethods.GetProcAddress(monikerSupportLibrary, functionName);
                            getCODelegate = (DelegateDllGetClassObject)Marshal.GetDelegateForFunctionPointer(procaddr, typeof(DelegateDllGetClassObject));

                        }
                        catch (Exception e)
                        {
                            if (Fx.IsFatal(e))
                                throw;

                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ComPlusProxyProviderException(SR.GetString(SR.FailedProxyProviderCreation), e));
                        }
                    }
                }
            }

            IClassFactory cf = null;
            IProxyProvider proxyProvider = null;

            try
            {
                getCODelegate(ClsidProxyInstanceProvider, typeof(IClassFactory).GUID, ref cf);

                proxyProvider = cf.CreateInstance(null, typeof(IProxyProvider).GUID) as IProxyProvider;
                Thread.MemoryBarrier();
            }
            catch (Exception e)
            {
                if (Fx.IsFatal(e))
                    throw;

                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ComPlusProxyProviderException(SR.GetString(SR.FailedProxyProviderCreation), e));
            }
            finally
            {
                if (null != cf)
                {
                    Marshal.ReleaseComObject(cf);
                    cf = null;
                }
            }

            return proxyProvider;
        }
    }


    internal static class OuterProxyWrapper
    {
        static ProxySupportWrapper proxySupport = new ProxySupportWrapper();

        public static IntPtr CreateOuterProxyInstance(IProxyManager proxyManager, ref Guid riid)
        {
            IntPtr pOuter = IntPtr.Zero;
            IProxyProvider proxyProvider = proxySupport.GetProxyProvider();

            if (proxyProvider == null)
            {
                throw Fx.AssertAndThrowFatal("Proxy Provider cannot be NULL");
            }
            Guid riid2 = riid;
            int hr = proxyProvider.CreateOuterProxyInstance(proxyManager, ref riid2, out pOuter);

            Marshal.ReleaseComObject(proxyProvider);

            if (hr != HR.S_OK)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new COMException(SR.GetString(SR.FailedProxyProviderCreation), hr));

            return pOuter;

        }

        public static IntPtr CreateDispatchProxy(IntPtr pOuter, IPseudoDispatch proxy)
        {
            IntPtr pInner = IntPtr.Zero;
            IProxyProvider proxyProvider = proxySupport.GetProxyProvider();

            if (proxyProvider == null)
            {
                throw Fx.AssertAndThrowFatal("Proxy Provider cannot be NULL");
            }
            int hr = proxyProvider.CreateDispatchProxyInstance(pOuter, proxy, out pInner);

            Marshal.ReleaseComObject(proxyProvider);

            if (hr != HR.S_OK)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new COMException(SR.GetString(SR.FailedProxyProviderCreation), hr));

            return pInner;

        }
    }
}