/*  libasf - An Advanced Systems Format media file parser
*  Copyright (C) 2006-2010 Juho Vähä-Herttua
*
*  This library is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public
*  License as published by the Free Software Foundation; either
*  version 2.1 of the License, or (at your option) any later version.
*   *  This library is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*  Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA   */

#include <stdlib.h>
#undef LOG_TAG
#define LOG_TAG "ASF data"

#ifndef ASFPARSER_H_INCLUDED
#include "asfparser.h"
#endif

#define ASF_STREAM_NUMBER_MASK (0x7f)
#define ASF_KEY_FRAME_MASK (0x80)

#define ASF_MIN_REPLICATED_LENGTH (8)
#define ASF_PTS_OFFSET (4)
#define ASF_MULTIPLE_PAYLOADS_DATALEN (2)

#define GETLEN2b(bits) (((bits) == 0x03) ? 4 : bits)

#define GETVALUE2b(bits, data) \
        (((bits) != 0x03) ? ((bits) != 0x02) ? ((bits) != 0x01) ? \
         0 : *(data) : ASFByteIO::asf_byteio_getWLE(data) : ASFByteIO::asf_byteio_getDWLE(data))

int ASFParser::asf_data_read_packet_data(asf_packet_t *packet, uint8_t flags,
                          uint8_t *data, uint32_t len)
{
        uint8_t datalen;

        if (!packet || !data)
                return 0;

    // Morris Yang : 5.2.2  Page.47
        datalen = GETLEN2b((flags >> 1) & 0x03) +
                  GETLEN2b((flags >> 3) & 0x03) +
                  GETLEN2b((flags >> 5) & 0x03) + 6;

        if (datalen > len) {
                return ASF_ERROR_INVALID_LENGTH;
        }

        packet->length = GETVALUE2b((flags >> 5) & 0x03, data);
        data += GETLEN2b((flags >> 5) & 0x03);
        /* sequence value should be never used anywhere */
        GETVALUE2b((flags >> 1) & 0x03, data);
        data += GETLEN2b((flags >> 1) & 0x03);
        packet->padding_length = GETVALUE2b((flags >> 3) & 0x03, data);
        data += GETLEN2b((flags >> 3) & 0x03);
        packet->send_time = ASFByteIO::asf_byteio_getDWLE(data);
        data += 4;
        packet->duration = ASFByteIO::asf_byteio_getWLE(data);
        data += 2;

        return datalen;
}

int ASFParser::asf_data_read_payload_data(asf_payload_t *payload,
        uint8_t flags,
        uint8_t *data,
        int size)
{
        uint8_t datalen;

        if (!payload || !data)
                return 0;

        datalen = GETLEN2b(flags & 0x03) +
                  GETLEN2b((flags >> 2) & 0x03) +
                  GETLEN2b((flags >> 4) & 0x03);

        if (datalen > size) {
                return ASF_ERROR_INVALID_LENGTH;
        }

        payload->media_object_number = GETVALUE2b((flags >> 4) & 0x03, data);
        data += GETLEN2b((flags >> 4) & 0x03);
        payload->media_object_offset = GETVALUE2b((flags >> 2) & 0x03, data);//in compress payload, this field is pts
        data += GETLEN2b((flags >> 2) & 0x03);
        payload->replicated_length = GETVALUE2b(flags & 0x03, data);
        data += GETLEN2b(flags & 0x03);

        return datalen;
}

int ASFParser::asf_data_parse_payloads (asf_packet_t *packet, /* in */
    asf_payload_t* pl, /* out */
    uint64_t preroll,
    uint8_t multiple,
    uint8_t type,
    uint8_t flags,
    uint8_t *data, /* in */
    uint32_t datalen,
    uint8_t* pts_delta, /* out */
    int* pt_skip, /* in & out */
    int* compressed/* out */) {
    int ret = ASF_PARSE_SUCCESS;
    int skip = *pt_skip;

    do {
        // Morris Yang 5.2.3.3 Page 53
        pl->stream_number = data[skip] & ASF_STREAM_NUMBER_MASK;
        pl->key_frame = !!(data[skip] & ASF_KEY_FRAME_MASK);
        skip++;

        int tmp = asf_data_read_payload_data(pl, flags, data + skip, datalen - skip);
        if (tmp < 0) {
            ret = tmp;
            break;
        }

        skip += tmp;

        if (pl->replicated_length > 1) {//min is 8bytes
            if (pl->replicated_length < ASF_MIN_REPLICATED_LENGTH || pl->replicated_length + skip > datalen) {
                /* not enough data */
                ret = ASF_ERROR_INVALID_LENGTH;
                break;
            }
            pl->replicated_data = data + skip;
            skip += pl->replicated_length;
            //pres time bytes(4bytes)Presentation Time
            pl->pts = ASFByteIO::asf_byteio_getDWLE(pl->replicated_data + ASF_PTS_OFFSET);
        } else if (pl->replicated_length == 1) {//conpress payload data
            if (skip >= (int)datalen) {
                /* not enough data */
                ret = ASF_ERROR_INVALID_LENGTH;
                break;
            }

            /* in compressed payload object offset is actually pts */
            pl->pts = pl->media_object_offset;
            ALOGI("asf_data_read_payloads 2: pl.pts=%d\n", pl->pts);
            pl->media_object_offset = 0;

            pl->replicated_length = 0;
            pl->replicated_data = NULL;

            *pts_delta = data[skip];
            skip++;
            *compressed = 1;
        } else {//==0
            pl->pts = packet->send_time;
            pl->replicated_data = NULL;
        }

        /* substract preroll value from pts since it's counted in */
        pl->pts = (pl->pts < preroll) ? 0 : (pl->pts - preroll);

        if (multiple) {
            tmp = GETLEN2b(type);
            if (tmp != ASF_MULTIPLE_PAYLOADS_DATALEN) {
                /* in multiple payloads datalen should be a word */
                ret = ASF_ERROR_INVALID_VALUE;
                break;
            }
            if (skip + tmp > (int)datalen) {
                /* not enough data */
                ret = ASF_ERROR_INVALID_LENGTH;
                break;
            }

            pl->datalen = GETVALUE2b(type, data + skip);
            if (pl->datalen > datalen) {
                ALOGE("pl.datalen %u is invalid, datalen %d", pl->datalen, datalen);
                ret = ASF_ERROR_INVALID_LENGTH;
                break;
            }
            skip += tmp;
        } else {
            pl->datalen = datalen - skip;
        }
    }while (0);

    *pt_skip = skip;

    return ret;
}

int ASFParser::asf_data_read_payloads(asf_packet_t *packet,
    uint64_t preroll,
    uint8_t multiple,
    uint8_t type,
    uint8_t flags,
    uint8_t* data,
    uint32_t datalen) {
    asf_payload_t pl;
    int i = 0, tmp = 0, skip = 0;

    if (!packet || !data || packet->payload_count == 0) {
        return ASF_ERROR_INTERNAL;
    }

    while (i < packet->payload_count) {
        uint8_t pts_delta = 0;
        int compressed = 0;

        tmp = asf_data_parse_payloads(packet, &pl, preroll, multiple, type,
            flags, data, datalen, &pts_delta, &skip, &compressed);
        if (tmp < 0) {
            ALOGE("asf_data_parse_payloads error %d", tmp);
            return tmp;
        }

        if (compressed) {
            int payloads, start = skip, used = 0;

            /* count how many compressed payloads this payload includes */
            for (payloads=0; used < (int)pl.datalen; payloads++) {
                used += 1 + data[start + used];//1byte data len + real data len
            }

            if (used != (int)pl.datalen) {
                /* invalid compressed data size */
                return ASF_ERROR_INVALID_LENGTH;
            }

            /* add additional payloads excluding the already allocated one */
            packet->payload_count += payloads - 1;
            if (packet->payload_count > packet->payloads_size) {
                void *tempptr;

                tempptr = realloc(packet->payloads,
                                  packet->payload_count * sizeof(asf_payload_t));
                if (!tempptr) {
                    return ASF_ERROR_OUTOFMEM;
                }
                packet->payloads = (asf_payload_t *)tempptr;
                packet->payloads_size = packet->payload_count;
            }

            while (skip < start + used) {
                pl.datalen = data[skip];
                skip++;

                pl.data = data + skip;
                skip += pl.datalen;

                pl.pts += pts_delta;
                memcpy(&packet->payloads[i], &pl, sizeof(asf_payload_t));
                i++;
            }
        } else {
            pl.data = data + skip;
            memcpy(&packet->payloads[i], &pl, sizeof(asf_payload_t));

            /* update the skipped data amount and payload index */
            skip += pl.datalen;
            i++;
        }
    }

    return skip;
}

void ASFParser::asf_data_init_packet(asf_packet_t *packet)
{
        if (!packet)
                return;

        packet->ec_length = 0;
        packet->ec_data = NULL;

        packet->length = 0;
        packet->padding_length = 0;
        packet->send_time = 0;
        packet->duration = 0;

        packet->payload_count = 0;
        packet->payloads = NULL;
        packet->payloads_size = 0;

        packet->payload_data_len = 0;
        packet->payload_data = NULL;

        packet->data = NULL;
        packet->data_size = 0;
}

int ASFParser::asf_data_get_packet(asf_packet_t *packet)
{
        asf_iostream_t *iostream = NULL;
        uint32_t read = 0;
        uint8_t packet_len_flags, packet_property, payload_length_type;
        void *tmpptr;
        int tmp;

        if (!packet || !file)
                return 0;

        iostream = &file->iostream;
        if (file->packet_size == 0) {
                ALOGE("asf_data_get_packet:error 1\n");
                return ASF_ERROR_INVALID_LENGTH;
        }

        /* If the internal data is not allocated, allocate it */
        if (packet->data_size < file->packet_size) {
                tmpptr = realloc(packet->data, file->packet_size);
                if (!tmpptr) {
                        ALOGE("asf_data_get_packet:error 2\n");
                        return ASF_ERROR_OUTOFMEM;
                }
                packet->data = (uint8_t*)tmpptr;
                packet->data_size = file->packet_size;
        }

        tmp = ASFByteIO::asf_byteio_read(packet->data,
                              file->packet_size,
                              iostream);
        if (tmp < 0) {
                /* Error reading packet data */
                ALOGE("asf_data_get_packet:error 3\n");
                return tmp;
        }

        //read Error Correction Flags;
        tmp = packet->data[read++];

        // Morris Yang : 5.2.1  Page 45
        if (tmp & 0x80) {
                uint8_t opaque_data, ec_length_type;

                packet->ec_length = tmp & 0x0f;
                opaque_data = (tmp >> 4) & 0x01;
                ec_length_type = (tmp >> 5) & 0x03;

                if (ec_length_type != 0x00 ||
                    opaque_data != 0 ||
                    packet->ec_length != 0x02) {
                        /* incorrect error correction flags */
                        ALOGE("asf_data_get_packet:error 4\n");
                        //return ASF_ERROR_INVALID_VALUE;
                }

                if (read+packet->ec_length > file->packet_size) {
                    ALOGE("asf_data_get_packet:error 5\n");
                        return ASF_ERROR_INVALID_LENGTH;
                }
                packet->ec_data = &packet->data[read];
                read += packet->ec_length;
                packet_len_flags = packet->data[read++];
        } else {
                packet->ec_length = 0;
                /*if error flag not set, data start with Payload*/
                packet_len_flags = (uint8_t)tmp;
        }

        if (read+2 > file->packet_size) {
            ALOGE("asf_data_get_packet:error 6\n");
                return ASF_ERROR_INVALID_LENGTH;
        }

        //Payload parsing information
        packet_property = packet->data[read++];
        tmp = asf_data_read_packet_data(packet,
                                        packet_len_flags,
                                        &packet->data[read],//packet length
                                        file->packet_size - read);
        if (tmp < 0) {
            ALOGE("asf_data_get_packet:error 7\n");
                return tmp;
        }
        read += tmp;

        /* this is really idiotic(Very stupid ), packet length can (and often will) be
         * undefined and we just have to use the header packet size as the size
         * value */
        if (!((packet_len_flags >> 5) & 0x03)) {
                packet->length = file->packet_size;
        }

        if (packet->length == 0) {
            ALOGE("asf_data_get_packet: error packet->length = 0");
            return ASF_ERROR_INVALID_LENGTH;
        }

        /* this is also really idiotic, if packet length is smaller than packet
         * size, we need to manually add the additional bytes into padding length
         */
        if (packet->length < file->packet_size) {
                packet->padding_length += file->packet_size - packet->length;
                packet->length = file->packet_size;
        }

        if (packet->length != file->packet_size) {
                /* packet with invalid length value */
                ALOGE("asf_data_get_packet:error 8\n");
                return ASF_ERROR_INVALID_LENGTH;
        }

        /* check if we have multiple payloads */
        if (packet_len_flags & 0x01) {
                if (read+1 > packet->length) {
                        ALOGE("asf_data_get_packet:error 9\n");
                        return ASF_ERROR_INVALID_LENGTH;
                }
                tmp = packet->data[read++];

                packet->payload_count = tmp & 0x3f;    // Morris Yang 5.2.3.3 Page 53 (Number of Payloads bits 0-5)
                payload_length_type = (tmp >> 6) & 0x03;
        } else {
                packet->payload_count = 1;
                payload_length_type = 0x02; /* not used */
        }

        packet->payload_data_len = packet->length - read;

        if (packet->payload_count > packet->payloads_size) {
                tmpptr = realloc(packet->payloads,
                                 packet->payload_count * sizeof(asf_payload_t));
                if (!tmpptr) {
                        ALOGE("asf_data_get_packet:error 10\n");
                        return ASF_ERROR_OUTOFMEM;
                }
                packet->payloads = (asf_payload_t *)tmpptr;
                packet->payloads_size = packet->payload_count;
        }

        packet->payload_data = &packet->data[read];
        read += packet->payload_data_len;

        /* The return value will be consumed bytes, not including the padding */
        tmp = asf_data_read_payloads(packet, file->preroll, packet_len_flags & 0x01,
                                     payload_length_type, packet_property,
                                     packet->payload_data,
                                     packet->payload_data_len - packet->padding_length);
        if (tmp < 0) {
                ALOGE("asf_data_get_packet:error 11\n");
                packet->payload_count = 0;
                //return tmp;
        }

        /*
        ALOGE("\npacket read %d bytes, eclen: %d, length: %d, padding: %d, time %d, duration: %d, payloads: %d\n",
                     read, packet->ec_length, packet->length, packet->padding_length, packet->send_time,
                     packet->duration, packet->payload_count);
        */
        return read;
}

void ASFParser::asf_data_free_packet(asf_packet_t *packet)
{
        if (!packet)
                return;

        if (packet->payloads) free(packet->payloads);
        if (packet->data) free(packet->data);

        packet->ec_data = NULL;
        packet->payloads = NULL;
        packet->payload_data = NULL;
        packet->data = NULL;
}