/* ------------------------------------------------------------------
 * Copyright (C) 1998-2009 PacketVideo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */

#include "pvpvxparser.h"

// Use default DLL entry point for Symbian
#include "oscl_dll.h"
OSCL_DLL_ENTRY_POINT_DEFAULT()


///////////////////////////////////////////////////
OSCL_EXPORT_REF CPVXInfo::CPVXInfo(void)
{
    Reset();
}

////////////////////////////////////////////////////
OSCL_EXPORT_REF CPVXInfo::~CPVXInfo()
{
}

////////////////////////////////////////////////////
// Clear the PVX info
void CPVXInfo::Reset(void)
{
    iSecurity = false;
    iPVXVersion = 0;
    iStartTime = 0;
    iEndTime = -1;
    iFlags = 0;
    iDownload = false;
    iPurge = false;
    iRandomPos = false;
    iPlaybackControl = ENoPlayback;
    iUserPlayback = false;
    iEndOfClip = ENoAction;
    iLaunchURL = OSCL_wHeapStringA();
}


/////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF CPVXParser::CPVXParser(void)
{
    iLogger = PVLogger::GetLoggerObject("");
}

/////////////////////////////////////////////////////////////////////////////
// Parses the PVX file content contained in aBuffer (2.0 or Imperial) and puts
// the parse content in pPVXInfo
OSCL_EXPORT_REF CPVXParser::CPVXParserStatus CPVXParser::ParsePVX(OsclMemoryFragment &aPVX, OSCL_HeapString<OsclMemAllocator>& aClipURL, CPVXInfo& aPVXInfo)
{

    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "CPVXParser::ParsePVX() called"));

    uint32 aBufferLen = aPVX.len;
    uint8* aBuffer = (uint8*)aPVX.ptr;
    // Check if the PVX info structure is valid

    // Get the PVX file size
    uint32 FileLoc = 0;
    if (FileLoc + 2 >= aBufferLen)
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "CPVXParser::ParsePVX Error  1 FileLoc+2 >= aBufferLen"));
        return CPVXParser_InvalidSize;
    }

    FileLoc += 2;   // Skip the file size

    // Get video and audio IDs (2.0) OR PVX file version (Imperial) (2 bytes)
    if (FileLoc + 2 >= aBufferLen)
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "CPVXParser::ParsePVX Error 2 FileLoc+2 >= aBufferLen"));
        return CPVXParser_InvalidSize;
    }

    aPVXInfo.iPVXVersion = BufToShortInt(&aBuffer[FileLoc]);
    FileLoc += 2;

    // Get the subscriber size info (2 bytes)
    if (FileLoc + 2 >= aBufferLen)
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "CPVXParser::ParsePVX Error 3 FileLoc+2 >= aBufferLen"));
        return CPVXParser_InvalidSize;
    }

    int16 dSubSize = BufToShortInt(&aBuffer[FileLoc]);
    FileLoc += 2;

    // Get subscriber info if present
    if (dSubSize > 0)
    {
        // Get the user ID size (2 bytes)
        if (FileLoc + 2 >= aBufferLen)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                            (0, "CPVXParser::ParsePVX Error 4 FileLoc+2 >= aBufferLen"));
            return CPVXParser_InvalidSize;
        }
        int16 dUserIDSize = BufToShortInt(&aBuffer[FileLoc]);
        FileLoc += 2;

        // Get User ID if present (variable length specified by dUserIDSize)
        if (dUserIDSize > 0)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                            (0, "CPVXParser::ParsePVX() called FileLoc=%d,dUserIDSize=%d, \
\t\t\taBufferLen=%d,MAX_PVXUSERID_LEN=%d", FileLoc, dUserIDSize, aBufferLen, MAX_PVXUSERID_LEN));
            if (FileLoc + dUserIDSize >= aBufferLen || dUserIDSize > MAX_PVXUSERID_LEN - 1)
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "CPVXParser:: \
\t\t\t\tParsePVX Error 5 FileLoc+2 >= aBufferLen"));
                return CPVXParser_InvalidSize;
            }
            aPVXInfo.iUserID.write(0, dUserIDSize, (char *)&aBuffer[FileLoc]);
            FileLoc += dUserIDSize;
        }

        // Get the application specific string size (2 bytes)
        if (FileLoc + 2 >= aBufferLen)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                            (0, "CPVXParser::ParsePVX Error 6 FileLoc+2 >= aBufferLen"));
            return CPVXParser_InvalidSize;
        }

        int16 dAppSpecificSize = BufToShortInt(&aBuffer[FileLoc]);
        FileLoc += 2;

        // Get application specific string if present (variable length specified by dAppSpecificSize)
        if (dAppSpecificSize > 0)
        {
            if (FileLoc + dAppSpecificSize >= aBufferLen || dAppSpecificSize > MAX_PVXAPPSPECIFICSTRING_LEN - 1)
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                                (0, "CPVXParser::ParsePVX Error 7 FileLoc+2 >= aBufferLen"));
                return CPVXParser_InvalidSize;
            }

            aPVXInfo.iAppSpecificString.write(0, dAppSpecificSize, (char *)&aBuffer[FileLoc]);
            FileLoc += dAppSpecificSize;
        }

        // Get the security flag (1 byte)
        if (FileLoc + 1 >= aBufferLen)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                            (0, "CPVXParser::ParsePVX Error FileLoc+1 >= aBufferLen"));
            return CPVXParser_InvalidSize;
        }

        if (aBuffer[FileLoc] > 0)
        {
            aPVXInfo.iSecurity = true;
        }
        else
        {
            aPVXInfo.iSecurity = false;
        }
        FileLoc += 1;

        // Get authentication session ID (36 bytes)
        if (FileLoc + 36 >= aBufferLen)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                            (0, "CPVXParser::ParsePVX Error 8 FileLoc+2 >= aBufferLen"));
            return CPVXParser_InvalidSize;
        }

        aPVXInfo.iSessionID.write(0, MAX_PVXSESSIONID_LEN, (char *)&aBuffer[FileLoc]);
        FileLoc += 36;

        // Get expiry timestamp (20 bytes)
        if (FileLoc + 20 >= aBufferLen)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                            (0, "CPVXParser::ParsePVX Error 9 FileLoc+2 >= aBufferLen"));
            return CPVXParser_InvalidSize;
        }
        aPVXInfo.iExpiryTimeStamp.write(0, MAX_PVXEXPIRYTIMESTAMP_LEN, (char *)&aBuffer[FileLoc]);
        FileLoc += 20;

        // Get random filler field (16 bytes)
        if (FileLoc + 16 >= aBufferLen)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                            (0, "CPVXParser::ParsePVX Error 10 FileLoc+2 >= aBufferLen"));
            return CPVXParser_InvalidSize;
        }
        aPVXInfo.iRandomFiller.write(0, MAX_PVXRANDOMFILLER_LEN, (char *)&aBuffer[FileLoc]);
        FileLoc += 16;

        // Get digital signature (40 bytes)
        if (FileLoc + 40 >= aBufferLen)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                            (0, "CPVXParser::ParsePVX Error 11 FileLoc+2 >= aBufferLen"));
            return CPVXParser_InvalidSize;
        }
        aPVXInfo.iDigitalSignature.write(0, MAX_PVXDIGITALSIGNATURE_LEN, (char *)&aBuffer[FileLoc]);
        FileLoc += 40;
    }

    //  For proper implementation, URL size should be an offset from subscriber auth size

    //  But following PVPlayer 2.0 PC/CE implementation

    // Get the URL size (2 bytes)
    if (FileLoc + 2 >= aBufferLen)
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                        (0, "CPVXParser::ParsePVX Error 12 FileLoc+2 >= aBufferLen"));
        return CPVXParser_InvalidSize;
    }

    int16 dURLSize = BufToShortInt(&aBuffer[FileLoc]);
    FileLoc += 2;

    // Get the URL (variable length specified by dURLSize)
    if (dURLSize > 0)
    {
        if (FileLoc + dURLSize > aBufferLen)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                            (0, "CPVXParser::ParsePVX Error 13 FileLoc+2 >= aBufferLen"));
            return CPVXParser_InvalidSize;
        }

        // Only copy dURLSize of bytes
        aClipURL.set((char*)&aBuffer[FileLoc], dURLSize);
    }
    else
    {
        // URL is not present. Bad PVX file
        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "CPVXParser::ParsePVX Error NO URL...BAD PVX FILE"));
        return CPVXParser_URLCorrupted;
    }
    FileLoc += dURLSize;

    // If iPVXVersion is 768(0x0300), Imperial format
    if (aPVXInfo.iPVXVersion == 0x0300)
    {
        // Get playback start time (4 bytes)
        if (FileLoc + 4 >= aBufferLen)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "CPVXParser::ParsePVX Error 15 FileLoc+2 >= aBufferLen"));
            return CPVXParser_InvalidSize;
        }
        aPVXInfo.iStartTime = BufToLongInt(&aBuffer[FileLoc]);
        FileLoc += 4;

        // Get playback end time (4 bytes)
        if (FileLoc + 4 >= aBufferLen)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "CPVXParser::ParsePVX Error 16 FileLoc+2 >= aBufferLen"));
            return CPVXParser_InvalidSize;
        }
        aPVXInfo.iEndTime = BufToLongInt(&aBuffer[FileLoc]);
        FileLoc += 4;

        // Get flags (4 bytes)
        if (FileLoc + 4 >= aBufferLen)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "CPVXParser::ParsePVX Error 17 FileLoc+2 >= aBufferLen"));
            return CPVXParser_InvalidSize;
        }
        aPVXInfo.iFlags = BufToLongInt(&aBuffer[FileLoc]);
        FileLoc += 4;

        // Check the flags
        aPVXInfo.iRandomPos = (aPVXInfo.iFlags & 0x01 ? false : true);
        aPVXInfo.iDownload = (aPVXInfo.iFlags & 0x02 ? true : false);
        aPVXInfo.iPurge = (aPVXInfo.iFlags & 0x04 ? true : false);
        aPVXInfo.iUserPlayback = (aPVXInfo.iFlags & 0x20 ? false : true);
        switch (aPVXInfo.iFlags & 0x18)
        {
            case 0x08:  // Automatic after download complete
                aPVXInfo.iPlaybackControl = CPVXInfo::EAfterDownload;
                break;
            case 0x10:  // Play as soon as possible
                aPVXInfo.iPlaybackControl = CPVXInfo::EAsap;
                break;
            case 0x18:  // Reserved
                aPVXInfo.iPlaybackControl = CPVXInfo::EReserve;
                break;
            case 0x00:  // Not automatic
            default:
                aPVXInfo.iPlaybackControl = CPVXInfo::ENoPlayback;
                break;
        }

        // Get end of clip behavior (2 bytes)
        if (FileLoc + 2 >= aBufferLen)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "CPVXParser::ParsePVX Error 18 FileLoc+2 >= aBufferLen"));
            return CPVXParser_InvalidSize;
        }
        switch (BufToShortInt(&aBuffer[FileLoc]))
        {
            case 1:
                aPVXInfo.iEndOfClip = CPVXInfo::ECloseApp;
                break;

            case 2:
                aPVXInfo.iEndOfClip = CPVXInfo::ENextUrl;
                break;

            case 0:
            default:
                aPVXInfo.iEndOfClip = CPVXInfo::ENoAction;
                break;
        }
        FileLoc += 2;

        // Get launch URL size (2 bytes)
        if (FileLoc + 2 > aBufferLen)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "CPVXParser::ParsePVX Error 19 FileLoc+2 >= aBufferLen"));
            return CPVXParser_InvalidSize;
        }
        int16 dLaunchURLSize = BufToShortInt(&aBuffer[FileLoc]);
        FileLoc += 2;

        // Get launch URL (variable length specified by dLaunchURLSize)
        if (dLaunchURLSize > 0)
        {
            if (FileLoc + dLaunchURLSize > aBufferLen)
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, (0, "CPVXParser::ParsePVX Error 20 FileLoc+2 >= aBufferLen"));
                return CPVXParser_InvalidSize;
            }
            OSCL_HeapStringA launchurl8((const char *)&aBuffer[FileLoc], dLaunchURLSize);

            OSCL_wString::chartype* launchurl16 = OSCL_ARRAY_NEW(OSCL_wString::chartype, dLaunchURLSize + 1);
            if (launchurl16 == NULL)
            {
                // Memory allocation failure
                return CPVXParser_InvalidSize;
            }
            oscl_UTF8ToUnicode((const OSCL_String::chartype*)launchurl8.get_cstr(), launchurl8.get_size(), \
                               launchurl16, dLaunchURLSize + 1);

            OSCL_wHeapStringA tmp16(launchurl16, dLaunchURLSize);

            aPVXInfo.iLaunchURL = tmp16;
            OSCL_ARRAY_DELETE(launchurl16);
        }
        FileLoc += dLaunchURLSize;
    }

    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "CPVXParser::ParsePVX() End"));
    return CPVXParser_Success;
}


///////////////////////////////////////////////////////////////////////////////
// Converts little endian data to short int
int16 CPVXParser::BufToShortInt(uint8* data)
{

    // Little endian
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "CPVXParser::BufToShortInt() called"));
    return (int16)(((((int16)data[1]) << 8)&0xFF00) | (((int16)data[0])&0x00FF));
}


///////////////////////////////////////////////////////////////////////////////
// Converts little endian data to long int
int32 CPVXParser::BufToLongInt(uint8* data)
{
    // Little endian
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "CPVXParser::BufToLongInt() called"));
    return (int32)(((((int32)data[3]) << 24)&0xFF000000) | ((((int32)data[2]) << 16)&0x00FF0000) |
                   ((((int32)data[1]) << 8)&0x0000FF00) | (((int32)data[0])&0x000000FF));
}
////////////////////////////////////////////////////////////////////////////////





