/***************************************************************************
 *   Copyright (C) 2007 by Sergio Pistone                                  *
 *   sergio_pistone@yahoo.com.ar                                           *
 *                                                                         *
 *   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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
	#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>

#include <string>
#include <map>

#include "xineinput.h"
#include "waveencoder.h"
#ifdef HAVE_ALSA
	#include "alsaplayer.h"
#endif

#define NAME		"xineadump"
#define VERSION		"0.1"

void abnormalExit( bool error, bool usage, char* message )
{
	if ( message )
		fprintf( stderr, "%s\n", message );

	if ( usage )
	{
		fprintf( stderr,
			"Usage: " NAME " [OPTIONS]\n"
			"Use xine to decode input file/stream and dump its audio\n\n"
			"    --input=[MRL]                    file or stream to decode\n"
			"    --output=[PATH]                  file to write decoded audio as PCM WAV\n"
			"    --stdout                         decode to stdout instead of file\n"
			"                                     note that when not decoding to stdout the\n"
			"                                     header will only be written once the end\n"
			"                                     of the stream (or the limit) is reached\n"
#ifdef HAVE_ALSA
			"    --play                           play instead of decoding to file/stdout\n"
#endif
			"    --verbose                        show progress indication\n"
			"    --length                         limit amount of audio to dump (msecs)\n"
			"    --version                        show version and exit\n"
		);
	}

	exit( error ? 1 : 0 );
}

std::map<std::string,std::string> parseArgs( int argc, char* argv[] )
{
	std::map<std::string,std::string> args;

	for ( int idx = 1; idx < argc; ++idx )
	{
		std::string arg( argv[idx] );
		if ( arg.length() < 3 )
			abnormalExit( true, true, "Invalid param received." );

		if ( arg[0] != '-' || arg[1] != '-' )
			abnormalExit( true, true, "Invalid param received." );

		size_t pos = arg.find_first_of( "=", 2 );
		if ( pos == std::string::npos )
			args[arg.substr( 2 )] = "";
		else
			args[arg.substr( 2, pos-2 )] = arg.substr( pos + 1 );
	}

	return args;
}

unsigned long positiveNumber( const char* text, unsigned long min, unsigned long max, bool* error )
{
	if ( ! text || ! *text )
	{
		if ( error )
			*error = true;
		return 0;
	}

	char* nonDigit;
	long long number = strtoll( text, &nonDigit, 10 );

	if ( error )
		*error = *nonDigit != '\0' || number < min || number > max;

	return number;
}

int main ( int argc, char **argv )
{
	std::map<std::string,std::string> args = parseArgs( argc, argv );

	std::string inputMRL, outputPath;
	bool verbose = false, stdoutMode = false, playMode = false;
	unsigned long length = Input::NoLimit;

	if ( args.find( "help" ) != args.end() )
		abnormalExit( false, true, 0 );

	if ( args.find( "version" ) != args.end() )
	{
		fprintf( stderr, "%s v%s\n", NAME, VERSION );
		exit( 0 );
	}

	if ( args.find( "verbose" ) != args.end() &&
		 args["verbose"] != "" && args["verbose"] != "true" && args["verbose"] != "false" )
		abnormalExit( true, true, "Invalid modifier for verbose option." );
	else
		verbose = args.find( "verbose" ) != args.end() && args["verbose"] != "false";
	args.erase( "verbose" );

	if ( args.find( "length" ) != args.end() )
	{
		bool error;
		unsigned long aux = positiveNumber( args["length"].c_str(), 1, Input::MaxLimit, &error );
		if ( error )
			abnormalExit( true, true, "Invalid length specified." );
		else
			length = aux;
		args.erase( "length" );
	}

	if ( args.find( "input" ) == args.end() || args["input"].empty() )
		abnormalExit( true, true, "Must specify input file." );
	else
		inputMRL = args["input"];
	args.erase( "input" );

#ifdef HAVE_ALSA
	if ( args.find( "play" ) != args.end() && args["play"] != "" )
		abnormalExit( true, true, "Invalid modifier for play option." );
	else
		playMode = args.find( "play" ) != args.end();
	args.erase( "play" );
#endif

	if ( args.find( "stdout" ) != args.end() && args["stdout"] != "" )
		abnormalExit( true, true, "Invalid modifier for stdout option." );
	else
		stdoutMode = args.find( "stdout" ) != args.end();
	args.erase( "stdout" );

	if ( playMode && stdoutMode )
		abnormalExit( true, true, "Can't specify both stdout and play options." );

	if ( (playMode || stdoutMode) && args.find( "output" ) != args.end() )
		abnormalExit( true, true, "Can't specify output with stdout or play options." );

	if ( (! playMode && ! stdoutMode) && (args.find( "output" ) == args.end() || args["output"].empty()) )
#ifdef HAVE_ALSA
		abnormalExit( true, true, "Must specify output file or one of stdout or play modes." );
#else
		abnormalExit( true, true, "Must specify output file or stdout mode." );
#endif

	outputPath = args["output"];
	args.erase( "output" );

	if ( args.size() )
		abnormalExit( true, true, "Unknown option received." );

	XineInput input( inputMRL, length );
	//ZeroInput input( length );

#ifdef HAVE_ALSA
	if ( playMode )
	{
		ALSAPlayer player( verbose );
		if ( ! player.play( input ) )
			abnormalExit( true, false, "There was an error decoding the input." );
 	}
	else
#endif
	{
		WaveEncoder encoder( outputPath, verbose );
		if ( ! encoder.play( input ) )
			abnormalExit( true, false, "There was and error transcoding (probably a decoding error)." );
	}

	return 0;
}
