/**
 * @file PMT.cpp
 *
 */

#include "mpeg2/ts/PMT.h"

namespace MPEG2
{
namespace TS
{

PMT::PMT()
{
    _descriptor = NULL;
    _element_count = 0;
    _elements = NULL;
}

PMT::~PMT()
{
    if (_descriptor != NULL)
    {
        free(_descriptor);
    }
    if (_elements != NULL)
    {
        for (int i = 0; i < _element_count; ++i)
        {
            if (_elements[i]._descriptor != NULL)
            {
                free(_elements[i]._descriptor);
            }
        }
        free(_elements);
    }
}

bool PMT::decode_section()
{
    bool result = true;
    _table_id = _section[0x00];
    if (_table_id == TABLE_ID_PMT)
    {
        _section_syntax_indicator = (_section[0x01] & 0x80) >> 7;
        _section_length           = ((_section[0x01] << 8) + _section[0x02]) & 0x0FFF;
        _program_number           = (_section[0x03] << 8) + _section[0x04];
        _version_number           = (_section[0x05] & 0x3E) >> 1;
        _current_next_indicator   = _section[0x05] & 0x01;
        _section_number           = _section[0x06];
        _last_section_number      = _section[0x07];
        _PCR_PID                  = ((_section[0x08] << 8) + _section[0x09]) & 0x1FFF;
        _program_info_length      = ((_section[0x0a] << 8) + _section[0x0b]) & 0x0FFF;
        if (_descriptor != NULL)
        {
            free(_descriptor);
        }
        _descriptor = (uint8_t *)malloc(_program_info_length);
        memcpy(_descriptor, &_section[0x0c], _program_info_length);

        if (_elements != NULL)
        {
            for (int i = 0; i < _element_count; ++i)
            {
                if (_elements[i]._descriptor != NULL)
                {
                    free(_elements[i]._descriptor);
                }
            }
            free(_elements);
            _elements = NULL;
        }

        uint16_t offset = 0x0c + _program_info_length;

        _element_count = 0;
        while (offset < (_length - 4))
        {
            if (_elements == NULL)
            {
                _elements = (struct _element *)malloc(sizeof(struct _element));
            }
            else
            {
                _elements = (struct _element *)realloc(_elements, sizeof(struct _element) * (_element_count + 1));
            }
            _elements[_element_count]._stream_type    = _section[offset];
            _elements[_element_count]._elementary_PID = ((_section[offset + 1] << 8) + _section[offset + 2]) & 0x1FFF;
            _elements[_element_count]._ES_info_length = ((_section[offset + 3] << 8) + _section[offset + 4]) & 0x0FFF;
            _elements[_element_count]._descriptor     = (uint8_t *)malloc(_elements[_element_count]._ES_info_length);
            memcpy(_elements[_element_count]._descriptor, &_section[offset + 5], _elements[_element_count]._ES_info_length);
            offset += (5 + _elements[_element_count]._ES_info_length);
            _element_count++;
        }
    }
    else
    {
        reset();
        result = false;
    }

    return result;
}

void PMT::reset()
{
    _table_id                 = 0;
    _section_syntax_indicator = 0;
    _section_length           = 0;
    _program_number           = 0;
    _version_number           = 0;
    _current_next_indicator   = 0;
    _section_number           = 0;
    _last_section_number      = 0;
    _PCR_PID                  = 0;
    _program_info_length      = 0;
    if (_descriptor != NULL)
    {
        free(_descriptor);
        _descriptor = NULL;
    }
    if (_elements != NULL)
    {
        for (int i = 0; i < _element_count; ++i)
        {
            if (_elements[i]._descriptor != NULL)
            {
                free(_elements[i]._descriptor);
            }
        }
        free(_elements);
        _elements = NULL;
        _element_count = 0;
    }
}

} // TS
} // MPEG2
