    /*

    Copyright (C) 2002 Hans Meine
                       hans_meine@gmx.net

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.

    */

#include "datahandle.h"
#include "../../mcop/debug.h"

namespace GSL {

void DataHandle::copyFrom(const DataHandle &other)
{
	handle_ = other.handle_;
	if(handle_)
		gsl_data_handle_ref(handle_);
}

DataHandle::DataHandle(GslDataHandle *handle)
	: handle_(handle)
{
}

DataHandle::DataHandle(const DataHandle &other) { copyFrom(other); }

DataHandle &DataHandle::operator =(const DataHandle &other)
{
	if(!( other == *this )) {
		if(handle_)
			gsl_data_handle_unref(handle_);
		copyFrom(other);
	}
	return *this;
}

bool DataHandle::operator ==(const DataHandle &other) const
{
	return handle_ == other.handle_;
}

bool DataHandle::isNull() const
{
	return handle_ == 0;
}

DataHandle::~DataHandle()
{
	if(handle_)
		gsl_data_handle_unref(handle_);
}

gint DataHandle::open()
{
	arts_return_val_if_fail(handle_ != 0, -1);

	arts_debug("open()ing datahandle (open_count before: %d)..",
			   handle_->open_count);

	return gsl_data_handle_open(handle_);
}

void DataHandle::close()
{
	arts_return_if_fail(handle_ != 0);

	arts_debug("close()ing datahandle (open_count before: %d)..",
			   handle_->open_count);

	gsl_data_handle_close(handle_);
}

bool DataHandle::isOpen() const
{
	if(!handle_)
		return false;

	return handle_->open_count != 0;
}

GslLong DataHandle::read(GslLong valueOffset, GslLong valueCount, gfloat *values)
{
	arts_return_val_if_fail(handle_ != 0, 0);

	return gsl_data_handle_read(handle_, valueOffset, valueCount, values);
}

DataHandle DataHandle::createCropped(GslLong headCutValueCount,
									 GslLong tailCutValueCount)
{
	arts_return_val_if_fail(handle_ != 0, null());

	return DataHandle(gsl_data_handle_new_crop(handle_,
											   headCutValueCount, tailCutValueCount));
}

DataHandle DataHandle::createCut(GslLong cutOffset,
								 GslLong cutValueCount)
{
	arts_return_val_if_fail(handle_ != 0, null());

	return DataHandle(gsl_data_handle_new_cut(handle_, cutOffset,
											  cutValueCount));
}

DataHandle DataHandle::createReversed()
{
	arts_return_val_if_fail(handle_ != 0, null());

	return DataHandle(gsl_data_handle_new_reverse(handle_));
}

GslDataCache *DataHandle::createGslDataCache()
{
	arts_debug("wanna have cache with padding %d for each of %d channels..",
			   gsl_get_config()->wave_chunk_padding,
			   channelCount());
	return gsl_data_cache_from_dhandle(handle_,
									   gsl_get_config()->wave_chunk_padding *
									   channelCount());
}

GslLong DataHandle::valueCount() const
{
	arts_return_val_if_fail(handle_ != 0, 0);
	arts_return_val_if_fail(isOpen(), 0);

	return handle_->setup.n_values;
}

guint DataHandle::channelCount() const
{
	arts_return_val_if_fail(handle_ != 0, 0);
	arts_return_val_if_fail(isOpen(), 0);

	return handle_->setup.n_channels;
}

guint DataHandle::bitDepth() const
{
	arts_return_val_if_fail(handle_ != 0, 0);
	arts_return_val_if_fail(isOpen(), 0);

	return handle_->setup.bit_depth;
}

// ----------------------------------------------------------------------------

WaveChunkDescription::WaveChunkDescription(const GslWaveDsc *parent, guint index)
	: parent_(parent), parentIndex_(index)
{
	if(index>parent->n_chunks)
	{
		arts_debug("wrong index given to WaveChunkDescription constructor, "
				   "using 0 instead..");
		parentIndex_ = 0;
	}
}

float WaveChunkDescription::oscillatorFrequency()
{
	return parent_->chunks[parentIndex_].osc_freq;
}

float WaveChunkDescription::mixerFrequency()
{
	return parent_->chunks[parentIndex_].mix_freq;
}

GslWaveLoopType WaveChunkDescription::loopType()
{
	return parent_->chunks[parentIndex_].loop_type;
}

GslLong WaveChunkDescription::loopStart()
{
	return parent_->chunks[parentIndex_].loop_start;
}

GslLong WaveChunkDescription::loopEnd()
{
	return parent_->chunks[parentIndex_].loop_end;
}

guint WaveChunkDescription::loopCount()
{
	return parent_->chunks[parentIndex_].loop_count;
}

WaveDataHandle WaveChunkDescription::createDataHandle()
{
	return WaveDataHandle(parent_, parentIndex_);
}

// ----------------------------------------------------------------------------

/**
 * We use GslWaveFileInfos' refcounting - probably this lazy
 * construction happens only once, where the object is used. After
 * copying it would have to be constructed again, but that's not
 * likely to happen. (?)
 */
void WaveDescription::ensurePointer() const
{
	if(!desc_)
		desc_ = gsl_wave_dsc_load(parentInfo_, parentIndex_, &error_);
}

void WaveDescription::copyFrom(const WaveDescription &other)
{
	parentInfo_ = other.parentInfo_;
	parentIndex_ = other.parentIndex_;
	gsl_wave_file_info_ref(other.parentInfo_);
}

// internal, private
WaveDescription::WaveDescription(GslWaveFileInfo *parent, guint index,
								 const std::string &name)
	: parentInfo_(parent), name_(name), parentIndex_(index),
	  desc_(0), error_(GSL_ERROR_NONE)
{
	gsl_wave_file_info_ref(parentInfo_);
}

WaveDescription::~WaveDescription()
{
	if(desc_)
		gsl_wave_dsc_free(desc_);
	gsl_wave_file_info_unref(parentInfo_);
}

WaveDescription::WaveDescription(const WaveDescription &other)
	: desc_(0), error_(GSL_ERROR_NONE)
{
	copyFrom(other);
}

WaveDescription &WaveDescription::operator =(const WaveDescription &other)
{
	if(desc_)
		gsl_wave_dsc_free(desc_);
	gsl_wave_file_info_unref(parentInfo_);
	copyFrom(other);
	return *this;
}

const std::string &WaveDescription::name() const
{
	return name_;
}

GslErrorType WaveDescription::error() const
{
	ensurePointer();
	return error_;
}

guint WaveDescription::chunkCount() const
{
	ensurePointer();
	return desc_ ? desc_->n_chunks : 0;
}

WaveChunkDescription WaveDescription::chunkDescription(guint index) const
{
	ensurePointer();
	return WaveChunkDescription(desc_, index);
}

guint WaveDescription::channelCount() const
{
	ensurePointer();
	return desc_ ? desc_->n_channels : 0;
}

// ----------------------------------------------------------------------------

void WaveFileInfo::copyFrom(const WaveFileInfo &other)
{
	info_ = other.info_;
	filename_ = other.filename_;
	if(info_)
		gsl_wave_file_info_ref(info_);

	error_ = other.error_;
}

WaveFileInfo::WaveFileInfo(const std::string &filename)
	: info_(0), error_(GSL_ERROR_NONE), filename_(filename)
{
	info_ = gsl_wave_file_info_load(filename.c_str(), &error_);
}

WaveFileInfo::~WaveFileInfo()
{
	if(info_)
		gsl_wave_file_info_unref(info_);
}

WaveFileInfo::WaveFileInfo(const WaveFileInfo &other)
{
	copyFrom(other);
}

WaveFileInfo &WaveFileInfo::operator =(const WaveFileInfo &other)
{
	if(info_)
		gsl_wave_file_info_unref(info_);
	copyFrom(other);
	return *this;
}

guint WaveFileInfo::waveCount() const
{
	return info_ ? info_->n_waves : 0;
}

std::string WaveFileInfo::waveName(guint index) const
{
	if(index >= waveCount())
		return "";
	return info_->waves[index].name;
}

WaveDescription WaveFileInfo::waveDescription(guint index)
{
	return WaveDescription(info_, index, waveName(index));
}

GslErrorType WaveFileInfo::error() const
{
	return error_;
}

// ----------------------------------------------------------------------------

WaveDataHandle::WaveDataHandle()
	: DataHandle(NULL),
	  oscillatorFrequency_(0),
	  mixerFrequency_(0)
{
}

WaveDataHandle::WaveDataHandle(const GslWaveDsc *waveDesc, guint chunkIndex)
	: DataHandle(NULL),
	  oscillatorFrequency_(0),
	  mixerFrequency_(0)
{
	handle_ = gsl_wave_handle_create(const_cast<GslWaveDsc *>(waveDesc),
									 chunkIndex, &error_);
	if(error() == GSL_ERROR_NONE)
	{
		oscillatorFrequency_ = waveDesc->chunks[chunkIndex].osc_freq;
		mixerFrequency_ = waveDesc->chunks[chunkIndex].mix_freq;
	}
}

WaveDataHandle::WaveDataHandle(const std::string& filename,
							   guint waveIndex,
							   guint chunkIndex)
	: DataHandle(NULL),
	  oscillatorFrequency_(0),
	  mixerFrequency_(0)
{
	GSL::WaveFileInfo info(filename);
	error_ = info.error();

	if(!info.error())
	{
		GSL::WaveDescription desc= info.waveDescription(waveIndex);
		error_ = desc.error();

		if(!desc.error() && (desc.chunkCount() > chunkIndex))
		{
			GSL::WaveChunkDescription chunkDesc= desc.chunkDescription(chunkIndex);

			(*this) = chunkDesc.createDataHandle();
		}
	}
}

GslErrorType WaveDataHandle::error() const
{
	return error_;
}

float WaveDataHandle::oscillatorFrequency() const
{
	return oscillatorFrequency_;
}

float WaveDataHandle::mixerFrequency() const
{
	return mixerFrequency_;
}

}
