/**
 * @file Demultiplexer.cpp
 *
 */

#include "mpeg2/ts/Demultiplexer.h"

namespace MPEG2
{
namespace TS
{

Demultiplexer::Demultiplexer()
{
    MPEG2_MUTEX_INIT;

    _flag = 0;

    _continuity_counter_sdt  = 0xFF;
    _continuity_counter_0012 = 0xFF;
    _continuity_counter_0026 = 0xFF;
    _continuity_counter_0027 = 0xFF;
}

Demultiplexer::~Demultiplexer()
{
    MPEG2_MUTEX_DESTROY;
}

void Demultiplexer::setListener(Listener *listener)
{
    MPEG2_LOCK;
    _listener = listener;
    MPEG2_UNLOCK;
}

void Demultiplexer::setFlag(uint32_t flag, bool onoff)
{
    MPEG2_LOCK;
    if (onoff)
    {
        _flag |= flag;
    }
    else
    {
        _flag &= ~flag;
    }
    MPEG2_UNLOCK;
}

void Demultiplexer::putSDT(Header *header, uint8_t *packet)
{
    if (_continuity_counter_sdt != 0xFF)
    {
        if (((_continuity_counter_sdt + 1) & 0x0F) != header->_continuity_counter)
        {
            printf("packet loss\n");
            _sdt.reset();
            ((Table *)&_sdt)->reset();
        }
        _continuity_counter_sdt = header->_continuity_counter;
    }
    if (_sdt.decode(header, packet))
    {
        if ((_listener) && ((_flag & FLG_SDT) == FLG_SDT))
        {
            _listener->detect(&_sdt);
        }
        _sdt.reset();
    }
}

void Demultiplexer::putEIT(Header *header, uint8_t *packet)
{
    switch (header->_pid)
    {
    case PID_EIT_0012:
        if (_continuity_counter_0012 != 0xFF)
        {
            if (((_continuity_counter_0012 + 1) & 0x0F) != header->_continuity_counter)
            {
                printf("packet loss\n");
                _eit_0012.reset();
                ((Table *)&_eit_0012)->reset();
            }
        }
        _continuity_counter_0012 = header->_continuity_counter;
        break;

    case PID_EIT_0026:
        if (_continuity_counter_0026 != 0xFF)
        {
            if (((_continuity_counter_0026 + 1) & 0x0F) != header->_continuity_counter)
            {
                printf("packet loss\n");
                _eit_0026.reset();
                ((Table *)&_eit_0026)->reset();
            }
        }
        _continuity_counter_0026 = header->_continuity_counter;
        break;

    case PID_EIT_0027:
        if (_continuity_counter_0027 != 0xFF)
        {
            if (((_continuity_counter_0027 + 1) & 0x0F) != header->_continuity_counter)
            {
                printf("packet loss\n");
                _eit_0027.reset();
                ((Table *)&_eit_0027)->reset();
            }
        }
        _continuity_counter_0027 = header->_continuity_counter;
        break;
    }

    switch (header->_pid)
    {
    case PID_EIT_0012:
        if (_eit_0012.decode(header, packet))
        {
            if ((_listener) && ((_flag & FLG_EIT) == FLG_EIT))
            {
                _listener->detect(&_eit_0012);
            }
            _eit_0012.reset();
        }
        break;
    case PID_EIT_0026:
        if (_eit_0026.decode(header, packet))
        {
            if ((_listener) && ((_flag & FLG_EIT) == FLG_EIT))
            {
                _listener->detect(&_eit_0026);
            }
            _eit_0026.reset();
        }
        break;
    case PID_EIT_0027:
        if (_eit_0027.decode(header, packet))
        {
            if ((_listener) && ((_flag & FLG_EIT) == FLG_EIT))
            {
                _listener->detect(&_eit_0027);
            }
            _eit_0027.reset();
        }
        break;
    }
}

void Demultiplexer::put(uint8_t *buffer, uint32_t size)
{
    uint32_t offset = 0;
    while (offset < size)
    {
        if (buffer[offset] != SYNC_BYTE)
        {
            break;
        }

        MPEG2_LOCK;

        Header header(&buffer[offset]);
        if (!header._transport_error)
        {
            switch (header._pid)
            {
            case PID_PAT:
                break;

            case PID_CAT:
                break;

            case PID_NIT:
                break;

            case PID_SDT_BAT:
                putSDT(&header, &buffer[offset]);
                break;

            case PID_EIT_0012:
            case PID_EIT_0026:
            case PID_EIT_0027:
                putEIT(&header, &buffer[offset]);
                break;

            case PID_RST:
                break;

            case PID_TDT:
                break;
            }
        }

        MPEG2_UNLOCK;

        offset += PACKET_SIZE;
    }
}


} // TS
} // MPEG2
