/*********************************************************

	DSP OssAudioRenderer implementation

*********************************************************/

#include "OssAudioRenderer.h"
#include "AudioQueue.h"
#include "RegAccess.h"
#include <avifile.h>
//#include <cpuinfo.h>
#include <utils.h>

#ifdef __NetBSD__
#include <soundcard.h>
#else
#include <sys/soundcard.h>
#endif

#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

#include <iostream>

#define __MODULE__ "OssAudioRenderer"

#define Debug if(0)
using namespace std;

OssAudioRenderer::OssAudioRenderer(IAviReadStream* as, WAVEFORMATEX& Owf)
    :IAudioRenderer(as, Owf)
{
    try
    {
	m_iAudioFd = -1;
	try
	{
    	    m_iAudioFd = open("/dev/dsp", O_RDWR | O_NDELAY);
	    if (m_iAudioFd < 0)
  		throw FATAL("Can't open audio device");

	    int flag = fcntl(m_iAudioFd, F_GETFL, 0);
	    if (flag < 0)
		throw FATAL("fcntl");
	    flag &= ~O_NDELAY;
	    if (fcntl(m_iAudioFd, F_SETFL, flag) < 0)
		throw FATAL("fcntl");
	}
	catch(FatalError&)
	{
	    if (m_iAudioFd > 0)
		::close(m_iAudioFd);
	    m_iAudioFd = open("/dev/dsp", O_RDWR);
	}
        if (m_iAudioFd < 0)
     	    throw FATAL("Can't open audio device");

	ioctl(m_iAudioFd, SNDCTL_DSP_RESET, NULL);
	audio_buf_info zz;
	ioctl(m_iAudioFd, SNDCTL_DSP_GETOSPACE, &zz);
	m_iSndLimit = zz.bytes;
	cout << "m_iSndLimit " << m_iSndLimit << endl;

	int tmp = m_Owf.nChannels-1;
	if (ioctl(m_iAudioFd, SNDCTL_DSP_STEREO, &tmp) != 0)
	    throw FATAL("ioctl(stereo)");

	tmp = m_Owf.wBitsPerSample;
	if (ioctl(m_iAudioFd, SNDCTL_DSP_SAMPLESIZE, &tmp) < 0)
	    throw FATAL("ioctl(samplesize)");

	tmp = m_Owf.nSamplesPerSec;

	int supportedFreq[] =
	{
	    8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 0
	};

        int i;
	for (i = 0; supportedFreq[i]; i++)
	{
	    if (tmp <= supportedFreq[i])
	    {
		tmp = supportedFreq[i];
                break;
	    }
	}
	if (!supportedFreq[i])
           tmp = 44100; // default;

        if (tmp != m_Owf.nSamplesPerSec)
	    cout << "For playing using frequency " << tmp
		<< "Hz instead of unsupported "
		<< m_Owf.nSamplesPerSec << "Hz as you wish" << endl;

	if (ioctl(m_iAudioFd, SNDCTL_DSP_SPEED, &tmp) != 0)
	    throw FATAL("ioctl(speed)");

	cout << "Format OK" << endl;
    }
    catch (FatalError& error)
    {
	if (m_iAudioFd > 0)
	    ::close(m_iAudioFd);
	throw;
    }
    pthread_create(&m_AudioThread, NULL, doAudioOut, (void*)this);
}

OssAudioRenderer::~OssAudioRenderer()
{
    Debug cout << "Destroying Oss Audio Renderer" << endl;
    m_bQuit = true;
    m_pQueue->Broadcast();
    pthread_join(m_AudioThread, NULL);
    ::close(m_iAudioFd);
}

double OssAudioRenderer::GetBufferTime() const
{
    return IAudioRenderer::GetBufferTime();

    // unused
    if (!m_bInitialized)
	return 0;

    audio_buf_info zz;
    ioctl(m_iAudioFd, SNDCTL_DSP_GETOSPACE, &zz);

    double frame_time;

    frame_time= m_pQueue->GetSize() +
	(signed)(m_iSndLimit-zz.bytes) / (double) m_pQueue->GetBytesPerSec();

    cout << frame_time << endl;
    return frame_time;
}

void* OssAudioRenderer::doAudioOut(void* arg)
{
    OssAudioRenderer& a = *(OssAudioRenderer*)arg;

    while (!a.m_bQuit)
    {
	if (a.m_bPaused)
	{
	    avi_usleep(10000);
	    // cout<<"m_bPaused"<<endl;
	    continue;
	}

	static int reset_dev = 0;
	if (!a.m_bInitialized)
	{
	    if (reset_dev)
	    {
		a.m_pQueue->Clear();
		a.reset();
		reset_dev = 0;
	    }
	    avi_usleep(10000);
	    // cout<<"Not m_bInitialized"<<endl;
	    continue;
	} else
	    reset_dev=1;

	audio_buf_info zz;
	ioctl(a.m_iAudioFd, SNDCTL_DSP_GETOSPACE, &zz);
	if (zz.bytes == 0)
	{
	    avi_usleep(10000);
	    Debug cout<<"Buffer full"<<endl;
	    continue;
	}

	a.m_pQueue->Lock();
	if (a.m_pQueue->GetSize() < 8192)
	{
	    a.m_pQueue->Wait();
	    a.m_pQueue->Unlock();
	    continue;
	}
	a.m_pQueue->Unlock();

	//cout<<"Buffer fullness   "<< zz.bytes << endl;
/*	cout<<">"<<a.m_pAudiostream->GetTime()<<" "<<a.bufferTime()
		<<" "<<" -> "<<a.m_pAudiostream->GetTime()-a.bufferTime()
	        <<" "<<longcount()/550000000.<<endl;*/
	//float in=a.getTime();
	//int64_t ts=longcount();
//	cout<<"Before: "<<in<<endl;

	int result = a.m_pQueue->Write(a.m_iAudioFd);

	//cout << "Wrote " << result << " bytes" << endl;
	if (result == 0 || (result < 0 && errno == EAGAIN))
	    avi_usleep(10000);
	else if (result < 0)
	    perror("AudioQueue::write");
	else
	{
	    //Locker locker(a.m_Mutex);

	    int64_t ct = longcount();
	    a.m_lAudioTime = ct;
	    if (a.m_dPauseTime > 0)
	    {
		a.m_lTimeStart += ct - a.m_lAudioTime;
		//Debug cout << "Restart after Pause" << endl;
		//if (to_float(ct, a.m_lAudioTime) > a.m_dSpecTime)
		//{
		    //cout << "PAUSE fix  " << to_float(ct, a.m_lAudioTime) << endl;
		//    a.m_dAudioRealpos += a.m_dSpecTime;
	       // }
		a.m_dPauseTime = 0.0;
	    }
	    if (a.m_lTimeStart == 0)
	    {
		a.m_lTimeStart = ct;
		//a.m_dAudioRealpos -= a.m_dSpecTime;
	    }
	    //a.m_dAudioRealpos = a.m_pAudiostream->GetTime() - a.GetBufferTime();

		//	    cout<<a.m_pAudiostream->GetTime()<<" "<<frame_time
	    //		<<" "<<" -> "<<a.m_dAudioRealpos
	    //	        <<" "<<longcount()/550000000.<<endl;
	    //float out=a.getTime();
	    //int64_t tx=longcount();
	    //	    cout<<"Write mismatch: "<<1000.*(out-in)-to_float(tx,ts)
	    //	    <<" ms ( writing "<<to_float(tx,ts)<<" ms, stream difference "<<1000.*(out-in)<<" ms )"<<endl;
	}
    }

    Debug cout<<"Exiting audio thread"<<endl;
    return 0;
}

void OssAudioRenderer::reset()
{
    if (m_iAudioFd)
	ioctl(m_iAudioFd, SNDCTL_DSP_RESET, NULL);
}
