/* CSL - Common Sound Layer
 * Copyright (C) 2000-2001 Stefan Westerfeld and Tim Janik
 *
 * 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 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.
 */

#include "stream_impl.h"
#include "adapter_impl.h"
#include "artsflow.h"
#include "debug.h"
#include "convert.h"
#include "connect.h"

using namespace Arts;
using namespace CSL;
using namespace std;

/* Stream: */
Stream_impl::Stream_impl(Adapter_impl *adapter)
	:_ID(0),
	 _bufferUsed(0),
	 _bufferSize(0),		/* NOT USED */
	 _active(false),
	 adapter(adapter)
{
}

/* PlayStream: */
PlayStream_impl::PlayStream_impl(Adapter_impl *adapter,
								 const string& role, long ID)
	: Stream_impl(adapter), resampler(this), connected(false)
{
	_role = role;
	_ID = ID;
	_direction = dirOutput;
	play.autoRestoreID(_role);
}

void PlayStream_impl::title(const string& newTitle)
{
	if(newTitle != _title)
	{
		_title = newTitle;
		play.title(_title);
		title_changed(newTitle);
	}
}

/*
 * read is here for the resampler
 */
unsigned long PlayStream_impl::read(unsigned char *data, unsigned long len)
{
	return buffer.read(len, data);
}

void PlayStream_impl::write(const std::vector<Arts::mcopbyte>& bytes)
{
	/* TODO: clip to buffersize?
	 *       fix constness in aRts
     */
	buffer.write(bytes.size(), const_cast<unsigned char *>(&bytes[0]));
	_bufferUsed = buffer.size();	/* TODO: emit change notification */
	adapter->levelChanged(this, _bufferUsed);
}

void PlayStream_impl::bufferSize(long newSize)
{
	if(newSize != _bufferSize)
	{
		_bufferSize = newSize;
		bufferSize_changed(newSize);
	}
}

void PlayStream_impl::params(const StreamParams& newParams)
{
	/* no float support yet */
	buffer.clear();

	_params = newParams;
	_params.format = Adapter_impl::normalizeFormat(newParams.format);

	arts_debug("playstream params: %d channels, rate %d, bits %d",
			   _params.channels, _params.rate, _params.format & sf_size_mask);
	resampler.setChannels(_params.channels);
	resampler.setStep(samplingRateFloat/(float)_params.rate);
	resampler.setBits(_params.format & sf_size_mask);

	if((newParams.format & sf_endian_mask) == sf_endian_le)
		resampler.setEndianness(Resampler::littleEndian);
	else if((newParams.format & sf_endian_mask) == sf_endian_be)
		resampler.setEndianness(Resampler::bigEndian);
}

void PlayStream_impl::active(bool newActive)
{
	if(newActive == _active) return;

	_active = newActive;
	if(_active)
	{
		PlayStream self = PlayStream::_from_base(this->_copy());
		if(!connected)
		{
			connect(self,play);
			connected = true;
		}
		start();
		play.start();
	}
	else
	{
		stop();
		play.stop();
	}
	active_changed(newActive);
}

void PlayStream_impl::clearBuffer()
{
	buffer.clear();
	_bufferUsed = buffer.size();	/* TODO: emit change notification */
	adapter->levelChanged(this, _bufferUsed);
}

void PlayStream_impl::close()
{
	/* TODO */
}

void PlayStream_impl::calculateBlock(unsigned long samples)
{
	/* TODO: handle resampler.underrun() */
	resampler.run(left,right,samples);
	_bufferUsed = buffer.size();	/* TODO: emit change notification */
	adapter->levelChanged(this, _bufferUsed);
}

/* RecordStream: */
RecordStream_impl::RecordStream_impl(Adapter_impl *adapter,
									 const string& role, long ID)
	: Stream_impl(adapter), connected(false)
{
	_role = role;
	_ID = ID;
	_direction = dirInput;

	maxsamples = 0;
	outblock = 0;

	record.autoRestoreID(_role);
}

RecordStream_impl::~RecordStream_impl()
{
	if(maxsamples)
		delete[] outblock;
}

void RecordStream_impl::title(const string& newTitle)
{
	if(newTitle != _title)
	{
		_title = newTitle;
		record.title(_title);
		title_changed(newTitle);
	}
}

vector<mcopbyte> *RecordStream_impl::read(long len)
{
	long toRead = len;
	if(toRead < 0)
		toRead = 0;

	if(buffer.size() < toRead)
		toRead = buffer.size();

	vector<mcopbyte> *result = new vector<mcopbyte>(toRead);

	//mcopbyte *data = new mcopbyte[toRead];
	//long got = buffer.read(len, data);
	//arts_assert(got == toRead);

	//copy(data, data+got, result->begin());
	//delete[] data;

	long got = buffer.read(len, &(*result)[0]);
	arts_assert(got == toRead);

	_bufferUsed = buffer.size();
	adapter->levelChanged(this, _bufferUsed);

	return result;
}

void RecordStream_impl::bufferSize(long newSize)
{
	if(newSize != _bufferSize)
	{
		_bufferSize = newSize;
		bufferSize_changed(newSize);
	}
}

void RecordStream_impl::params(const StreamParams& newParams)
{
	/* no float support yet */
	buffer.clear();

	_params = newParams;
	_params.format = Adapter_impl::normalizeFormat(newParams.format);

	arts_debug("recordstream params: %d channels, rate %d, bits %d",
			   _params.channels, _params.rate, _params.format & sf_size_mask);
}

void RecordStream_impl::active(bool newActive)
{
	if(newActive == _active) return;

	_active = newActive;
	if(_active)
	{
		RecordStream self = RecordStream::_from_base(this->_copy());
		if(!connected)
		{
			connect(record,self);
			connected = true;
		}
		start();
		record.start();
	}
	else
	{
		stop();
		record.stop();
	}
	active_changed(newActive);
}

void RecordStream_impl::clearBuffer()
{
	buffer.clear();
	_bufferUsed = buffer.size();	/* TODO: emit change notification */
	adapter->levelChanged(this, _bufferUsed);
}

void RecordStream_impl::close()
{
	/* TODO */
}

void RecordStream_impl::calculateBlock(unsigned long samples)
{
	if(samples > maxsamples)
	{
		maxsamples = samples;

		if(outblock) delete[] outblock;
		outblock = new mcopbyte[maxsamples * 4]; // 2 channels, 16 bit
	}

	arts_return_if_fail(_params.channels > 0);
	arts_return_if_fail(_params.format == sf_u8
					 || _params.format == sf_s16_le
					 || _params.format == sf_s16_be);

	if(_params.channels == 1)
	{
		if(_params.format == sf_u8)
			convert_mono_float_8(samples,left,outblock);
		else if(_params.format == sf_s16_le)
			convert_mono_float_16le(samples,left,outblock);
		else if(_params.format == sf_s16_be)
			convert_mono_float_16be(samples,left,outblock);
	}
	else if(_params.channels == 2)
	{
		if(_params.format == sf_u8)
			convert_stereo_2float_i8(samples,left,right,outblock);
		else if(_params.format == sf_s16_le)
			convert_stereo_2float_i16le(samples,left,right,outblock);
		else if(_params.format == sf_s16_be)
			convert_stereo_2float_i16be(samples,left,right,outblock);
	}
	else arts_warning("channels != 1 && channels != 2?");

	buffer.write((_params.format & sf_size_mask)/8
				* _params.channels * samples, outblock);
	_bufferUsed = buffer.size();
	adapter->levelChanged(this, _bufferUsed);
}
