/*
* Copyright (c) 2017, Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

#include "cm_include.h"
#include "cm_printf_host.h"
#include "cm_debug.h"
#include "cm_mem.h"

void PFParser::getToken(void)
{
    m_prevToken = m_currToken;
    m_currToken = Token();

    // This is a lexer that has 2 modes
    // Inside a conversion specification and outside
    while (*m_currLoc != '\0')
    {
        if (!m_inSpec)
        {
            // We're just picking off characters until we see a %
            m_currToken.tokenType = Token::String;

            while (*m_currLoc != '\0')
            {
                if (*m_currLoc == '%')
                {
                    // Peek to check for %%
                    if (*(m_currLoc+1) != '\0' && *(m_currLoc+1) != '%')
                    {
                        // This is definitely a directive, not a %%
                        break;
                    }
                    // This IS %% so take another character off the input
                    m_currToken.tokenString += *m_currLoc++;
                }
                m_currToken.tokenString += *m_currLoc++;
            }

            if (*m_currLoc == '%')
                m_inSpec = true;

            if (m_currToken.tokenString.length() > 0)
                return;
        }

        if (m_inSpec)
        {
            char currChar = *m_currLoc++;
            switch(currChar)
            {
            default:
                // We've had an unexpected character
                m_currToken.tokenType = Token::Error;
                m_currToken.tokenString += currChar; // Preserve the error char
                m_inSpec = false; // End of the format spec
                return;
            case '%':
                m_currToken.tokenType = Token::Percent;
                return;
            case '-':
                m_currToken.tokenType = Token::Minus;
                return;
            case '+':
                m_currToken.tokenType = Token::Plus;
                return;
            case ' ':
                m_currToken.tokenType = Token::Space;
                return;
            case '.':
                m_currToken.tokenType = Token::Period;
                return;
            case '#':
                m_currToken.tokenType = Token::Hash;
                return;
            case '*':
                m_currToken.tokenType = Token::Star;
                return;
            case 'h':
                // Have to deal with 'hh'
                if (*m_currLoc == 'h')
                {
                    m_currLoc++;
                    m_currToken.tokenType = Token::hh_Mod;
                    return;
                }
                m_currToken.tokenType = Token::h_Mod;
                return;
            case 'l':
                // Have to deal with 'll'
                if (*m_currLoc == 'l')
                {
                    m_currLoc++;
                    m_currToken.tokenType = Token::ll_Mod;
                    return;
                }
                m_currToken.tokenType = Token::l_Mod;
                return;
            case 'j':
                m_currToken.tokenType = Token::j_Mod;
                return;
            case 'z':
                m_currToken.tokenType = Token::z_Mod;
                return;
            case 't':
                m_currToken.tokenType = Token::t_Mod;
                return;
            case 'L':
                m_currToken.tokenType = Token::L_Mod;
                return;
            case 'c':
                m_currToken.tokenType = Token::c_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 's':
                m_currToken.tokenType = Token::s_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'd':
                m_currToken.tokenType = Token::d_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'i':
                m_currToken.tokenType = Token::i_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'o':
                m_currToken.tokenType = Token::o_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'x':
                m_currToken.tokenType = Token::x_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'X':
                m_currToken.tokenType = Token::X_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'u':
                m_currToken.tokenType = Token::u_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'f':
                m_currToken.tokenType = Token::f_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'F':
                m_currToken.tokenType = Token::F_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'e':
                m_currToken.tokenType = Token::e_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'E':
                m_currToken.tokenType = Token::E_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'a':
                m_currToken.tokenType = Token::a_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'A':
                m_currToken.tokenType = Token::A_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'g':
                m_currToken.tokenType = Token::g_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'G':
                m_currToken.tokenType = Token::G_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'n':
                m_currToken.tokenType = Token::n_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case 'p':
                m_currToken.tokenType = Token::p_Conv;
                m_inSpec = false; // End of the format spec
                return;
            case '0':
                if (*m_currLoc < '1' || *m_currLoc > '9')
                {
                    // Next character not part of a larger integer
                    m_currToken.tokenType = Token::Zero;
                    return;
                }
                // Deliberately drop through
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                m_currToken.tokenString += currChar;
                while (*m_currLoc >= '0' && *m_currLoc <= '9')
                {
                    m_currToken.tokenString += *m_currLoc++;
                }
                // Create integer
                // Since we've created this integer string and we know it is simple we can use
                // atoi knowing that it won't throw an error
                m_currToken.tokenString = atoi(m_currToken.tokenString.c_str());
                m_currToken.tokenType = Token::Integer;
                return;
            }
        }
    }

    m_currToken.tokenType = Token::End;
}

bool PFParser::accept(PFParser::Token::TokenType s)
{
    if (m_currToken == s)
    {
        getToken();
        return true;
    }
    return false;
}

bool PFParser::expect(Token::TokenType s)
{
    if (accept(s))
        return true;
    error();
    return false;
}

int PFParser::format(void)
{
    if (m_currToken == Token::_None_)
    {
        getToken();
    }
    while(m_currToken != Token::End && m_currToken != Token::Error)
    {
        if (accept(Token::String))
        {
        }
        else if (accept(Token::Percent))
        {
            return directive();
        }
    }
    return 0;
}

int PFParser::directive(void)
{
    int numArgs = 0;
    flags();
    numArgs += width();
    numArgs += precision();
    length_modifier();

    int numConvArgs = conversion();
    if (numConvArgs == 0)
    {
        // Not expecting ANY arguments
        // Ignore any previous directives (width, precision etc.)
        numArgs = 0;
    }
    else
    {
        numArgs += numConvArgs;
    }

    return numArgs;
}

void PFParser::flags(void)
{
    if (accept(Token::Minus))
    {
    }

    if (accept(Token::Plus))
    {
    }

    if (accept(Token::Space))
    {
    }

    if (accept(Token::Zero))
    {
    }

    if (accept(Token::Hash))
    {
    }
}

int PFParser::width(void)
{
    if (accept(Token::Integer))
    {
        return 0;
    }
    else if (accept(Token::Star))
    {
        return 1;
    }
    return 0;
}

int PFParser::precision(void)
{
    if (accept(Token::Period))
    {
        if (accept(Token::Integer))
        {
            return 0;
        }
        else if (expect(Token::Star))
        {
            return 1;
        }
        return 0;
    }
    return 0;
}

void PFParser::length_modifier(void)
{
    if (accept(Token::hh_Mod))
    {
    }
    else if (accept(Token::h_Mod))
    {
    }
    else if (accept(Token::l_Mod))
    {
    }
    else if (accept(Token::ll_Mod))
    {
    }
    else if (accept(Token::j_Mod))
    {
        m_unsupported = true;
    }
    else if (accept(Token::t_Mod))
    {
        m_unsupported = true;
    }
    else if (accept(Token::z_Mod))
    {
        m_unsupported = true;
    }
    else if (accept(Token::L_Mod))
    {
    }
}

int PFParser::conversion(void)
{
    int numArgs = 1;
    if (accept(Token::Percent))
    {
        numArgs = 0;
    }
    else if (accept(Token::c_Conv))
    {
    }
    else if (accept(Token::s_Conv))
    {
    }
    else if (accept(Token::d_Conv))
    {
    }
    else if (accept(Token::i_Conv))
    {
    }
    else if (accept(Token::o_Conv))
    {
    }
    else if (accept(Token::x_Conv))
    {
    }
    else if (accept(Token::X_Conv))
    {
    }
    else if (accept(Token::u_Conv))
    {
    }
    else if (accept(Token::f_Conv))
    {
    }
    else if (accept(Token::F_Conv))
    {
    }
    else if (accept(Token::e_Conv))
    {
    }
    else if (accept(Token::E_Conv))
    {
    }
    else if (accept(Token::a_Conv))
    {
    }
    else if (accept(Token::A_Conv))
    {
    }
    else if (accept(Token::g_Conv))
    {
    }
    else if (accept(Token::G_Conv))
    {
    }
    else if (accept(Token::n_Conv))
    {
        m_unsupported = true;
    }
    else if (expect(Token::p_Conv))
    {
    }
    else
    {
        // Expect must have failed
        numArgs = 0;
    }
    return numArgs;
}

int CalcSizeFromHeader(unsigned char * memory)
{
    PCM_PRINT_HEADER header = (PCM_PRINT_HEADER)memory;

    if((header->objectType == CM_PRINT_OBJECT_TYPE_MATRIX) ||
       (header->objectType == CM_PRINT_OBJECT_TYPE_VECTOR))
    {
        switch (header->dataType)
        {
            case CM_PRINT_DATA_TYPE_CHAR:
                return CM_PRINT_SIZE_WITH_PAYLOAD(header->height*header->width*sizeof(char           ));

            case CM_PRINT_DATA_TYPE_UCHAR:
                return CM_PRINT_SIZE_WITH_PAYLOAD(header->height*header->width*sizeof(unsigned char  ));

            case CM_PRINT_DATA_TYPE_INT:
                return CM_PRINT_SIZE_WITH_PAYLOAD(header->height*header->width*sizeof(int            ));

            case CM_PRINT_DATA_TYPE_UINT:
                return CM_PRINT_SIZE_WITH_PAYLOAD(header->height*header->width*sizeof(unsigned int   ));

            case CM_PRINT_DATA_TYPE_FLOAT:
                return CM_PRINT_SIZE_WITH_PAYLOAD(header->height*header->width*sizeof(float          ));

            case CM_PRINT_DATA_TYPE_SHORT:
                return CM_PRINT_SIZE_WITH_PAYLOAD(header->height*header->width*sizeof(short          ));

            case CM_PRINT_DATA_TYPE_USHORT:
                return CM_PRINT_SIZE_WITH_PAYLOAD(header->height*header->width*sizeof(unsigned short ));

            case CM_PRINT_DATA_TYPE_QWORD:
                return CM_PRINT_SIZE_WITH_PAYLOAD(header->height*header->width*sizeof(long long      ));

            case CM_PRINT_DATA_TYPE_UQWORD:
                return CM_PRINT_SIZE_WITH_PAYLOAD(header->height*header->width*sizeof(unsigned long long ));

            case CM_PRINT_DATA_TYPE_DOUBLE:
                return CM_PRINT_SIZE_WITH_PAYLOAD(header->height*header->width*sizeof(double         ));

            default:
                CmAssert(0);
        }
        return PRINT_HEADER_SIZE;
    }
    else if(header->objectType == CM_PRINT_OBJECT_TYPE_STRING ||
            header->objectType == CM_PRINT_OBJECT_TYPE_FORMAT)
    {
        return PRINT_HEADER_SIZE + PRINT_FORMAT_STRING_SIZE;
    }
    else if(header->objectType == CM_PRINT_OBJECT_TYPE_SCALAR)
    {
        if(!((header->dataType == CM_PRINT_DATA_TYPE_CHAR)   ||
             (header->dataType == CM_PRINT_DATA_TYPE_UCHAR)  ||
             (header->dataType == CM_PRINT_DATA_TYPE_UINT)   ||
             (header->dataType == CM_PRINT_DATA_TYPE_INT)    ||
             (header->dataType == CM_PRINT_DATA_TYPE_USHORT) ||
             (header->dataType == CM_PRINT_DATA_TYPE_SHORT)  ||
             (header->dataType == CM_PRINT_DATA_TYPE_DOUBLE) ||
             (header->dataType == CM_PRINT_DATA_TYPE_QWORD)  ||
             (header->dataType == CM_PRINT_DATA_TYPE_UQWORD) ||
             (header->dataType == CM_PRINT_DATA_TYPE_FLOAT)))
        {
            CmAssert(0);
        }
        return PRINT_HEADER_SIZE;
    }
    else
    {
        CmAssert(0);
        return PRINT_HEADER_SIZE;
    }
}

void PFParser::Flush(void)
{
    if (m_inputStart && m_currLoc)
    {
        if (m_currToken != Token::End &&
            m_currToken != Token::_None_)
        {
            // Tidy up any remaining characters
            // Any characters that remain to be flushed need to be check for illegal directives (e.g. %n
            // will cause an exception if attempted to be printed with no argument)
            int numArgs = format();
            if (m_unsupported)
            {
                CM_PRINTF(m_streamOut,"Unsupported (but valid C++11) format string used : %s", m_inputStart);
                reset();
            }
            else if (m_error)
            {
                CM_PRINTF(m_streamOut,"Error in printf format string : %s", m_inputStart);
                reset();
            }
            else if (numArgs > 0)
            {
                // Not enough arguments provided for remaining directives
                CM_PRINTF(m_streamOut,"Not enough (no) arguments supplied for format string : %s", m_inputStart);
                reset();
            }
            else
            {
                CM_PRINTF(m_streamOut,"%s", m_inputStart);
            }
        }
        reset();
    }
}

PRINT_FMT_STATUS PFParser::GetNextFmtToken(char* tkn, size_t size)
{
    memset(tkn, 0, size);

    if (m_numMultArg)
    {
        if (!m_argsExpected)
        {
            // Copy the whole of the format string into the token
            if ((size_t)(m_currLoc - m_inputStart) <= size)
            {
                CmSafeMemCopy(tkn, m_inputStart, m_currLoc - m_inputStart);
                tkn[m_currLoc - m_inputStart] = '\0';
                return PF_SUCCESS;
            }
            return PF_FAIL;
        }
        // Still processing input arguments
        return PF_SUCCESS;
    }

    int numArgs = format();
    switch (numArgs)
    {
    default:
        return PF_FAIL; // Something has gone wrong
    case 0:
    case 1:
        // Copy the whole of the format string into the token
        if ((size_t)(m_currLoc - m_inputStart) <= size)
        {
            CmSafeMemCopy(tkn, m_inputStart, m_currLoc - m_inputStart);
            tkn[m_currLoc - m_inputStart] = '\0';
            return PF_SUCCESS;
        }
        return PF_FAIL;
    case 2:
    case 3:
        m_numMultArg = numArgs - 1;
        m_argsExpected = numArgs - 1;
        return PF_SUCCESS;
    }
}

bool PFParser::outputToken(const char *tkn, PCM_PRINT_HEADER header)
{
    if (m_numMultArg && m_argsExpected)
    {
        // Processing items for multi-arg directives
        if (header->objectType == CM_PRINT_OBJECT_TYPE_SCALAR &&
            header->dataType != CM_PRINT_DATA_TYPE_FLOAT &&
            header->dataType != CM_PRINT_DATA_TYPE_DOUBLE &&
            header->dataType != CM_PRINT_DATA_TYPE_QWORD &&
            header->dataType != CM_PRINT_DATA_TYPE_UQWORD)
        {
            // Received an int type argument as expected
            switch (header->dataType)
            {
                case CM_PRINT_DATA_TYPE_INT:
                    {
                        int* scalar_int = reinterpret_cast<int*>(&header->scalar64);
                        m_args[m_numMultArg - m_argsExpected] = *scalar_int;
                    }
                    break;
                case CM_PRINT_DATA_TYPE_UINT:
                    {
                        unsigned int* scalar_uint = reinterpret_cast<unsigned int*>(&header->scalar64);
                        m_args[m_numMultArg - m_argsExpected] = *scalar_uint;
                    }
                    break;
                case CM_PRINT_DATA_TYPE_CHAR:
                    m_args[m_numMultArg - m_argsExpected] = *((char*           )&(header->scalar64));
                    break;
                case CM_PRINT_DATA_TYPE_UCHAR:
                    m_args[m_numMultArg - m_argsExpected] = *((unsigned char*  )&(header->scalar64));
                    break;
                case CM_PRINT_DATA_TYPE_SHORT:
                    {
                        short* scalar_short = reinterpret_cast<short*>(&header->scalar64);
                        m_args[m_numMultArg - m_argsExpected] = *scalar_short;
                    }
                    break;
                case CM_PRINT_DATA_TYPE_USHORT:
                    {
                        unsigned short* scalar_ushort = reinterpret_cast<unsigned short*>(&header->scalar64);
                        m_args[m_numMultArg - m_argsExpected] = *scalar_ushort;
                    }
                    break;
            }
            m_argsExpected -= 1;
            return true;
        }
        else
        {
            // Not received the expected argument
            // Dump what is in the format string and attempt to recover as well as possible
            return false;
        }
    }

    // Inform the user that they've used an unsupported format string if that is the case
    // (e.g. %jd, %td etc.)
    if (m_unsupported)
    {
        CM_PRINTF(m_streamOut,"Unsupported (but valid C++11) printf format string : %s", tkn);
        reset();
        return true;
    }
    // Inform the user that they've got an error (illegal format string)
    if (m_error)
    {
        CM_PRINTF(m_streamOut, "Error in printf format string : %s", tkn);
        reset();
        return true;
    }

    // Output as appropriate
    if (!m_numMultArg)
    {
        switch (header->dataType)
        {
        case CM_PRINT_DATA_TYPE_INT:
            {
                int* scalar_int = reinterpret_cast<int*>(&header->scalar64);
                CM_PRINTF(m_streamOut, tkn, *scalar_int);
            }
            break;

        case CM_PRINT_DATA_TYPE_UINT:
            {
                unsigned int* scalar_uint = reinterpret_cast<unsigned int*>(&header->scalar64);
                CM_PRINTF(m_streamOut, tkn, *scalar_uint);
            }
            break;

        case CM_PRINT_DATA_TYPE_CHAR:
            CM_PRINTF(m_streamOut, tkn, *((char*)&(header->scalar64)));
            break;

        case CM_PRINT_DATA_TYPE_UCHAR:
            CM_PRINTF(m_streamOut, tkn, *((unsigned char*)&(header->scalar64)));
            break;

        case CM_PRINT_DATA_TYPE_FLOAT:
            {
                float* scalar_float = reinterpret_cast<float*>(&header->scalar64);
                CM_PRINTF(m_streamOut, tkn, *scalar_float);
            }
            break;

        case CM_PRINT_DATA_TYPE_SHORT:
            {
                short* scalar_short = reinterpret_cast<short*>(&header->scalar64);
                CM_PRINTF(m_streamOut, tkn, *scalar_short);
            }
            break;

        case CM_PRINT_DATA_TYPE_USHORT:
            {
                unsigned short* scalar_ushort = reinterpret_cast<unsigned short*>(&header->scalar64);
                CM_PRINTF(m_streamOut, tkn, *scalar_ushort);
            }
            break;

        case CM_PRINT_DATA_TYPE_DOUBLE:
            {
                double* scalar_double = reinterpret_cast<double*>(&header->scalar64);
                CM_PRINTF(m_streamOut, tkn, *scalar_double);
            }
            break;

        case CM_PRINT_DATA_TYPE_QWORD:
            CM_PRINTF(m_streamOut, tkn, *((long long*)&(header->scalar64)));
            break;

        case CM_PRINT_DATA_TYPE_UQWORD:
            CM_PRINTF(m_streamOut, tkn, *((unsigned long long*)&(header->scalar64)));
            break;
        }
    }
    else if (m_numMultArg == 1)
    {
        switch (header->dataType)
        {
        case CM_PRINT_DATA_TYPE_INT:
            {
                int* scalar_int = reinterpret_cast<int*>(&header->scalar64);
                CM_PRINTF(m_streamOut,tkn, m_args[0], *scalar_int);
            }
            break;

        case CM_PRINT_DATA_TYPE_UINT:
            {
                unsigned int* scalar_uint = reinterpret_cast<unsigned int*>(&header->scalar64);
                CM_PRINTF(m_streamOut,tkn, m_args[0], *scalar_uint);
            }
            break;

        case CM_PRINT_DATA_TYPE_CHAR:
            CM_PRINTF(m_streamOut,tkn, m_args[0], *((char*           )&(header->scalar64)));
            break;

        case CM_PRINT_DATA_TYPE_UCHAR:
            CM_PRINTF(m_streamOut,tkn, m_args[0], *((unsigned char*  )&(header->scalar64)));
            break;

        case CM_PRINT_DATA_TYPE_FLOAT:
            {
                float* scalar_float = reinterpret_cast<float*>(&header->scalar64);
                CM_PRINTF(m_streamOut,tkn, m_args[0], *scalar_float);
            }
            break;

        case CM_PRINT_DATA_TYPE_SHORT:
            {
                short* scalar_short = reinterpret_cast<short*>(&header->scalar64);
                CM_PRINTF(m_streamOut,tkn, m_args[0], *scalar_short);
            }
            break;

        case CM_PRINT_DATA_TYPE_USHORT:
            {
                unsigned short* scalar_ushort = reinterpret_cast<unsigned short*>(&header->scalar64);
                CM_PRINTF(m_streamOut,tkn, m_args[0], *scalar_ushort);
            }
            break;

        case CM_PRINT_DATA_TYPE_DOUBLE:
            {
                double* scalar_double = reinterpret_cast<double*>(&header->scalar64);
                CM_PRINTF(m_streamOut,tkn, m_args[0], *scalar_double);
            }
            break;

        case CM_PRINT_DATA_TYPE_QWORD:
            CM_PRINTF(m_streamOut,tkn, m_args[0], *((long long*          )&(header->scalar64)));
            break;

        case CM_PRINT_DATA_TYPE_UQWORD:
            CM_PRINTF(m_streamOut,tkn, m_args[0], *((unsigned long long* )&(header->scalar64)));
            break;
        }
    }
    else if (m_numMultArg == 2)
    {
        switch (header->dataType)
        {
        case CM_PRINT_DATA_TYPE_INT:
            {
                int* scalar_int = reinterpret_cast<int*>(&header->scalar64);
                CM_PRINTF(m_streamOut,tkn, m_args[0], m_args[1], *scalar_int);
            }
            break;

        case CM_PRINT_DATA_TYPE_UINT:
            {
                unsigned int* scalar_uint = reinterpret_cast<unsigned int*>(&header->scalar64);
                CM_PRINTF(m_streamOut,tkn, m_args[0], m_args[1], *scalar_uint);
            }
            break;

        case CM_PRINT_DATA_TYPE_CHAR:
            CM_PRINTF(m_streamOut,tkn, m_args[0], m_args[1], *((char*           )&(header->scalar64)));
            break;

        case CM_PRINT_DATA_TYPE_UCHAR:
            CM_PRINTF(m_streamOut,tkn, m_args[0], m_args[1], *((unsigned char*  )&(header->scalar64)));
            break;

        case CM_PRINT_DATA_TYPE_FLOAT:
            {
                float* scalar_float = reinterpret_cast<float*>(&header->scalar64);
                CM_PRINTF(m_streamOut,tkn, m_args[0], m_args[1], *scalar_float);
            }
            break;

        case CM_PRINT_DATA_TYPE_SHORT:
            {
                short* scalar_short = reinterpret_cast<short*>(&header->scalar64);
                CM_PRINTF(m_streamOut,tkn, m_args[0], m_args[1], *scalar_short);
            }
            break;

        case CM_PRINT_DATA_TYPE_USHORT:
            {
                unsigned short* scalar_ushort = reinterpret_cast<unsigned short*>(&header->scalar64);
                CM_PRINTF(m_streamOut,tkn, m_args[0], m_args[1], *scalar_ushort);
            }
            break;

        case CM_PRINT_DATA_TYPE_DOUBLE:
            {
                double* scalar_double = reinterpret_cast<double*>(&header->scalar64);
                CM_PRINTF(m_streamOut,tkn, m_args[0], m_args[1], *scalar_double);
            }
            break;

        case CM_PRINT_DATA_TYPE_QWORD:
            CM_PRINTF(m_streamOut,tkn, m_args[0], m_args[1], *((long long*          )&(header->scalar64)));
            break;

        case CM_PRINT_DATA_TYPE_UQWORD:
            CM_PRINTF(m_streamOut,tkn, m_args[0], m_args[1], *((unsigned long long* )&(header->scalar64)));
            break;
        }
    }

    reset();
    return true;
}
void PFParser::DumpMemory(unsigned char * memory)
{
    PCM_PRINT_HEADER header = (PCM_PRINT_HEADER)memory;
    memory += PRINT_HEADER_SIZE;
    int threadid = header->tid;

    if(!m_numMultArg && header->objectType == CM_PRINT_OBJECT_TYPE_MATRIX )
    {
        CM_PRINTF(m_streamOut,"\n Thread id %d, Matrix , Width %d, Height %d \n", threadid, header->width, header->height);
    }
    else if(!m_numMultArg && header->objectType == CM_PRINT_OBJECT_TYPE_VECTOR)
    {
        CM_PRINTF(m_streamOut, " \n Thread id %d, Vector , Width %d\n", threadid, header->width);
    }
    else if(!m_numMultArg && header->objectType == CM_PRINT_OBJECT_TYPE_FORMAT)
    {
        // Flush any remaining characters from existing format string (if any)
        Flush();
        SetStart((char *)memory);
    }
    else if(!m_numMultArg && header->objectType == CM_PRINT_OBJECT_TYPE_STRING)
    {
        char tkn[PRINT_FORMAT_STRING_SIZE];
        PRINT_FMT_STATUS status = GetNextFmtToken(tkn, PRINT_FORMAT_STRING_SIZE);
        if (status == PF_SUCCESS)
        {
            // Inform the user that they've used an unsupported format string if that is the case
            // (e.g. %jd, %td etc.)
            if (m_unsupported)
            {
                CM_PRINTF(m_streamOut, "Unsupported (but valid C++11) format string used : %s", tkn);
            }
            // Inform the user that they've got an error (illegal format string)
            if (m_error)
            {
                CM_PRINTF(m_streamOut, "Error in printf format string : %s", tkn);
            }

            if (m_unsupported || m_error)
            {
                reset();
                return;
            }

            CM_PRINTF(m_streamOut, tkn, (char*)memory);
            reset();
        }

        return;
    }
    else if(header->objectType == CM_PRINT_OBJECT_TYPE_SCALAR)
    {
        char tkn[PRINT_FORMAT_STRING_SIZE];
        PRINT_FMT_STATUS status = GetNextFmtToken(tkn, PRINT_FORMAT_STRING_SIZE);
        if (status == PF_SUCCESS)
        {
            if (!outputToken(tkn, header))
            {
                // Something has gone wrong
                // Reset multi-arg at least
                CM_PRINTF(m_streamOut, "Problem outputting with format string %s\n", tkn);
                m_numMultArg = m_argsExpected = 0;
            }
        }
        return;
    }
    else
    {
        if (m_numMultArg)
        {
            // Something has gone wrong in multi-arg so reset
            CM_PRINTF(m_streamOut, "Error in multi-arg directive\n");
            m_numMultArg = 0;
            m_argsExpected = 0;
        }
        else
        {
            CM_PRINTF(m_streamOut, "Unknown TYPE\n");
        }
        return;
    }
}

void DumpAllThreadOutput(FILE *streamOut, unsigned char * dumpMem, size_t buffersize)
{

    unsigned int offsetFromHeader =   0;
    unsigned int off              =   PRINT_BUFFER_HEADER_SIZE;
    PFParser     pState(streamOut);

    while(1)
    {
        if((off + PRINT_HEADER_SIZE) >= buffersize)
            break;

        if(off >= (*(unsigned int *)dumpMem))
            break;

        offsetFromHeader = CalcSizeFromHeader(dumpMem + off);
        if( (off + offsetFromHeader) >= buffersize )
            break;

        pState.DumpMemory(dumpMem + off);

        off += offsetFromHeader;
    }

    // Flush any remaining characters in the format buffer
    pState.Flush();

}
