/**
 * @file SDT.cpp
 *
 */

#include "b25/aribstr.h"
#include "mpeg2/ts/SDT.h"

namespace MPEG2
{
namespace TS
{

static int getBit(unsigned char *byte, int *pbit, int gbit)
{
	int pbyte = *pbit / 8;
	unsigned char *fbyte = byte + pbyte;

	int cutbit = *pbit - (pbyte * 8);
	int lcutbit = 32 - (cutbit + gbit);

	unsigned char tbuf[4]; //
	unsigned int tnum;

	memcpy(tbuf, fbyte, sizeof(unsigned char) * 4);

	//
	tbuf[0] = tbuf[0] << cutbit;
	tbuf[0] = tbuf[0] >> cutbit;

	//
	tnum = tbuf[0] << 24 | tbuf[1] << 16 | tbuf[2] << 8 | tbuf[3];

	//
	tnum = tnum >> lcutbit;

	*pbit += gbit;

	return tnum;
  
}

static int parseSDThead(unsigned char *data, SDThead *h)
{
	int boff = 0;

	memset(h, 0, sizeof(SDThead));

	boff = 0;
	h->table_id = getBit(data, &boff, 8);
	h->section_syntax_indicator = getBit(data, &boff, 1);
	h->reserved_future_use1 = getBit(data, &boff, 1);
	h->reserved1 = getBit(data, &boff, 2);
	h->section_length = getBit(data, &boff, 12);
	h->transport_stream_id = getBit(data, &boff, 16);
	h->reserved2 = getBit(data, &boff, 2);
	h->version_number = getBit(data, &boff, 5);
	h->current_next_indicator = getBit(data, &boff, 1);
	h->section_number = getBit(data, &boff, 8);
	h->last_section_number = getBit(data, &boff, 8);
	h->original_network_id = getBit(data, &boff, 16);
	h->reserved_future_use2 = getBit(data, &boff, 8);

	return 11;
}

static int parseSDTbody(unsigned char *data, SDTbody *b)
{
	int boff = 0;

	memset(b, 0, sizeof(SDTbody));

	b->service_id = getBit(data, &boff, 16);
	b->reserved_future_use1 = getBit(data, &boff, 3);
	b->EIT_user_defined_flags = getBit(data, &boff, 3);
	b->EIT_schedule_flag = getBit(data, &boff, 1);
	b->EIT_present_following_flag = getBit(data, &boff, 1);
	b->running_status = getBit(data, &boff, 3);
	b->free_CA_mode = getBit(data, &boff, 1);
	b->descriptors_loop_length = getBit(data, &boff, 12);

	return 5;
}

static void getStr(char *tostr, unsigned char *byte, int *pbit, int len)
{
    char str[MAXSECLEN];
    int pbyte = *pbit / 8;
    unsigned char *fbyte = byte + pbyte;

    memset(str, 0, sizeof(char) * MAXSECLEN);
    memcpy(str, fbyte, len);

    *pbit += (len * 8);

    AribToString(tostr, str, len);
}

static int parseSVCdesc(unsigned char *data, SVCdesc *desc)
{
	int boff = 0;
  
	memset(desc, 0, sizeof(SVCdesc));

	desc->descriptor_tag = getBit(data, &boff, 8);
	desc->descriptor_length = getBit(data, &boff, 8);
	desc->service_type = getBit(data, &boff, 8);
	desc->service_provider_name_length = getBit(data, &boff, 8);
	getStr(desc->service_provider_name, data, &boff, desc->service_provider_name_length);
	desc->service_name_length = getBit(data, &boff, 8);
	getStr(desc->service_name, data, &boff, desc->service_name_length);

	return desc->descriptor_length + 2;
}

SDT::SDT()
{
//    printf("SDT::SDT()\n");
    _service_count = 0;
    _services = NULL;
}
SDT::SDT(SDT &sdt)
{
//    printf("SDT::SDT(SDT &)\n");
    _table_id                   = sdt._table_id;
    _section_syntax_indicator   = sdt._section_syntax_indicator;
    _section_length             = sdt._section_length;
    _transport_stream_id        = sdt._transport_stream_id;
    _version_number             = sdt._version_number;
    _current_next_indicator     = sdt._current_next_indicator;
    _section_number             = sdt._section_number;
    _last_section_number        = sdt._last_section_number;
    _original_network_id        = sdt._original_network_id;
    _service_count              = sdt._service_count;
    _services                   = (struct _service *)malloc(sizeof(struct _service) * _service_count);
    for (int i = 0; i < _service_count; ++i)
    {
        _services[i]._service_id                    = sdt._services[i]._service_id;
        _services[i]._EIT_user_defined_flags        = sdt._services[i]._EIT_user_defined_flags;
        _services[i]._EIT_schedule_flag             = sdt._services[i]._EIT_schedule_flag;
        _services[i]._EIT_present_following_flag    = sdt._services[i]._EIT_present_following_flag;
        _services[i]._running_status                = sdt._services[i]._running_status;
        _services[i]._free_CA_mode                  = sdt._services[i]._free_CA_mode;
        _services[i]._descriptors_loop_length       = sdt._services[i]._descriptors_loop_length;
        _services[i]._descriptor = NULL;
        if (sdt._services[i]._descriptor != NULL)
        {
            _services[i]._descriptor = (uint8_t *)malloc(_services[i]._descriptors_loop_length);
            memcpy(_services[i]._descriptor, _services[i]._descriptor, _services[i]._descriptors_loop_length);
        }
        _services[i]._desc = NULL;
        if (sdt._services[i]._desc != NULL)
        {
            _services[i]._desc = (SVCdesc *)malloc(sizeof(SVCdesc));
            memcpy(_services[i]._desc, sdt._services[i]._desc, sizeof(SVCdesc));
        }
    }
}

SDT::~SDT()
{
//    printf("SDT::~SDT()\n");
    reset();
}

bool SDT::decode_section()
{
//    printf("SDT::%s\n", __FUNCTION__);

	int len = 0;
	int loop_len = 0;
	SDThead  sdth;
	SDTbody  sdtb;
//	SVCdesc  desc;

    uint8_t *ptr = &_section[0];

	/* SDT */
	len = parseSDThead(ptr, &sdth);
    if ((sdth.table_id != 0x42) && (sdth.table_id != 0x46))
    {
        return false;
    }

    _table_id = sdth.table_id;

#if 0
    if (_services != NULL)
    {
        for (int i = 0; i < _service_count; ++i)
        {
            if (_services[i]._descriptor != NULL)
            {
                free(_services[i]._descriptor);
                _services[i]._descriptor = NULL;
            }
            if (_services[i]._desc != NULL)
            {
                free(_services[i]._desc);
                _services[i]._desc = NULL;
            }
        }
        free(_services);
        _services = NULL;
    }
    _service_count = 0;
#else
    reset();
#endif
	ptr += len;
	loop_len = sdth.section_length - (len - 3 + 4); // 3: common header, 4:CRC
	while (loop_len > 0)
    {
		len = parseSDTbody(ptr, &sdtb);
		ptr += len;
		loop_len -= len;

        if (_services == NULL)
        {
            _services = (struct _service *)malloc(sizeof(struct _service));
        }
        else
        {
            _services = (struct _service *)realloc(_services, sizeof(struct _service) * (_service_count + 1));
        }

        _services[_service_count]._service_id = sdtb.service_id;
        _services[_service_count]._descriptors_loop_length = 0;
        _services[_service_count]._descriptor = NULL;
        _services[_service_count]._desc = (SVCdesc *)malloc(sizeof(SVCdesc));

        parseSVCdesc(ptr, _services[_service_count]._desc);

        ++_service_count;

		ptr += sdtb.descriptors_loop_length;
		loop_len -= sdtb.descriptors_loop_length;
	}

    return true;
}

void SDT::reset()
{
    if (_services != NULL)
    {
        for (int i = 0; i < _service_count; ++i)
        {
            if (_services[i]._descriptor != NULL)
            {
                free(_services[i]._descriptor);
                _services[i]._descriptor = NULL;
            }
            if (_services[i]._desc != NULL)
            {
                free(_services[i]._desc);
                _services[i]._desc = NULL;
            }
        }
        free(_services);
        _services = NULL;
    }
    _service_count = 0;
}

} // TS
} // MPEG2
