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

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

using Microsoft.Singularity;
using Microsoft.Singularity.V1.Services;    

namespace Microsoft.Singularity.Eventing
{
    [CLSCompliant(false)]
    [AccessedByRuntime("output to header : defined in MemoryStorage.cpp")]
    public unsafe struct ArrayType {

        [AccessedByRuntime("output to header : defined in MemoryStorage.cpp")]
        public ushort Length;
        [AccessedByRuntime("output to header : defined in MemoryStorage.cpp")]
        public ushort ItemSize;
        [AccessedByRuntime("output to header : defined in MemoryStorage.cpp")]
        public uint Type;
        [AccessedByRuntime("output to header : defined in MemoryStorage.cpp")]
        public void * Buffer;
    }


    [CLSCompliant(false)]
    [AccessedByRuntime("output to header : defined in MemoryStorage.cpp")]
    public class MemoryStorage : EventingStorage {

        private Mutex mutex;
        private UnmanagedMemoryBuffer Buffers;

        private LocalController hostController = null;

        ~MemoryStorage() 
        {
            Destroy();
        }

        unsafe public bool InitializeFromExistingHandle()
        {
            mutex = new Mutex();

            if (mutex == null) {

                return false;
            }

            // Mirror the existing memory buffer. We do not need actually to also fetch
            // the pointers since someone else has the ownership of that memory
            
            Buffers = new UnmanagedMemoryBuffer();
            hostController = Controller.GetLocalController();

            return (Buffers != null);
        }


        unsafe public bool Initialize(ulong Size)
        {
            mutex = new Mutex();

            if (mutex == null) {

                return false;
            }

            UnmanagedMemoryBuffer NewBuffer = new UnmanagedMemoryBuffer();

            if (NewBuffer != null) {

                uint overheadSize = MemoryStorage.GetMemoryStorageOveheadImpl();

                if (!NewBuffer.AllocateBuffer(overheadSize + Size)) {

                    return false;
                }
                
                Buffers = NewBuffer;

                StorageHandle = MemoryStorage.MemoryStorageCreateImpl(
                    QoS.StorageSettings,
                    NewBuffer.GetBuffer(), 
                    (uint)NewBuffer.GetSize(), 
                    0);

                if (Controller.GetLocalController().RegisterStorage( this )) {

                    hostController = Controller.GetLocalController();
                    
                    return true;
                }

            }
                
            return false;
        }

        public override bool AddBuffer(ulong Size)
        {
            unsafe {
                
                UnmanagedMemoryBuffer NewBuffer = new UnmanagedMemoryBuffer();

                if (NewBuffer != null) {

                    if (!NewBuffer.AllocateBuffer(Size)) {

                        return false;
                    }
                }
                
                mutex.AcquireMutex();
                NewBuffer.Link = Buffers;
                Buffers = NewBuffer;
                mutex.ReleaseMutex();

                MemoryStorage.MemoryStorageRegisterBufferImpl(StorageHandle, 
                                                              NewBuffer.GetBuffer(), 
                                                              (uint)NewBuffer.GetSize());
            }
            return true;
        }

        void Destroy() {

            if (hostController != null) {

                hostController.UnRegisterStorage(this);
            }
        }

        [NoHeapAllocation]
        public override unsafe UIntPtr CreateQueryView(bool forward)
        {
            return CreateQueryViewImpl(StorageHandle, forward);
        }

        [NoHeapAllocation]
        public override unsafe void DeleteQueryView(UIntPtr queryHandle)
        {
            DeleteQueryViewImpl(queryHandle);
        }

        public override UIntPtr GetNextEntry(UIntPtr queryHandle,
                                             ref QueryBuffer entryBuffer)
        {
            unsafe {

                UInt32 offset;
                UIntPtr type;
                
                fixed(byte * ptr = &(entryBuffer.GetBuffer()[0])) {

                    entryBuffer.EntryHandle =  MemoryStorage.GetNextEntryImpl(queryHandle , 
                                                                 &type,
                                                                 &offset, 
                                                                 ptr, 
                                                                 entryBuffer.GetBufferSize());

                    entryBuffer.Type = type;
                    entryBuffer.UserOffset = offset;

                    return entryBuffer.EntryHandle;
                }
            }
        }
        
        // ABI interfaces

        [AccessedByRuntime("output to header : defined in MemoryStorage.cpp")]
        [MethodImpl(MethodImplOptions.InternalCall)]
        [StackBound(256)]
        [NoHeapAllocation]
        static extern unsafe uint GetMemoryStorageOveheadImpl();

        [AccessedByRuntime("output to header : defined in MemoryStorage.cpp")]
        [MethodImpl(MethodImplOptions.InternalCall)]
        [StackBound(256)]
        [NoHeapAllocation]
        static extern unsafe UIntPtr MemoryStorageCreateImpl(uint Flags,
            byte * memoryBuffer, 
            uint bufferSize, 
            uint ZoneSize);
        
        [AccessedByRuntime("output to header : defined in MemoryStorage.cpp")]
        [MethodImpl(MethodImplOptions.InternalCall)]
        [StackBound(256)]
        [NoHeapAllocation]
        static extern unsafe void MemoryStorageRegisterBufferImpl(UIntPtr Storage, 
            byte * memoryBuffer, 
            uint bufferSize);
        
        [AccessedByRuntime("output to header : defined in MemoryStorage.cpp")]
        [MethodImpl(MethodImplOptions.InternalCall)]
        [StackBound(256)]
        [NoHeapAllocation]
        public static extern unsafe UIntPtr CreateQueryViewImpl(UIntPtr Storage, bool forward);

        [AccessedByRuntime("output to header : defined in MemoryStorage.cpp")]
        [MethodImpl(MethodImplOptions.InternalCall)]
        [StackBound(256)]
        [NoHeapAllocation]
        public static extern unsafe void DeleteQueryViewImpl(UIntPtr queryHandle);

        [AccessedByRuntime("output to header : defined in MemoryStorage.cpp")]
        [MethodImpl(MethodImplOptions.InternalCall)]
        [StackBound(256)]
        [NoHeapAllocation]
        public static extern unsafe UIntPtr WalkEventDescriptorImpl(UIntPtr eventHandle,
                                                             UIntPtr currentField,
                                                             UInt16 * offset,
                                                             UInt16 * type,
                                                             char * bufferName,
                                                             UInt16 bufferSize );

        [AccessedByRuntime("output to header : defined in MemoryStorage.cpp")]
        [MethodImpl(MethodImplOptions.InternalCall)]
        [StackBound(256)]
        [NoHeapAllocation]
        public static extern unsafe UIntPtr GetNextEntryImpl(UIntPtr queryHandle,
                                                     UIntPtr * type,
                                                     UInt32 * userOffset,
                                                     byte * buffer,
                                                     UInt16 bufferSize );
    }

    [CLSCompliant(false)]
    [AccessedByRuntime("output to header : defined in MemoryStorage.cpp")]
    public class GlobalStorage : EventingStorage {

        ~GlobalStorage() 
        {
            Destroy();
        }

        public GlobalStorage(UIntPtr storageId)
        {
            StorageHandle = DiagnosisService.OpenGlobalStorage(storageId);
        }

        void Destroy() {

            if (StorageHandle != 0) {
                
                DiagnosisService.CloseGlobalStorage(StorageHandle);
            }
        }

        [NoHeapAllocation]
        public override unsafe UIntPtr CreateQueryView(bool forward)
        {
            return DiagnosisService.CreateQueryView(StorageHandle, forward);
        }

        [NoHeapAllocation]
        public override unsafe void DeleteQueryView(UIntPtr queryHandle)
        {
            DiagnosisService.DeleteQueryView(queryHandle);
        }

        public override Controller GetController()
        {
            return Controller.GetSystemController();
        }

        public override UIntPtr GetNextEntry(UIntPtr queryHandle,
                                             ref QueryBuffer entryBuffer)
        {
            unsafe {

                UInt32 offset;
                UIntPtr type;
                
                fixed(byte * ptr = &(entryBuffer.GetBuffer()[0])) {

                    entryBuffer.EntryHandle =  DiagnosisService.GetNextEntry(queryHandle , 
                                                                 &type,
                                                                 &offset, 
                                                                 ptr, 
                                                                 entryBuffer.GetBufferSize());

                    entryBuffer.Type = type;
                    entryBuffer.UserOffset = offset;

                    return entryBuffer.EntryHandle;
                }
            }
        }
     }
}

