/*
    Copyright (C) 2000 Paul Davis 

    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., 675 Mass Ave, Cambridge, MA 02139, USA.

    $Id: sndfilesource.cc,v 1.26 2005/01/27 12:55:49 pauld Exp $
*/

#include <string>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/time.h>

#include <pbd/mountpoint.h>
#include <ardour/sndfilesource.h>

#include "i18n.h"

using namespace ARDOUR;

string SndFileSource::peak_dir = "";

SndFileSource::SndFileSource (const XMLNode& node)
	: Source (node)
{
	if (set_state (node)) {
		throw failed_constructor();
	}

	init (_name, true);
	 SourceCreated (this); /* EMIT SIGNAL */
}

SndFileSource::SndFileSource (const string& idstr, bool build_peak)
	: Source(build_peak)
{
	init (idstr, build_peak);

	if (build_peak) {
		 SourceCreated (this); /* EMIT SIGNAL */
	}
}

void 
SndFileSource::init (const string& idstr, bool build_peak)
{
	string::size_type pos;
	string file;

	tmpbuf = 0;
	tmpbufsize = 0;
	sf = 0;

	_name = idstr;

	if ((pos = idstr.find_last_of (':')) == string::npos) {
		channel = 0;
		file = idstr;
	} else {
		channel = atoi (idstr.substr (pos+1).c_str());
		file = idstr.substr (0, pos);
	}

	/* although libsndfile says we don't need to set this,
	   valgrind and source code shows us that we do.
	*/

	memset (&_info, 0, sizeof(_info));

	/* note that we temporarily truncated _id at the colon */
	
	if ((sf = sf_open (file.c_str(), SFM_READ, &_info)) == 0) {
		char errbuf[256];
		sf_error_str (0, errbuf, sizeof (errbuf) - 1);
		error << compose(_("SndFileSource: cannot open file \"%1\" (%2)"), file, errbuf) << endmsg;
		throw failed_constructor();
	}

	if (channel >= _info.channels) {
		error << compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, channel) << endmsg;
		sf_close (sf);
		sf = 0;
		throw failed_constructor();
	}

	_length = _info.frames;
	_path = file;

	if (build_peak) {
		if (initialize_peakfile (false, file)) {
			sf_close (sf);
			sf = 0;
			throw failed_constructor ();
		}
	}
}

SndFileSource::~SndFileSource ()

{
	 GoingAway (this); /* EMIT SIGNAL */

	if (sf) {
		sf_close (sf);
	}

	if (tmpbuf) {
		delete [] tmpbuf;
	}
}

jack_nframes_t
SndFileSource::read_unlocked (Sample *dst, jack_nframes_t start, jack_nframes_t cnt) const
{
	return read (dst, start, cnt);
}

jack_nframes_t
SndFileSource::read (Sample *dst, jack_nframes_t start, jack_nframes_t cnt) const
{
	int32_t nread;
	float *ptr;
	uint32_t real_cnt;

	if (sf_seek (sf, (off_t) start, SEEK_SET) < 0) {
		char errbuf[256];
		sf_error_str (0, errbuf, sizeof (errbuf) - 1);
		error << compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), errbuf) << endmsg;
		return 0;
	}

	if (_info.channels == 1) {
		jack_nframes_t ret = sf_read_float (sf, dst, cnt);
		_read_data_count = cnt * sizeof(float);
		return ret;
	}

	real_cnt = cnt * _info.channels;

	{
		LockMonitor lm (_tmpbuf_lock, __LINE__, __FILE__);
		
		if (tmpbufsize < real_cnt) {
			
			if (tmpbuf) {
				delete [] tmpbuf;
			}
			tmpbufsize = real_cnt;
			tmpbuf = new float[tmpbufsize];
		}
		
		nread = sf_read_float (sf, tmpbuf, real_cnt);
		ptr = tmpbuf + channel;
		nread /= _info.channels;
		
		/* stride through the interleaved data */
		
		for (int32_t n = 0; n < nread; ++n) {
			dst[n] = *ptr;
			ptr += _info.channels;
		}
	}

	_read_data_count = cnt * sizeof(float);
		
	return nread;
}

string
SndFileSource::peak_path (string audio_path)
{
	/* XXX hardly bombproof! fix me */

	struct stat stat_file;
	struct stat stat_mount;

	string mp = mountpoint (audio_path);

	stat (audio_path.c_str(), &stat_file);
	stat (mp.c_str(), &stat_mount);

	char buf[32];
	snprintf (buf, sizeof (buf), "%ld-%ld-%d.peak", stat_mount.st_ino, stat_file.st_ino, channel);

	string res = peak_dir;
	res += buf;

	return res;
}

string
SndFileSource::old_peak_path (string audio_path)
{
	return peak_path (audio_path);
}
