#include "Audio.h"
#include <FK/Error.h>

#include <iostream>
#include <vector>

#ifdef __BIG_ENDIAN__
const int _ENDIAN = 1;
#else
const int _ENDIAN = 0;
#endif

using namespace std;

fk_AudioStream::fk_AudioStream(void)
{
	ovOpenStatus = startStatus = endStatus = false;
	return;
}

fk_AudioStream::~fk_AudioStream()
{
	end();
}

bool fk_AudioStream::open(const std::string &argFileName)
{
	int		ovStatus;

	if(getInit() == false) init();

	if(ovOpenStatus == true) ov_clear(&vf);

	if((ovStatus = ov_fopen((char *)&argFileName[0], &vf)) < 0) {
		switch(ovStatus) {
		  case OV_EREAD:
			fk_PutError("fk_AudioStream", "open", 1, "Read Error");
			break;

		  case OV_ENOTVORBIS:
			fk_PutError("fk_AudioStream", "open", 1, "Not Vorbis Error");
			break;

		  case OV_EVERSION:
			fk_PutError("fk_AudioStream", "open", 1, "Version Error");
			break;

		  case OV_EBADHEADER:
			fk_PutError("fk_AudioStream", "open", 1, "Header Error");
			break;

		  case OV_EFAULT:
			fk_PutError("fk_AudioStream", "open", 1, "Fault Error");
			break;

		  default:
			fk_PutError("fk_AudioStream", "open", 1, "Undefined Error");
			break;
		}

		return false;
	}
	ovOpenStatus = true;
	startStatus = endStatus = false;
	return true;
}

bool fk_AudioStream::ready(void)
{
	if(ovOpenStatus == false) return false;
	if(startStatus == false) StartQueue(true);
	return true;
}

bool fk_AudioStream::play(void)
{
	if(ovOpenStatus == false) return false;
	if(startStatus == false) {
		if(ready() == false) return false;
	}

	if(PlayStream() == true) return true;
	else if(endStatus & loopMode) {
		seek(loopStartTime);
		return PlayStream();
	}

	return false;
}

void fk_AudioStream::StartQueue(bool argInitFlg)
{
	long			size;
	ALuint			bufferID;
	ALint			queueNum;

	startStatus = true;

	if(argInitFlg == true) {
		current = 0;
		queueNum = -1;
		CreateID();
	}

	UnQueue(true);

	for(unsigned int i = 0; i < queueSize; i++) {

		size = ov_read(&vf, &buffer[0],
					   FK_OV_BUFSIZE*sizeof(char),
					   _ENDIAN, 2, 1, &current);

		if(size <= 0) {
			endStatus = true;
			break;
		}

		if(i == 0) MakeOVInfo(&vf);

		alGenBuffers(1, &bufferID);
		alBufferData(bufferID, format, &buffer[0], size, rate);
		alSourceQueueBuffers(source, 1, &bufferID);
	}

	return;
}

void fk_AudioStream::UnQueue(bool argBufferFlg)
{
	ALint		queueNum;
	ALuint		bufferID;
	int			i;

	alGetSourcei(source, AL_BUFFERS_QUEUED, &queueNum);
	for(i = 0; i < queueNum; i++) {
		alSourceUnqueueBuffers(source, 1, &bufferID);
		if(argBufferFlg == true) {
			alDeleteBuffers(1, &bufferID);
		}
	}
	return;
}

bool fk_AudioStream::PlayStream(void)
{
	ALint			status, i, procNum, bufNum;
	ALuint			bufferID;
	long			size;

	bufferID = 0;
	procNum = 0;
	bufNum = 0;

	alGetSourcei(source, AL_SOURCE_STATE, &status);
	
	// 再生していなければ、再生命令を出す
	if(status != AL_PLAYING) {
		alSourcePlay(source);
		sleep(0.001);
	}

	// 再生済みバッファの個数を取得
	alGetSourcei(source, AL_BUFFERS_PROCESSED, &procNum);

	for(i = 0; i < procNum; i++) {
		alSourceUnqueueBuffers(source, 1, &bufferID);

		// 既にファイルを読み終えてる場合、バッファを消去
		if(endStatus == true) {
			alDeleteBuffers(1, &bufferID);
			continue;
		}

		// ファイル読み込み
		nowTime = ov_time_tell(&vf);
		size = ov_read(&vf, &buffer[0],
					   FK_OV_BUFSIZE*sizeof(char),
					   _ENDIAN, 2, 1, &current);

		if(size == 0) {
			// 終端
			endStatus = true;
			alDeleteBuffers(1, &bufferID);
			continue;
		}

		alBufferData(bufferID, format, &buffer[0], size, rate);
		alSourceQueueBuffers(source, 1, &bufferID);
	}

	// 終了判定
	if(status != AL_PLAYING && endStatus == true) return false;
	return true;
}

void fk_AudioStream::stop(void)
{
	ALint		status, procNum, i;
	ALuint		bufferID;

	if(startStatus == false) return;
	alGetSourcei(source, AL_SOURCE_STATE, &status);
	if(status != AL_STOPPED) {
		alSourceStop(source);
	}
	alGetSourcei(source, AL_BUFFERS_PROCESSED, &procNum);
	for(i = 0; i < procNum; i++) {
		alSourceUnqueueBuffers(source, 1, &bufferID);
		alDeleteBuffers(1, &bufferID);
	}
}

void fk_AudioStream::end(void)
{
	if(ovOpenStatus == false) return;

	stop();

	if(startStatus == true) alDeleteSources(1, &source);
	startStatus = endStatus = false;
	EraseID();
	ov_clear(&vf);
	ovOpenStatus = false;

	return;
}	

double fk_AudioStream::tell(void)
{
	ALfloat		bufTell;
	ALint		queueNum;

	if(startStatus == false) return -1.0;

	alGetSourcei(source, AL_BUFFERS_QUEUED, &queueNum);
	alGetSourcef(source, AL_SEC_OFFSET, &bufTell);
	return nowTime + (double)bufTell;
}

void fk_AudioStream::seek(double argTime)
{
	if(ovOpenStatus == false) return;

	stop();
	ov_time_seek_page_lap(&vf, argTime);
	endStatus = false;
	StartQueue(!startStatus);

	return;
}
