/***************************************************************************
            \file audiofilter_thread.cpp
            \brief Wrap an access class into its own thread so that it can be parallelized
              (c) 2006 Mean , fixounet@free.fr
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "ADM_cpp.h"
using std::string;
#include "ADM_default.h"
#include "ADM_edit.hxx"
#include "audiofilter_thread.h"
#include <math.h>

#define MAX_CHUNK_IN_QUEUE 10
#define CHUNK_SIZE (20*1024) // should be more than enough

static void *boomerang(void *x)
{
    ADM_audioAccess_thread *a=(ADM_audioAccess_thread *)x;
    a->run();
    return NULL;
}
static void emptyListOfPacket(ListOfQueuePacket &list)
{
    int nb=list.size();
    for(int i=0;i<nb;i++)
    {
        ADM_queuePacket *pkt=&(list[i]);
        pkt->data=NULL;
    }
    list.clear();
}
/**
    \fn ADM_audioAccess_thread
    \brief
*/
ADM_audioAccess_thread::ADM_audioAccess_thread(ADM_audioAccess *son) :ADM_threadQueue()
{
    this->son=son;
    ADM_info("Swallowing audio access into a thread\n");
    for(int i=0;i<MAX_CHUNK_IN_QUEUE;i++)
    {
            ADM_byteBuffer *buff=new ADM_byteBuffer(CHUNK_SIZE);
            ADM_queuePacket pkt;
            pkt.data=buff->at(0);
            freeList.append(pkt);
            ListOfByteBuffers.append(buff);

    }
    
}
/**
    \fn ~ADM_audioAccess_thread
    \brief
*/

ADM_audioAccess_thread::~ADM_audioAccess_thread()
{
    stopThread();
    // Empty the list...
    emptyListOfPacket(list);
    emptyListOfPacket(freeList);
    int n=ListOfByteBuffers.size();
    for(int i=0;i<n;i++)
    {
        ADM_byteBuffer *b=ListOfByteBuffers[i];
        ListOfByteBuffers[i]=NULL;
        delete b;
    }
    ListOfByteBuffers.clear();
    // Thread stopped, we can kill the son
    delete son;
}

/**
    \fn setPos
    \brief
*/

bool      ADM_audioAccess_thread::setPos(uint64_t pos) 
{
    return false;
}
/**
    \fn getPos
    \brief
*/

uint64_t  ADM_audioAccess_thread::getPos(void)
{
    return 0;
}
/**
    \fn    getExtraData
    \brief
*/
                                    
bool      ADM_audioAccess_thread::getExtraData(uint32_t *l, uint8_t **d)
{
    return son->getExtraData(l,d);
}
/**
    \fn    getPacket
    \brief
*/

bool    ADM_audioAccess_thread::getPacket(uint8_t *buffer, uint32_t *size, uint32_t maxSize,uint64_t *dts)
{
    if(false==started)
    {
        startThread();      
    }
    while(1)
    {
        mutex->lock();
        if(threadState==RunStateStopOrder)  
        {
            mutex->unlock();
            return false;
        }
        if(list.size())
        {
            //
            // Dequeue one item
            ADM_queuePacket pkt=list[0];
            list.popFront();
            mutex->unlock();
            ADM_assert(pkt.data);
            ADM_assert(pkt.dataLen<maxSize);
            ADM_assert(pkt.dataLen<CHUNK_SIZE);
            memcpy(buffer,pkt.data,pkt.dataLen);
            *dts=pkt.dts;
            //printf("popping Packet with DTS=%"PRId64", size=%d\n",*dts,(int)pkt->dataLen);
            *size=pkt.dataLen;
            mutex->lock();
            freeList.append(pkt);
            if(producerCond->iswaiting())
            {
                producerCond->wakeup();
            }
            mutex->unlock();
            return true;
        }
        // If no item, thread still alive ?
        if(threadState==RunStateStopped)
        {
            ADM_info("Audio thread stopped, no more data\n");
            mutex->unlock();
            return false;
        }
        consumerCond->wait();// Will unlock mutex
    }
    return false;
}
/**
    \fn runAction
    \brief entry point for thread
*/
bool ADM_audioAccess_thread::runAction(void)
{
    while(1)
    {
        mutex->lock();
        if(threadState==RunStateStopOrder)  
        {
            ADM_info("Audio Thread, received stop order\n");
            mutex->unlock();
            goto theEnd;
        }
        if(!freeList.size())
        {
            producerCond->wait();
            continue;
        }
        ADM_queuePacket pkt=(freeList[0]);
        ADM_assert(pkt.data);
        freeList.popFront();
        mutex->unlock();

        if(false==son->getPacket(pkt.data,&(pkt.dataLen),CHUNK_SIZE,&(pkt.dts)))
        {
            ADM_info("Audio Thread, no more data\n");
            freeList.append(pkt);
            goto theEnd;
        }
      
        mutex->lock();
        list.append(pkt);
        //printf("Pushing Packet with DTS=%"PRId64",size=%d\n",dts,(int)size);
        if (consumerCond->iswaiting())
            consumerCond->wakeup();
        mutex->unlock();
    }

theEnd:
    return true;
}
/**
    \fn ADM_threadifyAudioAccess
*/
ADM_audioAccess *ADM_threadifyAudioAccess(ADM_audioAccess *son)
{
    return new ADM_audioAccess_thread(son);
}

// EOF
