/*
	Audio File Library
	Copyright (C) 2013 Michael Pruett <michael@68k.org>

	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., 51 Franklin Street, Fifth Floor,
	Boston, MA  02110-1301  USA
*/

#include "config.h"
#include "FLACFile.h"

#include "File.h"
#include "Setup.h"
#include "Track.h"
#include "byteorder.h"
#include "util.h"

#include <string.h>

const int _af_flac_compression_types[_AF_FLAC_NUM_COMPTYPES] =
{
	AF_COMPRESSION_FLAC
};

const _AFfilesetup flacDefaultFileSetup =
{
	_AF_VALID_FILESETUP,
	AF_FILE_FLAC,
	true,
	true,
	true,
	1,		// trackCount
	NULL,	// tracks
	0,		// instrumentCount
	NULL,	// instruments
	0,		// miscellaneousCount
	NULL	// miscellaneous
};

bool FLACFile::recognize(File *file)
{
	file->seek(0, File::SeekFromBeginning);
	uint8_t buffer[4];
	if (file->read(buffer, 4) != 4 || memcmp(buffer, "fLaC", 4) != 0)
		return false;
	return true;
}

FLACFile::FLACFile()
{
}

FLACFile::~FLACFile()
{
}

#if ENABLE(FLAC)

AFfilesetup FLACFile::completeSetup(AFfilesetup setup)
{
	if (setup->trackSet && setup->trackCount != 1)
	{
		_af_error(AF_BAD_NUMTRACKS, "FLAC file must have 1 track");
		return AF_NULL_FILESETUP;
	}

	TrackSetup *track = setup->getTrack();
	if (!track)
		return AF_NULL_FILESETUP;

	if (track->sampleFormatSet &&
		track->f.sampleFormat != AF_SAMPFMT_TWOSCOMP)
	{
		_af_error(AF_BAD_SAMPFMT,
			"FLAC files support only signed integer audio data");
		return AF_NULL_FILESETUP;
	}

	if (track->sampleWidthSet &&
		track->f.sampleWidth != 16 && track->f.sampleWidth != 24)
	{
		_af_error(AF_BAD_WIDTH,
			"FLAC files support only 16- or 24-bit audio data");
		return AF_NULL_FILESETUP;
	}

	_af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, track->f.sampleWidth);

	track->f.byteOrder = _AF_BYTEORDER_NATIVE;

	if (track->compressionSet &&
		track->f.compressionType != AF_COMPRESSION_FLAC)
	{
		_af_error(AF_BAD_COMPTYPE, "Only FLAC compression supported in FLAC files");
		return AF_NULL_FILESETUP;
	}

	track->f.compressionType = AF_COMPRESSION_FLAC;

	if (track->markersSet && track->markerCount)
	{
		_af_error(AF_BAD_NOT_IMPLEMENTED, "FLAC does not yet support markers");
		return AF_NULL_FILESETUP;
	}

	if (track->aesDataSet)
	{
		_af_error(AF_BAD_FILESETUP, "FLAC does not support AES data");
		return AF_NULL_FILESETUP;
	}

	if (setup->instrumentSet && setup->instrumentCount)
	{
		_af_error(AF_BAD_FILESETUP, "FLAC does not support instruments");
		return AF_NULL_FILESETUP;
	}

	if (setup->miscellaneousSet && setup->miscellaneousCount)
	{
		_af_error(AF_BAD_NOT_IMPLEMENTED, "FLAC does not yet support miscellaneous data");
		return AF_NULL_FILESETUP;
	}

	return _af_filesetup_copy(setup, &flacDefaultFileSetup, true);
}

status FLACFile::readInit(AFfilesetup)
{
	m_fh->seek(0, File::SeekFromBeginning);

	FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new();

	if (FLAC__stream_decoder_init_stream(decoder,
		readCallback,
		seekCallback,
		tellCallback,
		lengthCallback,
		eofCallback,
		writeCallback,
		metadataCallback,
		errorCallback,
		this) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
	{
		_af_error(AF_BAD_HEADER, "could not read FLAC file");
		return AF_FAIL;
	}

	if (!FLAC__stream_decoder_process_until_end_of_metadata(decoder))
		return AF_FAIL;

	FLAC__uint64 position;
	if (!FLAC__stream_decoder_get_decode_position(decoder, &position))
		return AF_FAIL;

	Track *track = getTrack();
	if (track)
	{
		track->fpos_first_frame = static_cast<off_t>(position);
		track->data_size = m_fh->length() - track->fpos_first_frame;
	}

	FLAC__stream_decoder_delete(decoder);

	if (!track)
		return AF_FAIL;
	return AF_SUCCEED;
}

status FLACFile::writeInit(AFfilesetup setup)
{
	if (initFromSetup(setup) == AF_FAIL)
		return AF_FAIL;

	return AF_SUCCEED;
}

status FLACFile::update()
{
	return AF_SUCCEED;
}

void FLACFile::parseStreamInfo(const FLAC__StreamMetadata_StreamInfo &streamInfo)
{
	Track *track = allocateTrack();

	track->f.channelCount = streamInfo.channels;
	track->f.sampleRate = streamInfo.sample_rate;

	track->f.byteOrder = _AF_BYTEORDER_NATIVE;

	track->f.framesPerPacket = 0;
	track->f.bytesPerPacket = 0;

	track->f.compressionType = AF_COMPRESSION_FLAC;
	track->f.compressionParams = NULL;

	_af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, streamInfo.bits_per_sample);

	track->totalfframes = streamInfo.total_samples;
}

FLAC__StreamDecoderReadStatus FLACFile::readCallback(const FLAC__StreamDecoder *, FLAC__byte buffer[], size_t *bytes, void *clientData)
{
	FLACFile *flac = static_cast<FLACFile *>(clientData);
	ssize_t result = flac->m_fh->read(buffer, *bytes);
	if (result > 0)
	{
		*bytes = result;
		return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
	}

	*bytes = 0;
	return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
}

FLAC__StreamDecoderSeekStatus FLACFile::seekCallback(const FLAC__StreamDecoder *, FLAC__uint64 absoluteByteOffset, void *clientData)
{
	FLACFile *flac = static_cast<FLACFile *>(clientData);
	if (flac->m_fh->seek(static_cast<off_t>(absoluteByteOffset), File::SeekFromBeginning) < 0)
		return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
	return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
}

FLAC__StreamDecoderTellStatus FLACFile::tellCallback(const FLAC__StreamDecoder *, FLAC__uint64 *absoluteByteOffset, void *clientData)
{
	FLACFile *flac = static_cast<FLACFile *>(clientData);
	off_t result = flac->m_fh->tell();
	if (result < 0)
		return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
	*absoluteByteOffset = static_cast<FLAC__uint64>(result);
	return FLAC__STREAM_DECODER_TELL_STATUS_OK;
}

FLAC__StreamDecoderLengthStatus FLACFile::lengthCallback(const FLAC__StreamDecoder *, FLAC__uint64 *length, void *clientData)
{
	FLACFile *flac = static_cast<FLACFile *>(clientData);
	off_t result = flac->m_fh->length();
	if (result < 0)
		return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
	*length = result;
	return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
}

FLAC__bool FLACFile::eofCallback(const FLAC__StreamDecoder *, void *clientData)
{
	FLACFile *flac = static_cast<FLACFile *>(clientData);
	return flac->m_fh->tell() == flac->m_fh->length();
}

FLAC__StreamDecoderWriteStatus FLACFile::writeCallback(const FLAC__StreamDecoder *, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *clientData)
{
	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}

void FLACFile::metadataCallback(const FLAC__StreamDecoder *, const FLAC__StreamMetadata *metadata, void *clientData)
{
	FLACFile *flac = static_cast<FLACFile *>(clientData);
	if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO)
		flac->parseStreamInfo(metadata->data.stream_info);
}

void FLACFile::errorCallback(const FLAC__StreamDecoder *, FLAC__StreamDecoderErrorStatus status, void *clientData)
{
	_af_error(AF_BAD_FILEFMT, "error %d parsing FLAC file", status);
}

#else

AFfilesetup FLACFile::completeSetup(AFfilesetup)
{
	_af_error(AF_BAD_FILEFMT, "FLAC is disabled");
	return AF_NULL_FILESETUP;
}

status FLACFile::readInit(AFfilesetup)
{
	_af_error(AF_BAD_FILEFMT, "FLAC is disabled");
	return AF_FAIL;
}

status FLACFile::writeInit(AFfilesetup)
{
	return AF_FAIL;
}

status FLACFile::update()
{
	return AF_FAIL;
}

#endif
