//------------------------------------------------------------------------------
// Lamp : Open source game middleware
// Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/** @file
 * OggVorbisリーダ実装
 * @author Junpee
 */

#include "LampBasic.h"
#include "Sound/Reader/OggVorbisReader.h"
#include "vorbis/vorbisfile.h"

namespace Lamp{

//------------------------------------------------------------------------------
// OggVorbisリーダ実装クラス
//------------------------------------------------------------------------------
/**
 * OggVorbisリーダ実装クラス
 */
class OggVorbisReaderImpl{
public:
	/**
	 * コンストラクタ
	 * @param filePath ファイルパス
	 */
	OggVorbisReaderImpl(const FilePath& filePath) : filePath_(filePath),
		file_(NULL), cursor_(0), pcmScale_(0),
		size_(0), sample_(0), channel_(0), bit_(0),
		oggVorbisinitialized_(false), initialized_(false){
		::memset(&oggVorbis_, 0, sizeof(OggVorbis_File));
	}

	/**
	 * デストラクタ
	 */
	virtual ~OggVorbisReaderImpl(){
		int result;
		if(file_ != NULL){
			if(oggVorbisinitialized_){
				result = ov_clear(&oggVorbis_);
				Assert(result == 0);
			}else{
				result = fclose(file_);
				Assert(result == 0);
			}
		}
	}

	//--------------------------------------------------------------------------
	/**
	 * サイズの取得
	 * @return サイズ
	 */
	virtual u_int getSize() const{
		Assert(initialized_);
		return size_;
	}

	/**
	 * サンプル数の取得
	 * @return サンプル数
	 */
	virtual int getSample() const{
		Assert(initialized_);
		return sample_;
	}

	/**
	 * チャンネル数の取得
	 * @return チャンネル数
	 */
	virtual int getChannel() const{
		Assert(initialized_);
		return channel_;
	}

	/**
	 * ビット数の取得
	 * @return ビット数
	 */
	virtual int getBit() const{
		Assert(initialized_);
		return bit_;
	}

	/**
	 * コメントの取得
	 * @return コメント
	 */
	virtual const String& getComment(){
		Assert(initialized_);
		return comment_;
	}

	//--------------------------------------------------------------------------
	/**
	 * 位置の設定
	 * @param cursor 設定する位置
	 */
	virtual void setCursor(u_int cursor){
		Assert((cursor % pcmScale_) == 0);
		if(ov_pcm_seek(&oggVorbis_, cursor / pcmScale_) != 0){
			ErrorOut("OggVorbisReader::setCursor() 位置の設定に失敗しました。");
		}
		cursor_ = cursor;
	}

	/**
	 * 位置の取得
	 * @return 位置
	 */
	virtual u_int getCursor(){ return cursor_; }

	//--------------------------------------------------------------------------
	/**
	 * ヘッダ読み込み
	 * @return 成功すればtrue
	 */
	virtual bool readHeader(){
		if(!filePath_.existFile()){ return false; }
		file_ = fopen(filePath_.getPath().getBytes(), "rb");
		if(file_ == NULL){ return false; }
		// OggVorbisを開く
		::memset(&oggVorbis_, 0, sizeof(OggVorbis_File));
		int result = ov_open(file_, &oggVorbis_, NULL, 0);
		// 必要そうならエラーコードの識別入れる
		if(result < 0){ return false; }
		oggVorbisinitialized_ = true;
		// シーク可能か
		if(ov_seekable(&oggVorbis_) == 0){ return false; }
		// 情報の取得
		vorbis_info* info = ov_info(&oggVorbis_, -1);
		if(info == NULL){ return false; }
		sample_ = info->rate;
		Assert((sample_ >= DSBFREQUENCY_MIN) && (sample_ <= 100000));
		channel_ = info->channels;
		Assert((channel_ == 1) || (channel_ == 2));
		// 16bit整数フォーマットを指定
		bit_ = 16;
		Assert((bit_ == 8) || (bit_ == 16));
		pcmScale_ = 2 * channel_;
		// サイズ取得
		__int64 size = ov_pcm_total(&oggVorbis_, -1);
		if(size < 0){ return false; }
		size *= pcmScale_;
		if(size > Limit::uIntMax){ return false; }
		size_ = (u_int)size;
		// コメントの読み込み
		vorbis_comment* commnets = ov_comment(&oggVorbis_,-1);
		int commentCount = commnets->comments;
		for(int i = 0; i < commentCount; i++){
			String comment = commnets->user_comments[i];
			if(comment.startsWith("COMMENT=")){
				comment_ = comment.getSubstring(8);
			}
		}
		setCursor(0);
		initialized_ = true;
		return true;
	}

	//--------------------------------------------------------------------------
	/**
	 * 読み込み
	 * @param buffer 読み込みバッファ
	 * @param size 読み込みサイズ
	 * @return 読み込んだサイズ。終端なら0、失敗すれば-1
	 */
	virtual int read(void* buffer, u_int size){
		char* readBuffer = (char*)buffer;
		int readSize = 0;
		while(true){
			// リトルエンディアン、16bit整数フォーマット、符号付
			int result = ov_read(&oggVorbis_,
				(readBuffer + readSize), (size - readSize), 0, 2, 1, NULL);
			if(result < 0){ return -1; }
			readSize += result;
			cursor_ += result;
			if((size == readSize) || (result == 0)){ break; }
		}
		Assert(size == readSize);
		return readSize;
	}

private:
	//--------------------------------------------------------------------------
	// ファイルパス
	FilePath filePath_;
	// ファイル
	FILE* file_;
	// OggVorbis
	OggVorbis_File oggVorbis_;
	// コメント
	String comment_;
	// 読み込み位置
	u_int cursor_;
	// PCMスケール
	u_int pcmScale_;

	// サイズ
	u_int size_;
	// サンプル数
	int sample_;
	// チャンネル数
	int channel_;
	// ビット数
	int bit_;
	// OggVorbis初期化済みフラグ
	bool oggVorbisinitialized_;
	// 初期化済みフラグ
	bool initialized_;

};
//------------------------------------------------------------------------------
// OggVorbisReader
//------------------------------------------------------------------------------
// コンストラクタ
OggVorbisReader::OggVorbisReader(const FilePath& filePath){
	implement_ = new OggVorbisReaderImpl(filePath);
}
//------------------------------------------------------------------------------
// デストラクタ
OggVorbisReader::~OggVorbisReader(){
	SafeDelete(implement_);
}
//------------------------------------------------------------------------------
// サイズの取得
u_int OggVorbisReader::getSize() const{ return implement_->getSize(); }
//------------------------------------------------------------------------------
// サンプル数の取得
int OggVorbisReader::getSample() const{ return implement_->getSample(); }
//------------------------------------------------------------------------------
// チャンネル数の取得
int OggVorbisReader::getChannel() const{ return implement_->getChannel(); }
//------------------------------------------------------------------------------
// ビット数の取得
int OggVorbisReader::getBit() const{ return implement_->getBit(); }
//------------------------------------------------------------------------------
// コメントの取得
const String& OggVorbisReader::getComment(){ return implement_->getComment(); }
//------------------------------------------------------------------------------
// 位置の設定
void OggVorbisReader::setCursor(u_int cursor){
	implement_->setCursor(cursor);
}
//------------------------------------------------------------------------------
// 位置の取得
u_int OggVorbisReader::getCursor(){ return implement_->getCursor(); }
//------------------------------------------------------------------------------
// ヘッダ読み込み
bool OggVorbisReader::readHeader(){ return implement_->readHeader(); }
//------------------------------------------------------------------------------
// 読み込み
int OggVorbisReader::read(void* buffer, u_int size){
	return implement_->read(buffer, size);
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
