////////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity - Singularity ABI
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   InterruptHandle.cs
//
//  Note:
//

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Singularity;
using Microsoft.Singularity.Io;
using Microsoft.Singularity.Memory;
using Microsoft.Singularity.V1.Services;

namespace Microsoft.Singularity.V1.Threads
{
    [CLSCompliant(false)]
    public struct InterruptHandle // : public SyncHandle
    {

        public readonly UIntPtr id; // could be moved to SyncHandle
        public static readonly InterruptHandle Zero = new InterruptHandle();

        internal InterruptHandle(UIntPtr id)
        {
            this.id = id;
        }

        [ExternalEntryPoint]
        public static unsafe bool CreateImpl(
            byte irqNum,
            InterruptHandle* handle)
        {
            return Create(irqNum, out *handle);
        }

        public static bool Create(byte irqNum,
                                  out InterruptHandle handle)
        {
            bool ret = false;
            handle = new InterruptHandle();

            //
            // Create an IoIrq, and a handle in the current process to hold it.
            //
            IoConfig config = Thread.CurrentProcess.IoConfig;
            for (int i = 0; i < config.DynamicRanges.Length; i++) {
                IoIrqRange iir = config.DynamicRanges[i] as IoIrqRange;
                if (iir != null && iir.Irq <= irqNum && irqNum < iir.Irq + iir.Size) {
                    IoIrq irq = iir.IrqAtOffset((byte)(irqNum - iir.Irq));

                    handle = new InterruptHandle(
                        Thread.CurrentProcess.AllocateHandle(irq));
                    irq.RegisterInterrupt();
                    ret = true;
                    break;
                }
            }

            Tracing.Log(Tracing.Debug,
                        "InterruptHandle.Create(irq={0:x2}, out id={0:x8})",
                        irqNum, handle.id);
            return ret;
        }

        [ExternalEntryPoint]
        public static bool Dispose(InterruptHandle handle)
        {
            Tracing.Log(Tracing.Debug, "InterruptHandle.Dispose(id={0:x8})",
                        handle.id);

            bool ret = false;
            if (handle.id != UIntPtr.Zero) {
                //
                // Releasing the handle will allow the IoIrq event to be
                // garbage-collected.
                //
                IoIrq irq = HandleTable.GetHandle(handle.id) as IoIrq;
                ret = irq.ReleaseInterrupt();
                Thread.CurrentProcess.ReleaseHandle(handle.id);
            }
            return ret;
        }

        [ExternalEntryPoint]
        public static bool Wait(InterruptHandle handle)
        {
            if (handle.id == UIntPtr.Zero) {
                Tracing.Log(Tracing.Error,
                            "InterruptHandle.Wait(id={0:x8}) on bad handle",
                            handle.id);
                return false;
            }

            //
            // Convert the handle to a IoIrq and wait on it.
            //
            IoIrq irq = HandleTable.GetHandle(handle.id) as IoIrq;
            bool ret = irq.WaitForInterrupt();

            Tracing.Log(Tracing.Debug, "InterruptHandle.Wait(id={0:x8})", handle.id);

            return ret;
        }

        [ExternalEntryPoint]
        public static void Pulse(InterruptHandle handle)
        {
            if (handle.id == UIntPtr.Zero) {
                Tracing.Log(Tracing.Error,
                            "InterruptHandle.Wait(id={0:x8}) on bad handle",
                            handle.id);
                return;
            }
            IoIrq irq = HandleTable.GetHandle(handle.id) as IoIrq;
            irq.Pulse();
            Tracing.Log(Tracing.Debug, "InterruptHandle.Pulse(id={0:x8})",
                        handle.id);
        }

        [ExternalEntryPoint]
        public static bool Ack(InterruptHandle handle)
        {
            if (handle.id == UIntPtr.Zero) {
                Tracing.Log(Tracing.Error,
                            "InterruptHandle.Ack(id={0:x8}) on bad handle",
                            handle.id);
                return false;
            }

            //
            // Convert the handle to an auto-reset event; set the event.
            //
            IoIrq irq = HandleTable.GetHandle(handle.id) as IoIrq;
            bool ret = irq.AckInterrupt();

            Tracing.Log(Tracing.Debug, "InterruptHandle.Ack(id={0:x8})", handle.id);
            return ret;
        }

    }
}
