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

using System;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.Singularity;
using Microsoft.Singularity.Memory;
using Microsoft.Singularity.Security;
using Microsoft.Singularity.Io;
using Microsoft.Singularity.V1.Security;
using Microsoft.Singularity.Eventing;

namespace Microsoft.Singularity.V1.Services
{
    [CLSCompliant(false)]
    public struct LogEntry
    {
    }

    public enum  ParameterCode {
        Success,
        OutOfRange,
        NotSet,
        Retrieved,
        Undefined,
    }

    [CLSCompliant(false)]
    [AccessedByRuntime("referenced from Tracing.cpp")]
    public struct ProcessService
    {
        // In order to pass the arguments to the kernel, we first need to
        // flatten the arguments parameter from an array of strings to
        // an array of characters (containing the contents of the strings).

        // If argVector is null we compute the the length needed to store
        // the result. It is up to the calling process to then allocate
        // the memory and call again.
        private static unsafe int FlattenStringArray(string[] arguments,
                                                      int* argLengths,
                                                      char* argVector
                                                      )
        {
            int totalCharacters = 0;

            int len = arguments.Length;
            for (int arg = 0; arg < len; arg++) {
                string argstring = arguments[arg];
                if (argstring == null) continue;
                totalCharacters += argstring.Length;
            }

            if (argVector == null) {
                return totalCharacters;
            }

            int offset = 0;
            for (int arg = 0; arg < len; arg++) {
                string argstring = arguments[arg];
                if (argstring == null) {
                    argLengths[arg] = 0;
                    continue;
                }
                int alen = argstring.Length;
                //argstring.CopyTo(0, argVector, offset, alen);
                for (int i = 0; i < alen; i++) {
                    argVector[offset+i] = argstring[i];
                }
                offset += alen;
                argLengths[arg] = alen;
            }
           return totalCharacters;
        }

        [ExternalEntryPoint]
        public static void Stop(int exitCode)
        {
            Tracing.Log(Tracing.Debug, "ProcessService.Stop(exit={0})",
                        (UIntPtr)unchecked((uint)exitCode));

            Thread.CurrentProcess.Stop(exitCode);
        }

        [ExternalEntryPoint]
        public static int GetCpuCount()
        {
            return Processor.GetRunningProcessorCount();
        }


        [ExternalEntryPoint]
        public static TimeSpan GetUpTime()
        {
            TimeSpan ts = SystemClock.KernelUpTime;
            return ts;
        }

        [ExternalEntryPoint]
        public static DateTime GetUtcTime()
        {
            DateTime dt = SystemClock.GetUtcTime();
            return dt;
        }

        [ExternalEntryPoint]
        public static long GetCycleCount()
        {
            return (long)Processor.CycleCount;
        }

        [ExternalEntryPoint]
        public static long GetCyclesPerSecond()
        {
            Kernel.Waypoint(300);
            return (long)Processor.CyclesPerSecond;
        }

        [ExternalEntryPoint]
        public static long GetContextSwitchCount()
        {
            int processors = Processor.CpuCount;
            long contextSwitches = 0;
            for (int processor = 0; processor < processors; processor++) {
                Processor p = Processor.GetProcessor(processor);
                contextSwitches += p.NumContextSwitches;
            }
            return contextSwitches;
        }

        [ExternalEntryPoint]
        public static void SetGcPerformanceCounters(TimeSpan spent, long bytes)
        {
            Thread.CurrentProcess.SetGcPerformanceCounters(spent, bytes);
        }

        [ExternalEntryPoint]
        public static long GetKernelGcCount()
        {
            int collectorCount;
            long collectorMillis;
            long collectorBytes;

            GC.PerformanceCounters(out collectorCount,
                                   out collectorMillis,
                                   out collectorBytes);

            return collectorCount;
        }

        [ExternalEntryPoint]
        public static long GetKernelBootCount()
        {
            return Resources.GetWarmBootCount();
        }

        [ExternalEntryPoint]
        public static long GetKernelInterruptCount()
        {
            int processors = Processor.CpuCount;
            long interrupts = 0;
            for (int processor = 0; processor < processors; processor++) {
                Processor p = Processor.GetProcessor(processor);
                interrupts += p.NumInterrupts;
            }
            return interrupts;
        }

        [ExternalEntryPoint]
        public static ushort GetCurrentProcessId()
        {
            return (ushort)Thread.CurrentProcess.ProcessId;
        }

        //
        //[ExternalEntryPoint]
        //public static PrincipalHandle GetCurrentPrincipal()
        //{
        //  return new PrincipalHandle(Thread.CurrentProcess.PrincipalId.val);
        //}
        //

        [ExternalEntryPoint]
        // Return parameter is really: DirectoryService.Imp opt(ExHeap) *
        public static unsafe SharedHeapService.Allocation * GetNamespaceEndpoint()
        {
            return (SharedHeapService.Allocation *)
                Thread.CurrentProcess.GetNamespaceEndpoint();
        }

        [ExternalEntryPoint]
        public static int GetStartupEndpointCount() {
            return Thread.CurrentProcess.GetStartupEndpointCount();
        }

        [ExternalEntryPoint]
        // Return parameter is really: ExtensionContract.Exp opt(ExHeap) *
        public static unsafe SharedHeapService.Allocation * GetStartupEndpoint(int arg)
        {
            return (SharedHeapService.Allocation *)
                Thread.CurrentProcess.GetStartupEndpoint(arg);
        }

        [ExternalEntryPoint]
        // Parameter is really: ExtensionContract.Exp opt(ExHeap) *
        public static unsafe void SetStartupEndpoint(int arg,
                                                     SharedHeapService.Allocation * ep)
        {
            SharedHeap.Allocation * mep = (SharedHeap.Allocation *)ep;
            Thread.CurrentProcess.SetEndpoint(arg, ref mep);
        }

        [ExternalEntryPoint]
        public static int GetStartupArgCount()
        {
            return Thread.CurrentProcess.GetStartupArgCount();
        }

        [ExternalEntryPoint]
        public static unsafe int GetStartupArg(int arg, char * output, int maxput)
        {
            string s = Thread.CurrentProcess.GetStartupArg(arg);
            Tracing.Log(Tracing.Debug,
                        "Process.GetStartupArg(arg={0}, out={1:x8}, max={2}) = {3}",
                        (UIntPtr)unchecked((uint)arg),
                        (UIntPtr)output,
                        (UIntPtr)unchecked((uint)maxput),
                        (UIntPtr)unchecked((uint)(s != null ? s.Length : 0)));

            if (s == null) {
                return 0;
            }
            if (output == null) {
                return s.Length + 1;
            }
            return s.InternalGetChars(output, maxput);
        }

        [ExternalEntryPoint]
        public static TimeSpan GetThreadTime() {
            return Thread.CurrentThread.ExecutionTime;
        }

        [ExternalEntryPoint]
        public static long GetThreadsCreatedCount() {
            return PerfCounters.ThreadsCreated;
        }

        [ExternalEntryPoint]
        public static unsafe bool GetSharedSourceHandlesImpl(uint infoId,
                                                             UIntPtr * storageHandle,
                                                             UIntPtr * sourceHandle,
                                                             UIntPtr * eventTypeHandle)
        {
            return GetSharedSourceHandles(infoId, out *storageHandle, out *sourceHandle, out *eventTypeHandle);
        }

        [AccessedByRuntime("referenced from Monitoring.cpp")]
        public static unsafe bool GetSharedSourceHandles(uint infoId,
                                                         out UIntPtr storageHandle,
                                                         out UIntPtr sourceHandle,
                                                         out UIntPtr eventTypeHandle)
        {
            return Controller.GetSharedSourceHandles(infoId, out storageHandle, out sourceHandle, out eventTypeHandle);
        }


        [ExternalEntryPoint]
        public static void Waypoint0()
        {
            Kernel.Waypoint0();
        }

        [ExternalEntryPoint]
        public static void Waypoint(int num)
        {
            Kernel.Waypoint(num);
        }

        [ExternalEntryPoint]
        public static void WaypointDone()
        {
            Kernel.WaypointDone();
        }

        [ExternalEntryPoint]
        public static void WaypointDump()
        {
            Kernel.WaypointDump();
        }

        [ExternalEntryPoint]
        public static int GetStartupBoolArgCount() {
            return Thread.CurrentProcess.GetStartupBoolArgCount();
        }

        [ExternalEntryPoint]
        public static int GetStartupLongArgCount() {
            return Thread.CurrentProcess.GetStartupLongArgCount();
        }

        [ExternalEntryPoint]
        public static int GetStartupStringArgCount() {
            return Thread.CurrentProcess.GetStartupStringArgCount();
        }

        [ExternalEntryPoint]
        public static int GetStartupStringArrayArgCount() {
            return Thread.CurrentProcess.GetStartupStringArrayArgCount();
        }

        [ExternalEntryPoint]
        public static unsafe ParameterCode GetStartupLongArgImpl(
            int index,
            long * value)
        {
            return GetStartupLongArg(index, out *value);
        }

        public static ParameterCode GetStartupLongArg(int index, out long value) {
            return (ParameterCode) Thread.CurrentProcess.GetStartupLongArg(index, out value);
        }

        [ExternalEntryPoint]
        public static unsafe ParameterCode GetStartupBoolArgImpl(
            int index,
            bool * value)
        {
            return GetStartupBoolArg(index, out *value);
        }

        public static ParameterCode GetStartupBoolArg(int index, out bool value) {
            return (ParameterCode)Thread.CurrentProcess.GetStartupBoolArg(index, out value);
        }

        [ExternalEntryPoint]
        public static unsafe ParameterCode GetStartupStringArrayArgImpl(
                                            int index,
                                            char *strbuff,
                                            int  *len,
                                            int *arrayLength,
                                            int *totalCharCount)
        {
            ParameterCode code;
            string[] strings;
            code = (ParameterCode) Thread.CurrentProcess.GetStartupStringArrayArg(index, out strings);
            if (code != ParameterCode.Success) {
                *arrayLength = 0;
                *totalCharCount = 0;
                return code;
            }
            if (strings == null) {
                *arrayLength = 0;
                *totalCharCount = 0;
                return ParameterCode.NotSet;
            }
            *totalCharCount = FlattenStringArray(strings, len, strbuff);
            *arrayLength = strings.Length;
            return ParameterCode.Success;
        }

        [ExternalEntryPoint]
        public static unsafe ParameterCode GetStartupStringArgImpl(
            int arg,
            char * output,
            int * maxput)
        {
            return GetStartupStringArg(arg, output, ref *maxput);
        }

        public static unsafe ParameterCode GetStartupStringArg(int arg, char * output, ref int maxput)
        {
            string s;
            ParameterCode code = (ParameterCode)Thread.CurrentProcess.GetStartupStringArg(arg, out s);
            Tracing.Log(Tracing.Debug,
                "Process.GetStartupStringArg(arg={0}, out={1:x8}, max={2}) = {3}",
                (UIntPtr)unchecked((uint)arg),
                (UIntPtr)output,
                (UIntPtr)unchecked((uint)maxput),
                (UIntPtr)unchecked((uint)(s != null ? s.Length : 0)));

            if (code != ParameterCode.Success) {
                return code;
            }
            if (output == null) {
                if (s == null) maxput = 0;
                else maxput = s.Length ;
                return ParameterCode.Success;
            }
            if (s != null) s.InternalGetChars(output, maxput);
            return ParameterCode.Success;
        }


    }
}
