// --------------------------------------------------------------------------------------------------------------------
// 
//   Copyright (c) Microsoft Corporation.
// 
// 
//   Callback for JET_param JET_paramDurableCommitCallback.
// 
// --------------------------------------------------------------------------------------------------------------------
namespace Microsoft.Isam.Esent.Interop.Windows8
{
    using System;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Reflection;
#if !MANAGEDESENT_ON_CORECLR
    using System.Runtime.CompilerServices;
#endif
    using Microsoft.Isam.Esent.Interop.Implementation;
    /// 
    /// A class which wraps the callback dealing with durable commits.
    /// 
    public class DurableCommitCallback : EsentResource
    {
        /// 
        /// API call tracing.
        /// 
        private static readonly TraceSwitch TraceSwitch = new TraceSwitch("ESENT DurableCommitCallback", "Wrapper around unmanaged ESENT durable commit callback");
        /// 
        /// Instance associated with this callback.
        /// 
        private JET_INSTANCE instance;
        /// 
        /// Hold a reference to the delegate so that it doesn't get garbage-collected.
        /// 
        private JET_PFNDURABLECOMMITCALLBACK wrappedCallback;
        /// 
        /// Hold a reference to the delegate so that it doesn't get garbage-collected.
        /// 
        [SuppressMessage("Exchange.Performance", "EX0023:DeadVariableDetector", Justification = "Need to hold on to a reference to the callback, so that it does not get garbage collected.")]
        private NATIVE_JET_PFNDURABLECOMMITCALLBACK wrapperCallback;
        /// 
        /// Initializes a new instance of the  class. 
        /// The constructor.
        /// 
        /// 
        /// The instance with which to associate the callback.
        /// 
        /// 
        /// The managed code callback to call.
        /// 
        public DurableCommitCallback(
            JET_INSTANCE instance,
            JET_PFNDURABLECOMMITCALLBACK wrappedCallback)
        {
            this.instance = instance;
            this.wrappedCallback = wrappedCallback;
            this.wrapperCallback = this.NativeDurableCommitCallback;
#if !MANAGEDESENT_ON_WSA // RuntimeHelpers works differently in Windows Store Apps.
            if (this.wrappedCallback != null)
            {
                RuntimeHelpers.PrepareMethod(this.wrappedCallback.Method.MethodHandle);
            }
            RuntimeHelpers.PrepareMethod(typeof(DurableCommitCallback).GetMethod("NativeDurableCommitCallback", BindingFlags.NonPublic | BindingFlags.Instance).MethodHandle);
#endif
            InstanceParameters instanceParameters = new InstanceParameters(this.instance);
            // This might be null.
            instanceParameters.SetDurableCommitCallback(this.wrapperCallback);
            this.ResourceWasAllocated();
        }
        /// 
        /// Generate a string representation of the structure.
        /// 
        /// The structure as a string.
        public override string ToString()
        {
            return string.Format(
                CultureInfo.InvariantCulture,
                "DurableCommitCallback({0})",
                this.instance.ToString());
        }
        /// 
        /// Terminate the durable commit session.
        /// 
        public void End()
        {
            this.CheckObjectIsNotDisposed();
            this.ReleaseResource();
        }
        /// 
        /// Free the durable commit session.
        /// We do not try to set the instance parameter to null, since the callback is disposed after JetTerm and
        /// the callback cannot be set after JetTerm.
        /// 
        protected override void ReleaseResource()
        {
            this.instance = JET_INSTANCE.Nil;
            this.wrappedCallback = null;
            this.wrapperCallback = null;
            this.ResourceWasReleased();
        }
        /// 
        /// The proxy callback function to call the user-defined managed delegate.
        /// 
        /// 
        /// The instance.
        /// 
        /// 
        /// The commit-id flushed.
        /// 
        /// 
        /// Reserved currently.
        /// 
        /// 
        /// An error code.
        /// 
        private JET_err NativeDurableCommitCallback(
            IntPtr instance,
            ref NATIVE_COMMIT_ID commitIdSeen,
            uint grbit)
        {
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                JET_INSTANCE jetInstance = new JET_INSTANCE()
                {
                    Value = instance
                };
                if (this.instance != jetInstance)
                {
                    // We assume it's only called on one instance at a time. The only thing
                    // we really care about is serialization of the byte array.
                    //
                    // It would be nice to throw an error, but we're going back to real
                    // code, which doesn't deal with managed exceptions well.
                    return JET_err.CallbackFailed;
                }
                JET_COMMIT_ID commitId = new JET_COMMIT_ID(commitIdSeen);
                return this.wrappedCallback(jetInstance, commitId, (DurableCommitCallbackGrbit)grbit);
            }
            catch (Exception ex)
            {
                Trace.WriteLineIf(
                    TraceSwitch.TraceWarning, string.Format(CultureInfo.InvariantCulture, "Caught Exception {0}", ex));
                JetApi.ReportUnhandledException(ex, "Unhandled exception during NativeDurableCommitCallback");
                // This should never be executed, but the compiler doesn't know it.
                return JET_err.CallbackFailed;
            }
        }
    }
}