////////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File: EventController.cpp
//
//  Note:   Kernel & Process
//

#include "hal.h"
#include "eventing.h"

extern SOURCE_CONTROLLER SourceController;

#include "csformat.inc"

extern int strformat(void (*pfOutput)(void *pContext, char c), void *pContext,
                     const char * pszFmt, va_list args);


struct SNPRINT_CONTEXT {

    char ** CrtPos;
    char * EndBuffer;
};

static void snprintfout(void *pContext, char c)
{
    SNPRINT_CONTEXT * context = (SNPRINT_CONTEXT *)pContext;

    if (*(context->CrtPos) < context->EndBuffer) {

        *(*context->CrtPos)++ = c;
    }
}

int snprintf(char *pszOut, int outSize, const char *pszFmt, ...)
{
    int nOut;
    va_list args;
    SNPRINT_CONTEXT context = {&pszOut, pszOut + outSize - 1};

    va_start(args, pszFmt);
    nOut = strformat(snprintfout, &context, pszFmt, args);
    va_end(args);

    *pszOut = '\0';

    return nOut;
}

extern void kdprints(const char * pszFmt);

PEVENT_FIELD_DESCRIPTOR GetDescriptorField(PEVENT_DESCRIPTOR typeEntry, int fieldIndex)
{
    PEVENT_FIELD_DESCRIPTOR field;

    //
    //  TODO: This needs to be optimized caching the total number of fields
    //

    int numFields = 0;

    for (field = typeEntry->fieldsLink; (field != NULL); field = field->fieldsLink) {

        numFields++;
    }

    fieldIndex = numFields - fieldIndex - 1;

    for (field = typeEntry->fieldsLink; (field != NULL) && (fieldIndex > 0); field = field->fieldsLink) {

        fieldIndex--;
    }

    return field;
}


PEVENT_FIELD_DESCRIPTOR FindFieldDescriptor(PEVENT_DESCRIPTOR typeEntry, int offset)
{
    PEVENT_FIELD_DESCRIPTOR field;

    for (field = typeEntry->fieldsLink; field != NULL; field = field->fieldsLink) {

        if (field->Offset == offset) {

            return field;
        }
    }
    return NULL;
}


uint64 ReadValueAs(void * baseAddress, int offset, int type)
{
    void * memLocation = (char *)baseAddress + offset;

    if (type & EVENT_FIELD_TYPE_arrayType) {

        return *(uint16 *)memLocation;
    }

    switch (type) {

        case EVENT_FIELD_TYPE_int8:
        case EVENT_FIELD_TYPE_uint8:
            return *(uint8 *)memLocation;

        case EVENT_FIELD_TYPE_int16:
        case EVENT_FIELD_TYPE_uint16:
            return *(uint16 *)memLocation;

        case EVENT_FIELD_TYPE_int32:
        case EVENT_FIELD_TYPE_uint32:
            return *(uint32 *)memLocation;

        case EVENT_FIELD_TYPE_int64:
        case EVENT_FIELD_TYPE_uint64:
            return *(uint64 *)memLocation;

        case EVENT_FIELD_TYPE_IntPtr:
        case EVENT_FIELD_TYPE_UIntPtr:
            return (uint64)(*(UIntPtr *)memLocation);
    }

    return 0;
}

uint64 GetFieldValue(UIntPtr EntryHandle, int fieldIndex)
{
    PMEMORY_HEADER entry = HANDLE_TO_HEADER(EntryHandle);
    PEVENT_DESCRIPTOR typeEntry = HANDLE_TO_TYPE(entry->Type);
    PEVENT_FIELD_DESCRIPTOR field = GetDescriptorField(typeEntry, fieldIndex);

    void * base = GetUserRecordStructure(entry);

    if (field != NULL) {

        return ReadValueAs(GetUserRecordStructure(entry), field->Offset, field->Type);
    }

    return 0;
}

struct PRINT_EVENT_CONTEXT {

    UIntPtr EntryHandle;
    int ArgOffset;
};

int WriteSymbolicValue(char *pszOut, int bufferSize, PENUM_DESCRIPTOR enumDescriptor, uint64 value)
{
    PEVENT_VALUE_DESCRIPTOR field;
    int retValue = 0;

    uint64 flags = value & enumDescriptor->FlagsMask;
    uint64 item = value & ~enumDescriptor->FlagsMask;

    for (field = enumDescriptor->fieldsLink; field != NULL; field = field->fieldsLink) {

        int ret = 0;

        if ((field->FlagLetter != 0)) {

            //
            //  This needs to be elaborated further with using a string for the false case too
            //

            char symbol = ((field->Value & flags) == field->Value) ? field->FlagLetter : '.';

            ret = snprintf(pszOut, bufferSize, "%c", symbol);

        } else if (field->Value == item) {

            char * strValue = GetExtendedString((UIntPtr)((PMEMORY_HEADER)field - 1), 1);

            ret = snprintf(pszOut, bufferSize, "%s", strValue);

        }

        //
        //  Advance the output buffer, updating the current number of bytes written
        //

        retValue += ret;
        pszOut += ret;
        bufferSize -= ret;
    }

    return retValue;

}

char * GetStringField(void * context, int argIdx)
{
    PRINT_EVENT_CONTEXT * eventContext = (PRINT_EVENT_CONTEXT*) context;

    argIdx += eventContext->ArgOffset;

    PMEMORY_HEADER entry = HANDLE_TO_HEADER(eventContext->EntryHandle);
    PEVENT_DESCRIPTOR typeEntry = HANDLE_TO_TYPE(entry->Type);

    PEVENT_FIELD_DESCRIPTOR field = GetDescriptorField(typeEntry, argIdx);

    if (field->Type & (Class_Microsoft_Singularity_Eventing_DataType___string |
                       Class_Microsoft_Singularity_Eventing_DataType___szChar)) {

        int extendedIndex = (int)GetFieldValue(eventContext->EntryHandle, argIdx);

        if (extendedIndex > 0) {

            char * str = GetExtendedString(eventContext->EntryHandle, extendedIndex);

            //
            //  From this point all formating is relative to this string
            //

            eventContext->ArgOffset = argIdx + 1;

            return str;
        }
    }

    return NULL;
}

int PrintEventField(void * context, char *pszOut, int bufferSize, int aln, int wid, char fmt, int argIdx)
{
    PRINT_EVENT_CONTEXT * eventContext = (PRINT_EVENT_CONTEXT*) context;
    argIdx += eventContext->ArgOffset;

    PMEMORY_HEADER entry = HANDLE_TO_HEADER(eventContext->EntryHandle);
    PEVENT_DESCRIPTOR typeEntry = HANDLE_TO_TYPE(entry->Type);

    PEVENT_FIELD_DESCRIPTOR field = GetDescriptorField(typeEntry, argIdx);

    if (field == NULL) return 0;

    int retValue = 0;
    char * str = NULL;

    if (field->Type == GENERIC_TYPE_SIGNATURE) {

        //
        //  This might be a nested structure or an enum. The process of retrieving the
        //  actual value becomes a bit more complicated. The existing entry will be placed
        //  at a known offset in the parent structure, but we cannot interpret the value unless
        //  we find out the descriptor for this field.
        //

        if (((PMEMORY_HEADER)field - 1)->Flags == RECORD_EVENT_GENERIC_FIELD) {

            //
            //  After validating the descriptor version agains the nested type, we can safely
            //  cast to a generic field
            //

            PEVENT_GENERIC_TYPE_DESCRIPTOR genericType = (PEVENT_GENERIC_TYPE_DESCRIPTOR)field;
            UIntPtr typeHandle = genericType->GenericTypeHandle;
            PEVENT_DESCRIPTOR descriptor = (PEVENT_DESCRIPTOR)GetUserRecordStructure(HANDLE_TO_HEADER(typeHandle));

            if (((PMEMORY_HEADER)descriptor - 1)->Flags == RECORD_EVENT_ENUM) {

                //
                //  The field is an enum. Handle here the symbolic
                //

                PENUM_DESCRIPTOR enumDescriptor = (PENUM_DESCRIPTOR)descriptor;
                uint16 valueBasicType = enumDescriptor->Type;
                uint64 value = ReadValueAs(GetUserRecordStructure(entry), field->Offset, valueBasicType);

                retValue = WriteSymbolicValue(pszOut, bufferSize, enumDescriptor, value);

            } else {

                retValue = snprintf(pszOut, bufferSize, "(unsupported field)");
            }

        } else {

            retValue = snprintf(pszOut, bufferSize, "(unknown)");
        }

    } else if (field->Type &
                    (Class_Microsoft_Singularity_Eventing_DataType___string |
                     Class_Microsoft_Singularity_Eventing_DataType___szChar)) {

        int extendedIndex = (int)GetFieldValue(eventContext->EntryHandle, argIdx);

        if (extendedIndex > 0) {

            char * str = GetExtendedString(eventContext->EntryHandle, extendedIndex);

            if (str != NULL) {

                retValue = snprintf(pszOut, bufferSize, "%s", str);
            } else {

                retValue = snprintf(pszOut, bufferSize, "(invalid string index)");
            }
        }

    } else {

        uint64 value = GetFieldValue(eventContext->EntryHandle, argIdx);

        if (aln < wid) {
            aln = wid;
        }

        if (fmt == 'x') {
            if (wid > 0) {
                retValue = snprintf(pszOut, bufferSize, "%0x", value);
            }
            else {
                retValue = snprintf(pszOut, bufferSize, "%x", value);
            }
        }
        else {
            retValue = snprintf(pszOut, bufferSize, "%d", value);
        }

    }

    return retValue;
}

void DebugPrintEvent(UIntPtr eventHandle)
{
    char msg[256];
    PRINT_EVENT_CONTEXT printContext = {eventHandle, 0};

    PMEMORY_HEADER entry = HANDLE_TO_HEADER(eventHandle);
    char * frmt = GetExtendedString(entry->Type, 2);

    if (frmt != NULL) {

        FormatCSOutput(&printContext, frmt, msg, sizeof(msg),PrintEventField, GetStringField);
    }

    bartok_char wmsg[256];
    int lMsg = strlen(msg);

    if (lMsg < (sizeof(msg) - 1)) {

        msg[lMsg] = '\n';
        lMsg += 1;
        msg[lMsg] = 0;
    }

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

        wmsg[i] = msg[i];
    }
    Class_Microsoft_Singularity_DebugStub::g_Print(wmsg, lMsg);
    //kdprints(msg);
}


bool Class_Microsoft_Singularity_Eventing_KernelController::
    g_DebugPrintLogEntry(UIntPtr controllerHandle, UIntPtr entryHandle)
{
    //
    //  TODO: Handle the paging case with apporpriate access as
    //

    DebugPrintEvent(entryHandle);
    return true;
}

bool Class_Microsoft_Singularity_Eventing_KernelController::
    g_RegisterExternalController(UIntPtr storageHandle, UIntPtr contextHandle)
{
    //  The external caller is responsible with the synchronization

    EXTERNAL_CONTROLLER_DESCRIPTOR source = {NULL, storageHandle, contextHandle};

    if (SourceController.FreeControllerList != NULL) {

        PEXTERNAL_CONTROLLER_DESCRIPTOR controller = SourceController.FreeControllerList;
        SourceController.FreeControllerList = controller->Link;
        *controller = source;
        RegisterExternalController(controller);

    } else {

        PMEMORY_HEADER Entry = InternalLogFixedRecord( GetLocalRepositoryHandle(),
            RECORD_EVENT_CONTROLLER,
            0,
            &source,
            sizeof(source));

        if (Entry == NULL) {
            return false;
        }

        PEXTERNAL_CONTROLLER_DESCRIPTOR newSource = (PEXTERNAL_CONTROLLER_DESCRIPTOR)GetUserRecordStructure(Entry);
        RegisterExternalController(newSource);
    }

    return true;
}

void Class_Microsoft_Singularity_Eventing_KernelController::
    g_UnRegisterExternalController(UIntPtr controllerHandle, UIntPtr contextHandle)
{
    //  Note the caller of this function needs to assure mutual exclusion

    PEXTERNAL_CONTROLLER_DESCRIPTOR tmpController = SourceController.ExternalControllers;

    for (tmpController = SourceController.ExternalControllers;
         tmpController != NULL;
         tmpController = tmpController->Link) {

        if ((tmpController->ContextHandle == contextHandle) &&
            (tmpController->ControllerHandle == controllerHandle)){

            UnRegisterExternalController(tmpController);

            return;
        }
    }
}

//
///////////////////////////////////////////////////////////////// End of File.


