////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   EventingKernel.cs
//
//

using System;
using System.Threading;
using System.Runtime.CompilerServices;

using Microsoft.Singularity;
using Microsoft.Singularity.V1.Services;    
using Microsoft.Singularity.Eventing;
using System.Collections;

namespace Microsoft.Singularity.Eventing
{

    [CLSCompliant(false)]
    [AccessedByRuntime("output to header : defined in EventController.cpp")]
    public class KernelController : LocalController {

        internal EventingStorage GeneralPurposeStorage;

        static public KernelController KernelControllerObject = null;

        internal override void Initialize() {

            if (KernelControllerObject != null) {

                //  Already initialized

                return;
            }

            base.Initialize();
            ProcessContext.Initialize();
            
            GeneralPurposeStorage = EventingStorage.CreateLocalStorage(QualityOfService.RecyclableEvents,
                    BUFFER_EXPANSION_SIZE);

            KernelControllerObject = this;
        }

        public override EventingStorage OpenStorage(UIntPtr storageHandle)
        {
            if (storageHandle == 0) {

                return GeneralPurposeStorage;
            }

            return new GlobalStorage(storageHandle);
        }

        
        public override void UnRegisterController(int processId)
        {
            Lock.AcquireMutex();
            ProcessContext ctx = ProcessContext.GetProcessContext(processId);
            
            if (ctx != null) {
                
                ctx.Delete();
            }

            Lock.ReleaseMutex();
        }

        public class ProcessContext {

            static Hashtable Clients;

            static public void Initialize() {

                Clients = new Hashtable();
            }

            uint ProcessId;
            UIntPtr ControllerHandle;
            UIntPtr Context;

            public bool Initialize(UIntPtr controllerHandle, UIntPtr context)
            {
                Clients[Thread.CurrentProcess.ProcessId] = this;
                
                ControllerHandle = controllerHandle;
                Context = context;

                if (!KernelController.RegisterExternalController(controllerHandle, context)) {

                    Clients.Remove(Thread.CurrentProcess.ProcessId);
                    return false;
                }

                return true;
            }

            [Inline]
            static public ProcessContext GetProcessContext(int processId)
            {
                return Clients[processId] as ProcessContext;
            }

            public void Delete()
            {
                if (ControllerHandle != 0) {
                    
                    DebugStub.WriteLine("UnregisterController {0}, {1}", __arglist(ControllerHandle, Context));
                    UnRegisterExternalController(ControllerHandle, Context);
                }
                
                ProcessId = 0;
                Clients.Remove(Thread.CurrentProcess.ProcessId);
            }
            
        }
            
        // System calls functions implementation.
        // TODO: Currently the handles passed to SIPs are actually the handles used by
        // the kernel. This must be changed to maintain a handle table for each SIP
        // and always use the conversion in these syscalls routines

        static public UIntPtr OpenStorageImpl(UIntPtr storageHandle)
        {
            if (storageHandle == 0) {
                
                //  Just return the kernel handle for now. 
                return KernelControllerObject.GeneralPurposeStorage.StorageHandle;
            } 

            // TODO: validate the handle against the existing list and
            // potential permissions. 

            return storageHandle;
        }

        public void CloseStorageImpl(UIntPtr storageHandle)
        {
            // See comments above for syscalls validations
            // There are no states maintained currently. Here will be however the
            // cleanup code for the storage returned to the process
        }
        
        public bool RegisterControllerImpl(UIntPtr controllerHandle, UIntPtr context)
        {
            ProcessContext ctx = new ProcessContext();
            
            if (ctx == null){

                return false;
            }

            Lock.AcquireMutex();
            bool success = ctx.Initialize(controllerHandle, context);
            Lock.ReleaseMutex();
            
            return success;
        }
    
        static public unsafe bool RegisterEventImpl(char * eventName, 
                                                    char * eventDescription, 
                                                    UIntPtr * eventHandle) 
        {
            UIntPtr handle = 0;

            string name = new string(eventName);
            string description = new string(eventDescription);
            
            bool success =  Controller.GetLocalController().RegisterEvent(name, 
                                                                          description, 
                                                                          ref handle);

            if (success) {

                *eventHandle = handle;
            }
            return success;
        }
            
        static public unsafe bool RegisterEventFieldImpl(UIntPtr eventHandle, 
            char * fieldName, 
            UInt16 offset, 
            UInt16 type) 
        {
            string name = new string(fieldName);

            return Controller.GetLocalController().RegisterEventField(eventHandle, 
                                                                      name, 
                                                                      offset, 
                                                                      type);
        }

        [NoHeapAllocation]
        static public unsafe UIntPtr LogSourceEntryImpl(UIntPtr storageHandle,
                                                        uint flags, 
                                                        UIntPtr eventType, 
                                                        byte * buffer, 
                                                        int size)
        {
            return EventSource.LogSourceEntryImpl(storageHandle,flags, eventType, buffer, size);
        }

        [NoHeapAllocation]
        static public unsafe UIntPtr LogSourceEntryImpl(UIntPtr storageHandle,
                                                        uint flags, 
                                                        UIntPtr eventType, 
                                                        byte * buffer, 
                                                        int size,
                                                        int arraysCount,
                                                        void ** arrays)
        {
            return EventSource.LogSourceEntryImpl(storageHandle,
                                                  flags, 
                                                  eventType, 
                                                  buffer, 
                                                  size, 
                                                  arraysCount, 
                                                  (ArrayType *)arrays);
        }

        [NoHeapAllocation]
        static public UIntPtr CreateQueryViewImpl(UIntPtr storageHandle, bool forward)
        {
            return MemoryStorage.CreateQueryViewImpl(storageHandle, forward);
        }

        [NoHeapAllocation]
        static public void DeleteQueryViewImpl(UIntPtr storageHandle)
        {
            MemoryStorage.DeleteQueryViewImpl(storageHandle);
        }
        
        static public unsafe UIntPtr GetNextEntryImpl(UIntPtr queryHandle,
                                                      UIntPtr * type,
                                                      UInt32 * userOffset,
                                                      byte * buffer,
                                                      UInt16 bufferSize )
        {
            return MemoryStorage.GetNextEntryImpl(queryHandle, 
                                                  type, 
                                                  userOffset, 
                                                  buffer, 
                                                  bufferSize);
        }

        public static unsafe bool RegisterEvent(char * eventName, 
                                                char * eventDescription, 
                                                UIntPtr * eventHandle) 
        {
            bool success;
            UIntPtr handle = 0;

            string name = new string(eventName);
            string description = new string(eventDescription);

            success = KernelControllerObject.RegisterEvent(name, description, ref handle);

            *eventHandle = handle;

            return success;
        }
            
        public static unsafe bool RegisterEventField(UIntPtr eventHandle, 
            char * fieldName, 
            UInt16 offset, 
            UInt16 type) {

            string name = new string(fieldName);
            
            return KernelControllerObject.RegisterEventField(eventHandle, 
                name, 
                offset, 
                type);
        }

        public static unsafe bool RegisterEventGenericField(UIntPtr eventHandle, 
                                                            char * fieldName, 
                                                            UInt16 offset, 
                                                            UInt16 size,
                                                            UIntPtr fieldTypeHandle) 
        {

            string name = new string(fieldName);
            
            return KernelControllerObject.RegisterEventGenericField(eventHandle, 
                                                                    name, 
                                                                    offset, 
                                                                    size,
                                                                    fieldTypeHandle);
        }

        public static unsafe bool RegisterEnum(char * enumName, 
                                               UInt16 type, 
                                               UIntPtr * eventHandle) 
        {
            bool success;
            UIntPtr handle = 0;

            string name = new string(enumName);

            success = KernelControllerObject.RegisterEnum(name, type, ref handle);

            *eventHandle = handle;

            return success;
        }
            
        public static unsafe bool RegisterEnumValue(UIntPtr eventHandle, 
                                                    char * valueName, 
                                                    UInt64 value, 
                                                    byte flagChar) 
        {

            string name = new string(valueName);
            
            return KernelControllerObject.RegisterEnumValue(eventHandle, 
                                                            name, 
                                                            value, 
                                                            flagChar);
        }

        public static unsafe UIntPtr WalkEventDescriptor(UIntPtr eventHandle,
                                                         UIntPtr currentField,
                                                         UInt16 * offset,
                                                         UInt16 * type,
                                                         char * bufferName,
                                                         UInt16 bufferSize )
        {
            return MemoryStorage.WalkEventDescriptorImpl(eventHandle, 
                                                         currentField, 
                                                         offset,
                                                         type,
                                                         bufferName,
                                                         bufferSize);
        }
        
        public static unsafe bool GetSourceInformation(UIntPtr sourceHandle,
                                                       UIntPtr * storageHandle,
                                                       UIntPtr * eventType,
                                                       UInt16 * count,
                                                       char * bufferName,
                                                       UInt16 bufferSize )
        {
            UIntPtr tmpStorageHandle = 0;
            UIntPtr tmpEventType = 0;
            UInt16 tmpCount = 0;
            String name = "";

            bool success = KernelControllerObject.GetSourceInformation(sourceHandle, 
                                                                      ref tmpStorageHandle, 
                                                                      ref tmpEventType, 
                                                                      ref tmpCount, 
                                                                      ref name);

            if (success) {

                *storageHandle = tmpStorageHandle;
                *eventType = tmpEventType;
                *count = tmpCount;

                String.LimitedFormatTo(name, new ArgIterator(), bufferName, bufferSize);
            }
            
            return success;
        }

        public static unsafe int QuerySourcesList(UIntPtr * buffer, int size)
        {
            int returnValue = 0;
            UIntPtr [] tmpBuffer = new UIntPtr[size];

            if (tmpBuffer != null) {

                returnValue = KernelControllerObject.QuerySourcesList(tmpBuffer, size);

                if (returnValue < size) {

                    size = returnValue;
                }

                for (int i = 0; i < size; i++) {

                    buffer[i] = tmpBuffer[i];
                }
            }

            return returnValue;
        }

        public static unsafe int QueryEventTypeList(UIntPtr * buffer, int size)
        {
            int returnValue = 0;
            UIntPtr [] tmpBuffer = new UIntPtr[size];

            if (tmpBuffer != null) {

                returnValue = KernelControllerObject.QueryEventTypeList(tmpBuffer, size);

                if (returnValue < size) {

                    size = returnValue;
                }

                for (int i = 0; i < size; i++) {

                    buffer[i] = tmpBuffer[i];
                }
            }

            return returnValue;
        }

        static public bool TestKernelStorageImpl()
        {
            TestKernelLogging.TestAllCases();
            return true;
        }

        //  ABI calls
        
        [AccessedByRuntime("output to header : defined in EventController.cpp")]
        [MethodImpl(MethodImplOptions.InternalCall)]
        [StackBound(256)]
        [NoHeapAllocation]
        public static extern unsafe bool RegisterExternalController(UIntPtr controllerHandle, UIntPtr storageHandle);
        
        [AccessedByRuntime("output to header : defined in EventController.cpp")]
        [MethodImpl(MethodImplOptions.InternalCall)]
        [StackBound(256)]
        [NoHeapAllocation]
        public static extern unsafe void UnRegisterExternalController(UIntPtr controllerHandle, UIntPtr storageHandle);

        [AccessedByRuntime("output to header : defined in EventController.cpp")]
        [MethodImpl(MethodImplOptions.InternalCall)]
        [StackBound(512)]
        [NoHeapAllocation]
        public static extern unsafe bool DebugPrintLogEntry(UIntPtr controllerHandle, UIntPtr entryHandle);
        
    }
   
}


