/* Extended Module Player
 * Copyright (C) 1996-2001 Claudio Matsuoka and Hipolito Carraro Jr
 *
 * This file is part of the Extended Module Player and is distributed
 * under the terms of the GNU General Public License. See doc/COPYING
 * for more information.
 *
 * $Id: options.c,v 1.6 2001/01/17 00:01:37 claudio Exp $
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>

#include "xmp.h"

extern char *optarg;
static int o, i;
static char *token;

extern int probeonly;
extern int loadonly;
extern int randomize;
extern int nocmd;
#ifdef HAVE_SYS_RTPRIO_H
extern int rt;
#endif

#define OPT_CHORUS	0x100
#define OPT_REVERB	0x101
#define OPT_NOPAN	0x102
#define OPT_LOADONLY	0x103
#define OPT_NORC	0x104
#define OPT_FX9BUG	0x105
#define OPT_PROBEONLY	0x106
#define OPT_BIGENDIAN	0x107
#define OPT_LILENDIAN	0x108
#define OPT_STDOUT	0x109
#define OPT_STEREO	0x10a
#define OPT_NOCMD	0x10b
#define OPT_REALTIME	0x10c
#define OPT_FIXLOOP	0x10d
#define OPT_CRUNCH	0x10e
#define OPT_NOFILTER	0x10f


static void list_wrap (char *s, int l, int r, int v)
{
    int i;
    static int c = 0;
    static int m = 0;
    char *t;

    if (s == NULL) {
	for (i = 0; i < l; i++)
	    printf (" ");
	c = l;
	m = r;
	return;
    } else if (c > l) {
	c++;
	printf (v ? "," : " ");
    }

    t = strtok (s, " ");

    while (t) {
	if ((c + strlen (t) + 1) > m) {
	    c = l;
	    printf ("\n");
	    for (i = 0; i < l; i++) {
		printf (" ");
	    }
	} else if (c > l) {
	    printf (" ");
	}
	c += strlen (t) + 1;
	printf ("%s", t);
	t = strtok (NULL, " ");
    }
}


static void usage (char *s, struct xmp_control *opt)
{
    struct xmp_fmt_info *f, *fmt;
    struct xmp_drv_info *d, *drv;
    char **hlp, buf[80];
    int i;

    printf ("Extended Module Player %s %s\n", xmp_version, xmp_date);
    printf (
"Copyright (C) 1996-2001 Claudio Matsuoka and Hipolito Carraro Jr\n"
"Portions Copyright (C) 1996-1997 Takashi Iwai, (C) 1989 Rich Gopstein and\n"
"Harris Corporation, (C) 1995 Marc Espie, (C) 1998 Tammo Hinrichs, (C) 1997\n"
"Bert Jahn, (C) 1998,2000 Olivier Lapicque, (C) 1999 Tatsuyuki Satoh,\n"
"(C) 1998 Sylvain Chipaux\n\n");
    printf ("%s\n", xmp_build); 

    printf ("Usage: %s [options] [modules]\n", s);

    printf ("\nSupported module formats:\n");
    xmp_get_fmt_info (&fmt);
    list_wrap (NULL, 3, 78, 1);
    for (i = 0, f = fmt; f; i++, f = f->next) {
        sprintf (buf, "%s (%s)", f->suffix, f->tracker);
        list_wrap (buf, 3, 0, 1);
    }
    sprintf (buf, "[%d known formats]", i);
    list_wrap (buf, 3, 0, 0);
    printf ("\n");

    printf ("\nAvailable drivers "
#ifdef DYNAMIC_DRV
	"(dynamically linked)"
#else
	"(statically linked)"
#endif
        ":\n");

    xmp_get_drv_info (&drv);
    list_wrap (NULL, 3, 78, 1);
    for (d = drv; d; d = d->next) {
        sprintf (buf, "%s (%s)", d->id, d->description);
        list_wrap (buf, 3, 0, 1);
    }

  printf ("\n");

    for (d = drv; d; d = d->next) {
	if (d->help)
	    printf ("\n%s options:\n", d->description);
	for (hlp = d->help; hlp && *hlp; hlp += 2)
	    printf ("   -D%-20.20s %s\n", hlp[0], hlp[1]);
    }

    printf (
"\nPlayer control options:\n"
"   -D parameter[=val]     Pass configuration parameter to the output driver\n"
"   -d --driver name       Force output to the specified device\n"
"   --fix-sample-loops     Use sample loop start /2 in MOD/UNIC/NP3\n" 
"   --offset-bug-emulation Emulate Protracker 2.x bug in effect 9\n"
"   -l --loop              Enable module looping\n"
"   -M --mute ch-list      Mute the specified channels\n"
"   --modrange             Limit the octave range to 3 octaves in MOD files\n"
"   --nocmd                Disable interactive commands\n"
"   --norc                 Don't read /etc/xmprc or $HOME/.xmprc\n"
"   -R --random            Random order playing\n"
#ifdef HAVE_SYS_RTPRIO_H
"   --realtime             Run in real-time priority\n" 
#endif
"   -S --solo ch-list      Set channels to solo mode\n"
"   -s --start num         Start from the specified order\n"
"   -T --tempo num         Initial tempo (default 6)\n"
"   -t --time num          Maximum playing time in seconds\n"

"\nPlayer sound options:\n"
"   -8 --8bit              Convert 16 bit samples to 8 bit\n"
"   --chorus num           Chorus depth (if supported)\n"
"   -m --mono              Mono output\n"
"   --nofilter             Disable IT filter\n"
"   --nopan                Disable dynamic panning\n"
"   -P --pan pan           Percentual pan amplitude (default %d%%)\n"
"   -r --reverse           Reverse left/right stereo channels\n"
"   --reverb num           Reverb depth (if supported)\n"
"   --stereo               Stereo output\n"

"\nSoftware mixer options:\n"
"   --big-endian           Use big-endian 16 bit samples\n"
"   -b --bits {8|16}       Software mixer resolution (8 or 16 bits)\n"
"   -c --stdout            Mix the module to stdout\n"
"   -f --frequency rate    Sampling rate in hertz (default %d Hz)\n"
"   -i --interpolate       Enable/disable interpolation (default %s)\n"
"   --little-endian        Use little-endian 16 bit samples\n"
"   -o --output-file name  Mix the module to file ('-' for stdout)\n"
"   -u --unsigned          Set the mixer to use unsigned samples\n"

"\nInformation options:\n"
"   -h --help              Print a summary of the command line options\n"
"   -L --license           Display software license\n"
"   --load-only            Load module and exit\n"
"   --probe-only           Probe audio device and exit\n"
"   -q --quiet             Quiet mode (verbosity level = 0)\n"
"   -V --version           Print version information\n"
"   -v --verbose           Verbose mode (incremental)\n"
	,opt->mix,
	opt->freq,
	opt->flags & XMP_CTL_ITPT ? "enabled" : "disabled"
    );
}


void get_options (int argc, char **argv, struct xmp_control *opt)
{
    int optidx = 0;
#define OPTIONS "8Bb:cD:d:f:hilM:mo:P:qRrS:s:T:t:uVv"
    static struct option lopt[] =
    {
	{ "8bit",		 0, 0, '8' },
	{ "big-endian",		 0, 0, OPT_BIGENDIAN },
	{ "bits",		 1, 0, 'b' },
	{ "chorus",		 1, 0, OPT_CHORUS },
	{ "crunch",		 1, 0, OPT_CRUNCH },
	{ "driver",		 1, 0, 'd' },
	{ "fix-sample-loops",	 0, 0, OPT_FIXLOOP },
	{ "frequency",		 1, 0, 'f' },
	{ "offset-bug-emulation",0, 0, OPT_FX9BUG },
	{ "help",		 0, 0, 'h' },
	{ "interpolate",	 0, 0, 'i' },
	{ "little-endian",	 0, 0, OPT_LILENDIAN },
	{ "load-only",	 	 0, 0, OPT_LOADONLY },
	{ "loop",		 0, 0, 'l' },
	{ "mute",		 1, 0, 'M' },
	{ "mono",		 0, 0, 'm' },
	{ "nocmd",		 0, 0, OPT_NOCMD },
	{ "nofilter",		 0, 0, OPT_NOFILTER },
	{ "nopan",		 0, 0, OPT_NOPAN },
	{ "norc",		 0, 0, OPT_NORC },
	{ "output-file",	 1, 0, 'o' },
	{ "pan",		 1, 0, 'P' },
	{ "probe-only",		 0, 0, OPT_PROBEONLY },
	{ "quiet",		 0, 0, 'q' },
	{ "random",		 0, 0, 'R' },
#ifdef HAVE_SYS_RTPRIO_H
	{ "realtime",		 0, 0, OPT_REALTIME },
#endif
	{ "reverb",		 1, 0, OPT_CHORUS },
	{ "reverse",		 0, 0, 'r' },
	{ "solo",		 1, 0, 'S' },
	{ "start",		 1, 0, 's' },
	{ "stdout",		 0, 0, 'c' },
	{ "stereo",		 0, 0, OPT_STEREO },
	{ "tempo",		 1, 0, 'T' },
	{ "time",		 1, 0, 't' },
	{ "unsigned",		 0, 0, 'u' },
	{ "version",		 0, 0, 'V' },
	{ "verbose",		 0, 0, 'v' },
	{ NULL,	0, 0, 0 }
    };

    i = 0;
    while ((o = getopt_long (argc, argv, OPTIONS, lopt, &optidx)) != -1) {
	switch (o) {
	case '8':
	    opt->flags |= XMP_CTL_8BIT;
	    break;
	case 'B':
	    opt->flags |= XMP_CTL_BIGEND;
	    break;
	case 'b':
	    opt->resol = atoi (optarg);
	    if (opt->resol != 8 && opt->resol != 16)
		opt->resol = 16;
	    break;
	case OPT_CHORUS:
	    opt->chorus = strtoul (optarg, NULL, 0);
	    break;
	case OPT_CRUNCH:
	    opt->crunch = strtoul (optarg, NULL, 0);
	    break;
	case 'c':
	    opt->outfile = "-";
	    break;
	case 'D':
	    xmp_set_driver_parameter (opt, optarg);
	    break;
	case 'd':
	    opt->drv_id = optarg;
	    break;
	case OPT_FIXLOOP:
	    opt->fetch |= XMP_CTL_FIXLOOP;
	    break;
	case OPT_FX9BUG:
	    opt->fetch |= XMP_CTL_FX9BUG;
	    break;
	case 'f':
	    opt->freq = atoi (optarg);
	    break;
	case 'i':
	    opt->flags ^= XMP_CTL_ITPT;
	    break;
	case 'l':
	    opt->flags |= XMP_CTL_LOOP;
	    break;
	case OPT_LILENDIAN:
	    opt->flags &= ~XMP_CTL_BIGEND;
	    break;
	case OPT_LOADONLY:
	    loadonly = 1;
	    break;
	case 'm':
	    opt->outfmt |= XMP_FMT_MONO;
	    break;
	case OPT_NOCMD:
	    nocmd = 1;
	    break;
	case OPT_NOFILTER:
	    opt->flags &= ~XMP_CTL_FILTER;
	    break;
	case OPT_NOPAN:
	    opt->flags &= ~XMP_CTL_DYNPAN;
	    break;
	case OPT_NORC:
	    break;
	case 'o':
	    opt->outfile = optarg;
	    break;
	case 'P':
	    opt->mix = atoi (optarg);
	    if (opt->mix < 0)
		opt->mix = 0;
	    if (opt->mix > 100)
		opt->mix = 100;
	    break;
	case OPT_PROBEONLY:
	    probeonly = 1;
	    opt->verbose = 0;
	    break;
	case 'q':
	    opt->verbose = 0;
	    break;
#ifdef HAVE_SYS_RTPRIO_H
	case OPT_REALTIME:
	    rt = 1;
	    break;
#endif
	case OPT_REVERB:
	    opt->reverb = strtoul (optarg, NULL, 0);
	    break;
	case 'R':
	    randomize = 1;
	    break;
	case 'r':
	    opt->flags |= XMP_CTL_REVERSE;
	    break;
	case 'M':
	case 'S':
	    if (o == 'S')
		xmp_channel_mute (0, 64, 1);
	    token = strtok (optarg, ",");
	    while (token) {
		int a, b;
		char buf[40];
		if (strchr (token, '-')) {
		    b = strcspn (token, "-");
		    strncpy (buf, token, b);
		    a = atoi (buf);
		    strncpy (buf, token + b + 1,
			strlen (token) - b - 1);
		    b = atoi (buf);
		} else
		    a = b = atoi (token);
		for (; b >= a; b--) {
		    if (b < 64)
			xmp_channel_mute (b, 1, (o == 'M'));
		}
		token = strtok (NULL, ",");
	    }
	    break;
	case 's':
	    opt->start = strtoul (optarg, NULL, 0);
	    break;
	case OPT_STEREO:
	    opt->outfmt &= ~XMP_FMT_MONO;
	    break;
	case 'T':
	    opt->tempo = strtoul (optarg, NULL, 0);
	    break;
	case 't':
	    opt->time = strtoul (optarg, NULL, 0);
	    break;
	case 'u':
	    opt->outfmt |= XMP_FMT_UNS;
	    break;
	case 'V':
	    printf ("Extended Module Player %s\n", xmp_version);
	    exit (0);
	case 'v':
	    opt->verbose++;
	    break;
	case 'h':
	    usage (argv[0], opt);
	default:
	    exit (-1);
	}
    }

    /* Set limits */
    if (opt->freq < 1000)
	opt->freq = 1000;	/* Min. rate 1 kHz */
    if (opt->freq > 48000)
	opt->freq = 48000;	/* Max. rate 48 kHz */

    /* Update fetch control */
    opt->fetch = opt->flags;
}
