////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   EventQuery.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
{
    // Delegates for the session and entry queries

    public class EnumerationContext
    {
        public EnumerationContext(){}
    }

    [CLSCompliant(false)]
    public delegate bool QuerySourceDelegate(UIntPtr sourceHandle,
                                             UIntPtr storageHandle,
                                             UIntPtr eventType,
                                             UInt16 count,
                                             string bufferName, 
                                             EventDescriptor Descriptor,
                                             ref EnumerationContext context);

    [CLSCompliant(false)]
    public delegate bool ActiveSourceEntryDelegate(UIntPtr sourceHandle,
                                                   int index,
                                                   EventDescriptor descriptor,
                                                   QueryBuffer entryBuffer,
                                                   ref EnumerationContext context);
    
    [CLSCompliant(false)]
    public delegate bool QueryEntryDelegate(EventDescriptor currentEntry, 
                                            QueryBuffer buffer, 
                                            ref EnumerationContext context);

    [CLSCompliant(false)]
    public delegate bool QueryFieldDelegate(FieldDescriptor fieldDescriptor, 
                                            object value, 
                                            ref EnumerationContext context);

    [CLSCompliant(false)]
    public class FieldDescriptor
    {
        public UInt16 Offset;
        public UInt16 Type;
        public string Name;

        public FieldDescriptor(UInt16 offset, UInt16 type, string name) 
        {
            Offset = offset;
            Type = type;
            Name = name;
        }

        public object GetObject(QueryBuffer buffer)
        {
            unsafe {

                switch (Type) {
                    case DataType.__int8:
                    case DataType.__uint8:
                        fixed(byte * ptrInt = &(buffer.GetBuffer()[buffer.UserOffset + Offset])) {

                            return *(byte *)ptrInt;
                        }

                    case DataType.__int16:
                    case DataType.__uint16:
                        fixed(byte * ptrInt = &(buffer.GetBuffer()[buffer.UserOffset + Offset])) {

                            return *(ushort *)ptrInt;
                        }

                    case DataType.__int32:
                    case DataType.__uint32:
                        fixed(byte * ptrInt = &(buffer.GetBuffer()[buffer.UserOffset + Offset])) {
                            return *(Int32 *)ptrInt;
                        }

                    case DataType.__UIntPtr:
                        fixed(byte * ptrInt = &(buffer.GetBuffer()[buffer.UserOffset + Offset])) {

                            return *(UIntPtr *)ptrInt;
                        }

                    case DataType.__IntPtr:
                        fixed(byte * ptrInt = &(buffer.GetBuffer()[buffer.UserOffset + Offset])) {

                            return *(IntPtr *)ptrInt;
                        }
                        
                    default:
                        DebugStub.WriteLine("Unknown field type");
                    break;
                }
            }

            return null;
        }

        public string GetTypeName() 
        {
            
            switch (Type) {
                case DataType.__int8: return "int8";
                case DataType.__int16: return "int16";
                case DataType.__int32: return "int32";
                case DataType.__int64: return "int64";
                case DataType.__uint8: return "uint8";
                case DataType.__uint16: return "uint16";
                case DataType.__uint32: return "uint32";
                case DataType.__uint64: return "uint64";
                case DataType.__IntPtr: return "IntPtr";
                case DataType.__UIntPtr: return "UIntPtr";
                case DataType.__arrayType:return "ArrayType";

                default:
                    return "unknown";
            }
        }

        
    }

    [CLSCompliant(false)]
    [AccessedByRuntime("output to header : defined in EventController.cpp")]
    public class EventDescriptor
    {
        internal UIntPtr EventHandle;
        internal string EventName;
        public ArrayList Fields;
        internal Hashtable LookupTable;
        internal Controller ControllerObject;

        public EventDescriptor(Controller controller, UIntPtr eventHandle)
        {
            EventHandle = eventHandle;
            ControllerObject = controller;
        }

        public string GetName()
        {
            return EventName;
        }

        public int GetFieldsCount()
        {
            return Fields.Count;
        }
        
        public bool Initialize()
        {
            UIntPtr fieldHandle = 0;
            UInt16 offset = 0;
            UInt16 type = 0;
            string fieldName = "";
            

            Fields = new ArrayList();
            LookupTable = new Hashtable();
            
            do {

                fieldHandle = ControllerObject.WalkEventDescriptor(EventHandle,
                                                                  fieldHandle,
                                                                  ref offset,
                                                                  ref type,
                                                                  ref fieldName);
                
                if (fieldHandle != 0) {

                    if (type == 0) {

                        // This entry describes the event metadata. Fetch the name here
                        
                        EventName = fieldName;
                        
                    } else {

                        // This is an actual field. Addit to our table
                        
                        FieldDescriptor fieldDescriptor = new FieldDescriptor(offset, 
                                                                              type, 
                                                                              fieldName);
                        if (fieldDescriptor == null) {
                            
                            return false;
                        }
                        
                        Fields.Add(fieldDescriptor);
                        LookupTable[fieldName] = fieldDescriptor;
                    }

                }
            } while (fieldHandle != 0);

            Fields.Reverse();

            return true;
            
        }

        public void EnumerateFields(QueryFieldDelegate fieldDelegate, 
                                    QueryBuffer buffer, 
                                    ref EnumerationContext context)
        {
            for (int i = 0; i < Fields.Count; i++) {

                FieldDescriptor fieldDescriptor = Fields[i] as FieldDescriptor;

                if (buffer != null) {
                    
                    if (!fieldDelegate(fieldDescriptor, fieldDescriptor.GetObject(buffer), ref context)) {

                        break;
                    }
                    
                } else {
                
                    if (!fieldDelegate(fieldDescriptor, null, ref context)) {

                        break;
                    }
                }
            }
        }
        
        public object GetProperty(QueryBuffer buffer, string propertyName)
        {
            FieldDescriptor fieldDescriptor = LookupTable[propertyName] as FieldDescriptor;

            if (fieldDescriptor == null) {
                
                return null;
            }

            return fieldDescriptor.GetObject(buffer);
        }
    }

    [CLSCompliant(false)]
    public class QueryBuffer
    {
        private byte [] Buffer = null;
        private UInt16 BufferSize;

        // offset of user-specific data in the buffer
        
        public UInt32 UserOffset;
        public UIntPtr Type;
        public UIntPtr EntryHandle;

        public QueryBuffer(UInt16 size)
        {
            BufferSize = size;
            Buffer = new byte[BufferSize];
        }

        [NoHeapAllocation]
        public byte[] GetBuffer()
        {
            return Buffer;
        }

        [NoHeapAllocation]
        public UInt16 GetBufferSize()
        {
            return BufferSize;
        }
    }

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

        // Implementation details

        private QueryBuffer EntryBuffer;

        private EventingStorage QueryStorage = null;
        private UIntPtr QueryHandle = 0;

        public bool Initialize(EventingStorage storage, 
                               bool forward)
        {
            if (EntryBuffer == null) {
                
                EntryBuffer = new QueryBuffer(256);
                if ((EntryBuffer == null) || (EntryBuffer.GetBuffer() == null)) {

                    return false;
                }
            }

            //  Cleanup for the previous states.

            if (QueryHandle != 0) {
                
                QueryStorage.DeleteQueryView(QueryHandle);
                QueryHandle = 0;
                QueryStorage = null;
            }

            //  Initialize the new query states

            QueryStorage = storage;

            if (QueryStorage == null) {
                
                return false;
            }
            
            QueryHandle = QueryStorage.CreateQueryView(forward);
            
            if (QueryHandle == 0) {

                QueryStorage = null;
                
                return false;
            }

            return true;
        }

        public void EnumerateEntries(QueryEntryDelegate entryDelegate, 
                                     ref EnumerationContext context)
        {
            if ((QueryHandle != 0) && (entryDelegate!= null)) {
                
                UIntPtr entryHandle;
                
                do {
                    
                    entryHandle =  QueryStorage.GetNextEntry(QueryHandle , 
                                                             ref EntryBuffer);

                    if (entryHandle != 0) {

                        if (!entryDelegate(GetEventDescriptor(QueryStorage.GetController(),
                                                              EntryBuffer.Type), 
                                           EntryBuffer, 
                                           ref context)) {

                            break;
                        }
                    }

                } while (entryHandle != 0);

            }
            
        }

        //  General methods for event schema registration and lookup

        internal static Hashtable DescriptorTable = null;
        internal static Mutex Lock = null;
        internal static EventDescriptor EventMetadata = null;

        public static bool InitializeQuerySystem()
        {
            if (DescriptorTable != null) {

                return false;
            }
            
            DescriptorTable = new Hashtable();
            Lock = new Mutex();

            if ((Lock == null) || (DescriptorTable == null) ) {

                return false;
            }
            
            return true;
        }

        public static void FlushCaches()
        {
            Lock.AcquireMutex();
            DescriptorTable.Clear();
            Lock.ReleaseMutex();
        }

        public static EventDescriptor GetEventDescriptor(Controller controller, UIntPtr typeHandle)
        {
            Lock.AcquireMutex();
            EventDescriptor descriptor = DescriptorTable[typeHandle] as EventDescriptor;
            
            if (descriptor != null) {

                Lock.ReleaseMutex();
                return descriptor;
            }

            descriptor = new EventDescriptor(controller, typeHandle);
            
            if ((descriptor != null) && descriptor.Initialize()) {

                DescriptorTable[typeHandle] = descriptor;

                if (typeHandle == 0) {

                    EventMetadata = descriptor;
                }

            } else {

                descriptor = null;
            }

            Lock.ReleaseMutex();
            return descriptor;
        }
    }
}
