//------------------------------------------------------------------------------
// 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
 * Waveリーダ実装
 * @author Junpee
 */

#include "LampBasic.h"
#include "Sound/Reader/WaveReader.h"

namespace Lamp{

//------------------------------------------------------------------------------
// コンストラクタ
WaveReader::WaveReader(const FilePath& filePath) : SoundReader(),
	filePath_(filePath), handle_(NULL), dataOffset_(-1), cursor_(0),
	size_(0), sample_(0), channel_(0), bit_(0), initialized_(false){
}
//------------------------------------------------------------------------------
// デストラクタ
WaveReader::~WaveReader(){
	// ファイルを閉じる
	if(handle_ != NULL){
		mmioClose(handle_, 0);
		handle_ = NULL;
	}
}
//------------------------------------------------------------------------------
// 位置の設定
void WaveReader::setCursor(u_int cursor){
	Assert(cursor_ <= size_);
	if(mmioSeek(handle_, (dataOffset_ + cursor), SEEK_SET) == -1){
		ErrorOut("WaveReader::setCursor() 位置の設定に失敗しました。");
	}
	cursor_ = cursor;
}
//------------------------------------------------------------------------------
// ヘッダ読み込み
bool WaveReader::readHeader(){
	// ファイルを開く
	handle_ = mmioOpen((LPSTR)filePath_.getPath().getBytes(), NULL,
		(MMIO_ALLOCBUF | MMIO_READ));
	if(handle_ == NULL){ return false; }
	// RIFFチャンクへの進入
	if(mmioDescend(handle_, &riffChunk_, NULL, 0) != MMSYSERR_NOERROR){
		return false;
	}
	if(riffChunk_.ckid != FOURCC_RIFF){ return false; }
	if(riffChunk_.fccType != mmioFOURCC('W', 'A', 'V', 'E')){ return false; }

	// コメントチャンク
	// まともにWaveファイルのコメントを編集できるソフトが無い、、、
	// mmioFOURCC('L', 'I', 'S', 'T');
	// mmioFOURCC('I', 'C', 'M', 'T');

	// フォーマットチャンクを開く
	MMCKINFO formatChunk_;
	formatChunk_.ckid = mmioFOURCC('f', 'm', 't', ' ');
	if(mmioDescend(handle_, &formatChunk_, &riffChunk_, MMIO_FINDCHUNK ) !=
		MMSYSERR_NOERROR){ return false; }
	// PCMWaveFormat読み込み
	if(formatChunk_.cksize < sizeof(PCMWAVEFORMAT)){ return false; }
	PCMWAVEFORMAT pcmWaveFormat;
	if(mmioRead(handle_, (HPSTR)&pcmWaveFormat, sizeof(PCMWAVEFORMAT)) !=
		sizeof(PCMWAVEFORMAT)){ return false; }
	// フォーマット情報の読み込み
	WAVEFORMAT& waveFormat = pcmWaveFormat.wf;
	sample_ = waveFormat.nSamplesPerSec;
	Assert((sample_ >= DSBFREQUENCY_MIN) && (sample_ <= 100000));
	channel_ = waveFormat.nChannels;
	Assert((channel_ == 1) || (channel_ == 2));
	bit_ = pcmWaveFormat.wBitsPerSample;
	Assert((bit_ == 8) || (bit_ == 16));
	// フォーマットチャンクから離脱
	if(mmioAscend(handle_, &formatChunk_, 0) != 0){ return false; }
	// データチャンクへの進入
	if(mmioSeek(handle_, riffChunk_.dwDataOffset + sizeof(FOURCC),
		SEEK_SET) == -1){ return false; }
	dataChunk_.ckid = mmioFOURCC('d', 'a', 't', 'a');
	if(mmioDescend(handle_, &dataChunk_, &riffChunk_, MMIO_FINDCHUNK) != 0){
		return false;
	}
	// データ情報取得
	size_ = dataChunk_.cksize;
	dataOffset_ = mmioSeek(handle_, 0, SEEK_CUR);
	if(dataOffset_ == -1){ return false; }
	cursor_ = 0;
	initialized_ = true;
	return true;
}
//------------------------------------------------------------------------------
// 読み込み
int WaveReader::read(void* buffer, u_int size){
	// mmioAdvance()の利点が不明だったのでmmioRead()で実装
	char* readBuffer = (char*)buffer;
	int readSize = 0;
	while(true){
		int result = mmioRead(handle_,
			(readBuffer + readSize), (size - readSize));
		if(result == -1){ return -1; }
		readSize += result;
		cursor_ += result;
		if((size == readSize) || (result == 0)){ break; }
	}
	return readSize;
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
