/*
Copyright (C) 2006  Adam Charrett

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA

tdtprocessor.c

Process Time/Date and Time Offset Tables.

*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <assert.h>

#include <dvbpsi/dvbpsi.h>
#include <dvbpsi/descriptor.h>
#include <dvbpsi/dr.h>
#include <dvbpsi/demux.h>

#include "multiplexes.h"
#include "services.h"
#include "pids.h"
#include "dvb.h"
#include "ts.h"
#include "main.h"
#include "cache.h"
#include "logging.h"
#include "list.h"
#include "tdtprocessor.h"
#include "dvbpsi/tdttot.h"

/*******************************************************************************
* Defines                                                                      *
*******************************************************************************/
#define TDT_PID 0x14
#define TABLE_ID_TDT 0x40
#define TABLE_ID_TOT 0x41

/*******************************************************************************
* Typedefs                                                                     *
*******************************************************************************/

typedef struct TDTProcessor_t
{
    PIDFilterSimpleFilter_t simplefilter;
    dvbpsi_handle handle;
    bool payloadstartonly;
}
TDTProcessor_t;

/*******************************************************************************
* Prototypes                                                                   *
*******************************************************************************/

static void TDTProcessorMultiplexChanged(PIDFilter_t *pidfilter, void *arg, Multiplex_t *newmultiplex);
static TSPacket_t * TDTProcessorProcessPacket(PIDFilter_t *pidfilter, void *arg, TSPacket_t *packet);
static void TDTHandler(void* arg, dvbpsi_tdt_tot_t* newTDT);

/*******************************************************************************
* Global variables                                                             *
*******************************************************************************/

static List_t *NewTDTCallbacksList = NULL;

/*******************************************************************************
* Global functions                                                             *
*******************************************************************************/

int TDTProcessorInit(void)
{
    NewTDTCallbacksList = ListCreate(); 
    return NewTDTCallbacksList ? 0: -1;
}

void TDTProcessorDeInit(void)
{
    ListFree(NewTDTCallbacksList, NULL);
}

PIDFilter_t *TDTProcessorCreate(TSFilter_t *tsfilter)
{
    PIDFilter_t *result = NULL;
    TDTProcessor_t *state;
    ObjectRegisterType(TDTProcessor_t);
    state = ObjectCreateType(TDTProcessor_t);
    if (state)
    {
        state->simplefilter.pidcount = 1;
        state->simplefilter.pids[0] = 0x14;
        result = PIDFilterSetup(tsfilter,
                    PIDFilterSimpleFilter, &state->simplefilter,
                    TDTProcessorProcessPacket, state,
                    NULL,NULL);
        if (result == NULL)
        {
            ObjectRefDec(state);
        }
        result->name = "TDT/TOT";
        result->type = PSISIPIDFilterType;
        PIDFilterMultiplexChangeSet(result,TDTProcessorMultiplexChanged, state);
        if (tsfilter->adapter->hardwareRestricted)
        {
            DVBDemuxAllocateFilter(tsfilter->adapter, TDT_PID, TRUE);
        }             
    }

    return result;
}

void TDTProcessorDestroy(PIDFilter_t *filter)
{
    TDTProcessor_t *state = (TDTProcessor_t *)filter->ppArg;
    assert(filter->processPacket == TDTProcessorProcessPacket);
    if (filter->tsFilter->adapter->hardwareRestricted)
    {
        DVBDemuxReleaseFilter(filter->tsFilter->adapter, TDT_PID);
    }        
    PIDFilterFree(filter);

    if (state->handle)
    {
        dvbpsi_DetachTDTTOT(state->handle);
    }
    ObjectRefDec(state);
}

void TDTProcessorRegisterTDTCallback(PluginTDTProcessor_t callback)
{
    if (NewTDTCallbacksList)
    {
        ListAdd(NewTDTCallbacksList, callback);
    }
}

void TDTProcessorUnRegisterTDTCallback(PluginTDTProcessor_t callback)
{
    if (NewTDTCallbacksList)
    {
        ListRemove(NewTDTCallbacksList, callback);
    }
}

/*******************************************************************************
* Local Functions                                                              *
*******************************************************************************/

static void TDTProcessorMultiplexChanged(PIDFilter_t *pidfilter, void *arg, Multiplex_t *newmultiplex)
{
    TDTProcessor_t *state = (TDTProcessor_t *)arg;
    if (state->handle)
    {
        dvbpsi_DetachTDTTOT(state->handle);
    }
    if (newmultiplex)
    {
        state->handle = dvbpsi_AttachTDTTOT(TDTHandler, (void*)state);
        state->payloadstartonly = TRUE;
    }
    else
    {
        state->handle = NULL;
    }
}

static TSPacket_t * TDTProcessorProcessPacket(PIDFilter_t *pidfilter, void *arg, TSPacket_t *packet)
{
    TDTProcessor_t *state = (TDTProcessor_t *)arg;

    if (state->handle == NULL)
    {
        return 0;
    }

    if (state->payloadstartonly)
    {
        if (TSPACKET_ISPAYLOADUNITSTART(*packet))
        {
            state->handle->i_continuity_counter = (TSPACKET_GETCOUNT(*packet) - 1) & 0xf;
            dvbpsi_PushPacket(state->handle, (uint8_t*)packet);
            state->payloadstartonly = FALSE;
        }
    }
    else
    {
        dvbpsi_PushPacket(state->handle, (uint8_t*)packet);
    }

    return 0;
}

static void TDTHandler(void* arg, dvbpsi_tdt_tot_t* newTDT)
{
    ListIterator_t iterator;

    for (ListIterator_Init(iterator, NewTDTCallbacksList); ListIterator_MoreEntries(iterator); ListIterator_Next(iterator))
    {
        PluginTDTProcessor_t callback = ListIterator_Current(iterator);
        callback(newTDT);
    }
    ObjectRefDec(newTDT);
}


