/*
 *  playlist-flac.c
 *  mod_musicindex
 *
 *  $Id: playlist-flac.c 1010 2012-08-07 13:38:34Z varenet $
 *
 *  Created by Regis BOUDIN on Thu Jun 30 2004.
 *  Copyright (c) 2004 Regis BOUDIN
 *  Copyright (c) 2004-2005,2007 Thibaut VARENE
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License version 2.1,
 *  as published by the Free Software Foundation.
 *
 */

/**
 * @file
 * Flac files management system.
 *
 * @author Regis Boudin
 * @author Thibaut Varene
 * @version $Revision: 1010 $
 * @date 2004-2005
 * @date 2007
 *
 * This file contains everything needed to produce music entries from
 * Flac files.
 */

#include "playlist.h"
#include "playlist-flac.h"

#include <FLAC/format.h>
#include <FLAC/metadata.h>

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

/**
 * Checks for valid FLAC filename extension.
 * FLAC is an arse dealing with unknown files, we /really/ want to have this check.
 * Valid extensions are .flac and .fla
 *
 * @param filename The filename to check.
 * @warning Will match anything with .fla*. Not a big deal since the flac
 * handler will discard the file if it's not a true FLAC file anyway.
 *
 * @return TRUE when the extension is correct, FALSE otherwise.
 */
static inline short flac_ext_check(const char *const filename)
{
	const char *const ext = strrchr(filename, '.');
	if (ext	&& (!strncasecmp(ext, ".fla", 4)))
		return TRUE;
	return FALSE;
}

/**
 * Fills in the information fields about FLAC data.
 *
 * This function reads the ogg tags (using libFLAC) from the flac file @a in
 * and fills in the struct mu_ent fields accordingly. If the file is not an
 * ogg one, the function returns head.
 *
 * @param pool Pool
 * @param in ogg file to parse (closed on normal exit)
 * @param filename current filename
 * @param r Apache request_rec struct to handle log writings (debugging)
 *
 * @return When possible, struct mu_ent correctly set up, file stream closed.
 */
mu_ent *make_flac_entry(request_rec *r, apr_pool_t *pool, FILE *const in,
	const char *const filename)
{
	const mu_config *const conf = (mu_config *)ap_get_module_config(r->per_dir_config, &musicindex_module);
	mu_ent				*p = NULL;
	struct stat			filestat;
	FLAC__StreamMetadata		streaminfo;
	FLAC__Metadata_SimpleIterator	*iterator;

	if (!flac_ext_check(filename) || !FLAC__metadata_get_streaminfo(filename, &streaminfo))
		return NULL;

	fstat(fileno(in), &filestat);
		
	p = NEW_ENT(pool);
	if (p == NULL)
		return NULL;

	p->filetype = FT_FLAC;
	p->flags |= EF_VBR;

	p->size = filestat.st_size;
	p->mtime = filestat.st_mtime;

	fclose(in);	/* No longer using the provided fstream */

	if (conf->options & (MI_QUICKPL))
		p->bitrate = p->length = p->freq = 0;
	else {
		/* FLAC codec doesn't provide the information we need, so we have to make it up by ourselves...
		We should check the correctness of the sample_rate using the appropriate FLAC_ function btw */
		p->bitrate = (8 * filestat.st_size) / (streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate);
		p->length = (unsigned short)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate);
		p->freq = streaminfo.data.stream_info.sample_rate;
	}

	iterator = FLAC__metadata_simple_iterator_new();

	if (!iterator)
		goto exit;

	if (FLAC__metadata_simple_iterator_init(iterator, filename, true, true)) {
		FLAC__StreamMetadata *block = NULL;
		const FLAC__StreamMetadata_VorbisComment *vc = NULL;
		register unsigned short i;
		do {
			if(FLAC__metadata_simple_iterator_get_block_type(iterator) != FLAC__METADATA_TYPE_VORBIS_COMMENT)
				continue;

			block = FLAC__metadata_simple_iterator_get_block(iterator);
			if(block == NULL)
				continue;

			/* If we reached that point, then we have found something! */
			vc = &block->data.vorbis_comment;
			for(i = 0; i < vc->num_comments; i++) {
				if (!strncasecmp((char *)vc->comments[i].entry, "album=", 6))
					p->album = apr_pstrndup(pool, (char *)vc->comments[i].entry+6, vc->comments[i].length-6);
				else if (!strncasecmp((char *)vc->comments[i].entry, "artist=", 7))
					p->artist = apr_pstrndup(pool, (char *)vc->comments[i].entry+7, vc->comments[i].length-7);
				else if (!strncasecmp((char *)vc->comments[i].entry, "title=", 6))
					p->title = apr_pstrndup(pool, (char *)vc->comments[i].entry+6, vc->comments[i].length-6);
				else if (!strncasecmp((char *)vc->comments[i].entry, "tracknumber=", 12))
					p->track = atoi((char *)vc->comments[i].entry+12);
				else if (!strncasecmp((char *)vc->comments[i].entry, "discnumber=", 11))
					p->posn = atoi((char *)vc->comments[i].entry+11);
				else if (!strncasecmp((char *)vc->comments[i].entry, "date=", 5))
					p->date = atoi((char *)vc->comments[i].entry+5);
			}
			FLAC__metadata_object_delete(block);

			break;
		} while (FLAC__metadata_simple_iterator_next(iterator));
	}
	FLAC__metadata_simple_iterator_delete(iterator);

exit:
	return p;
}
