/*
 * Copyright (C) 2008-2017 Paul Davis <paul@linuxaudiosystems.com>
 * Copyright (C) 2009 David Robillard <d@drobilla.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "ardour/caimportable.h"
#include <sndfile.h>
#include "pbd/error.h"

#include "pbd/i18n.h"

using namespace ARDOUR;
using namespace std;
using namespace PBD;

CAImportableSource::CAImportableSource (const string& path)
{
	try {
		af.Open (path.c_str());

		CAStreamBasicDescription file_format (af.GetFileDataFormat());
		CAStreamBasicDescription client_format (file_format);

		/* set canonial form (PCM, native float packed, 32 bit, with the correct number of channels
		   and interleaved (since we plan to deinterleave ourselves)
		*/

		client_format.SetCanonical(client_format.NumberChannels(), true);
		af.SetClientFormat (client_format);

	} catch (CAXException& cax) {
                //Don't report an error here since there is one higher up in import.
                //Since libsndfile gets tried second, any failures here may show as
                //invalid errors in the Error log.
		throw failed_constructor ();
	}

}

CAImportableSource::~CAImportableSource ()
{
}

samplecnt_t
CAImportableSource::read (Sample* buffer, samplecnt_t nframes)
{
	samplecnt_t nread = 0;
	AudioBufferList abl;
	samplecnt_t per_channel;
	bool at_end = false;

	abl.mNumberBuffers = 1;
	abl.mBuffers[0].mNumberChannels = channels();

	per_channel = nframes / abl.mBuffers[0].mNumberChannels;

	while (nread < per_channel) {

		UInt32 new_cnt = per_channel - nread;

		abl.mBuffers[0].mDataByteSize = new_cnt * abl.mBuffers[0].mNumberChannels * sizeof(Sample);
		abl.mBuffers[0].mData = buffer + nread;

		try {
			af.Read (new_cnt, &abl);
		} catch (CAXException& cax) {
			error << string_compose("CAImportable: %1", cax.mOperation);
			return -1;
		}

		if (new_cnt == 0) {
			/* EOF */
			at_end = true;
			break;
		}

		nread += new_cnt;
	}

	if (!at_end && nread < per_channel) {
		return 0;
	} else {
		return nread * abl.mBuffers[0].mNumberChannels;
	}
}

uint
CAImportableSource::channels () const
{
	return af.GetFileDataFormat().NumberChannels();
}

samplecnt_t
CAImportableSource::length () const
{
	return af.GetNumberFrames();
}

samplecnt_t
CAImportableSource::samplerate () const
{
	CAStreamBasicDescription client_asbd;

	try {
		client_asbd = af.GetClientDataFormat ();
	} catch (CAXException& cax) {
		error << string_compose ("CAImportable: %1", cax.mOperation) << endmsg;
		return 0.0;
	}

	return client_asbd.mSampleRate;
}

void
CAImportableSource::seek (samplepos_t pos)
{
	try {
		af.Seek (pos);
	} catch (CAXException& cax) {
		error << string_compose ("CAImportable: %1 to %2", cax.mOperation, pos) << endmsg;
	}
}


samplepos_t
CAImportableSource::natural_position () const
{
	// TODO: extract timecode, if any
	return 0;
}
