/*
 Copyright (C) 2002 Andreas Thiede ( a.thiede@berlin.de )

 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <IO/Input/BtAudioInput.hpp>
#include <IO/Manager/IOManager.hpp>
#include <IO/Manager/IOBuffer.hpp>
#include <IO/osx/TVHardware.hpp>
#include <lib/bt848.h>

#define MAX_BUFFERS 10

static IOBuffer *  ringBuffer[MAX_BUFFERS];
static int         currentBuffer = 0;

static void initRingBuffer()
{
    static int inited = 0;
    
    if( ! inited ) {
    
        for( int i = 0; i < MAX_BUFFERS; i++ ) {

            char * buf = ( char *) malloc( 8192  );

            IOBuffer * buffer = IOBuffer::initWithConstPointer( buf, 8192, i );

            ringBuffer[i] = buffer;
        }
        
        inited = 1;
    }
}

static IOBuffer * nextBuffer()
{
    IOBuffer * b = ringBuffer[ currentBuffer++ ];
    
    if( currentBuffer >= MAX_BUFFERS ) {
        currentBuffer = 0;
    }
    return b;
}

static inline float adjust( char src )
{
    float dst = (float) src;

    if ( dst > 0)
        dst /= 127.0;
    else
        dst /= 128.0;

    return dst;
}

BtAudioInput::BtAudioInput( const string & aName, IODeviceID anID )
 : IOAudioInput( aName, anID ), mWorkThread( aName, true )
{
    init();
}

BtAudioInput::~BtAudioInput()
{
    finit();
}


void * btAudioThreadEntry( void * _this )
{
    ((BtAudioInput*) _this )->grab();
    
    return 0;
}

bool BtAudioInput::playThrough( IOAudioOutput * )
{
    return false;
}

bool BtAudioInput::isGrabbing()
{
    return mStarted;
}

void BtAudioInput::grab()
{        
    while( mWorkThread.serviceCancellation() != IOThread::ioStop ) {

        int rc;
        static char audio_data[1024];
    
        memset( audio_data, 0, 1024 );

        //puts("BtAudioInput::grab");
        
        rc = BT848_ReadAudio( (UInt32*) &audio_data[0],  1024  );

        //printf("BtAudioInput::grab rc(%d)\n", rc);
    
        if( rc == 1024 ) {

            IOBuffer * buffer = nextBuffer();
            
            if( buffer ) {
            
                char  * src = (char*) &audio_data[0];
                float * dst;
                int     j;

                dst = (float*) buffer->getData();
        
                for( j = 0; j <  1024; j++ ) {

                    dst[  2 * j + 0   ] = adjust(src[ j ]);
                    dst[  2 * j + 1   ] = adjust(src[ j ]);
                }

                push( buffer );
            }
        }
    }
}

bool BtAudioInput::start() 
{ 
    if( mInited && ! mStarted && isAttached() && TVHardware::start( IODescription::ioIn, getDeviceID(), this ) ) {

        mWorkThread.start( btAudioThreadEntry, this );
    
        mStarted = true;
    }
    return mStarted;
}

bool BtAudioInput::stop() 
{ 
    if( mStarted ) {

        mWorkThread.stop();
        
        TVHardware::stop( IODescription::ioIn, getDeviceID(), this );
         
        mStarted = false;
    }
    
    return mStarted;
}

size_t BtAudioInput::frameSize()
{
    return 8192;
}

bool BtAudioInput::setVolume( int   l, int r   )
{
    return false;
}

bool BtAudioInput::getVolume( int & l, int &r  )
{
    return false;
}

void BtAudioInput::init()
{
    initRingBuffer();
    
    mInited = true;
}

void BtAudioInput::finit()
{
}
