/*
 *      Command line parsing related functions
 *
 *      Copyright (c) 1999 Mark Taylor
 *
 * 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 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
 * Library 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/* $Id: Parse.java,v 1.28 2011/05/24 22:17:17 kenchis Exp $ */

package mp3;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Scanner;

import mp3.GetAudio.sound_file_format;

public class Parse {

	private static boolean INTERNAL_OPTS = false;

	Version ver;
	ID3Tag id3;
	Presets pre;

	public final void setModules(Version ver2, ID3Tag id32, Presets pre2) {
		this.ver = ver2;
		this.id3 = id32;
		this.pre = pre2;
	}

	public GetAudio.sound_file_format input_format;
	/**
	 * force byte swapping default=0
	 */
	public boolean swapbytes = false;
	/**
	 * Verbosity
	 */
	public int silent;
	/**
	 * Ignore errors in values passed for tags
	 */
	private boolean ignore_tag_errors;
	public boolean brhist;
	/**
	 * to use Frank's time status display
	 */
	public float update_interval;
	/**
	 * to adjust the number of samples truncated during decode
	 */
	public int mp3_delay;
	/**
	 * user specified the value of the mp3 encoder delay to assume for decoding
	 */
	public boolean mp3_delay_set;

	public boolean disable_wav_header;
	/**
	 * used by MP3
	 */
	public MP3Data mp3input_data = new MP3Data();
	/**
	 * print info whether waveform clips
	 */
	public boolean print_clipping_info;

	/**
	 * WAV signed
	 */
	public boolean in_signed = true;

	public ByteOrder in_endian = ByteOrder.LITTLE_ENDIAN;

	public int in_bitwidth = 16;

	/**
	 * possible text encodings
	 */
	private enum TextEncoding {
		/**
		 * bytes will be stored as-is into ID3 tags, which are Latin1/UCS2 per
		 * definition
		 */
		TENC_RAW, /**
		 * text will be converted from local encoding to Latin1, as
		 * ID3 needs it
		 */
		TENC_LATIN1, /**
		 * text will be converted from local encoding to UCS-2, as
		 * ID3v2 wants it
		 */
		TENC_UCS2
	}

	private boolean set_id3tag(final LameGlobalFlags gfp, final int type,
			final String str) {
		switch (type) {
		case 'a':
			id3.id3tag_set_artist(gfp, str);
			return false;
		case 't':
			id3.id3tag_set_title(gfp, str);
			return false;
		case 'l':
			id3.id3tag_set_album(gfp, str);
			return false;
		case 'g':
			id3.id3tag_set_genre(gfp, str);
			return false;
		case 'c':
			id3.id3tag_set_comment(gfp, str);
			return false;
		case 'n':
			id3.id3tag_set_track(gfp, str);
			return false;
		case 'y':
			id3.id3tag_set_year(gfp, str);
			return false;
		case 'v':
			id3.id3tag_set_fieldvalue(gfp, str);
			return false;
		}
		return false;
	}

	private boolean set_id3v2tag(final LameGlobalFlags gfp, final int type,
			final String str) {
		switch (type) {
		case 'a':
			id3.id3tag_set_textinfo_ucs2(gfp, "TPE1", str);
			return false;
		case 't':
			id3.id3tag_set_textinfo_ucs2(gfp, "TIT2", str);
			return false;
		case 'l':
			id3.id3tag_set_textinfo_ucs2(gfp, "TALB", str);
			return false;
		case 'g':
			id3.id3tag_set_textinfo_ucs2(gfp, "TCON", str);
			return false;
		case 'c':
			id3.id3tag_set_comment(gfp, null, null, str, 0);
			return false;
		case 'n':
			id3.id3tag_set_textinfo_ucs2(gfp, "TRCK", str);
			return false;
		}
		return false;
	}

	private boolean id3_tag(final LameGlobalFlags gfp, final int type,
			final TextEncoding enc, final String str) {
		String x = null;
		boolean result;
		switch (enc) {
		default:
		case TENC_RAW:
			x = str;
			break;
		case TENC_LATIN1:
			x = str/* toLatin1(str) */;
			break;
		case TENC_UCS2:
			x = str/* toUcs2(str) */;
			break;
		}
		switch (enc) {
		default:
		case TENC_RAW:
		case TENC_LATIN1:
			result = set_id3tag(gfp, type, x);
			break;
		case TENC_UCS2:
			result = set_id3v2tag(gfp, type, x);
			break;
		}
		return result;
	}

	/**
	 * PURPOSE: Writes version and license to the file specified by fp
	 */
	private void lame_version_print(final PrintStream fp) {
		final String b = ver.getLameOsBitness();
		final String v = ver.getLameVersion();
		final String u = ver.getLameUrl();
		final int lenb = b.length();
		final int lenv = v.length();
		final int lenu = u.length();
		/* line width of terminal in characters */
		final int lw = 80;
		/* static width of text */
		final int sw = 16;

		if (lw >= lenb + lenv + lenu + sw || lw < lenu + 2)
			/* text fits in 80 chars per line, or line even too small for url */
			if (lenb > 0)
				fp.printf("LAME %s version %s (%s)\n\n", b, v, u);
			else
				fp.printf("LAME version %s (%s)\n\n", v, u);
		else {
			/* text too long, wrap url into next line, right aligned */
			if (lenb > 0)
				fp.printf("LAME %s version %s\n%*s(%s)\n\n", b, v, lw - 2
						- lenu, "", u);
			else
				fp.printf("LAME version %s\n%*s(%s)\n\n", v, lw - 2 - lenu, "",
						u);
		}
    }

	private void print_license(final PrintStream fp) {
		/* print version & license */
		lame_version_print(fp);
		fp.printf("Can I use LAME in my commercial program?\n"
				+ "\n"
				+ "Yes, you can, under the restrictions of the LGPL.  In particular, you\n"
				+ "can include a compiled version of the LAME library (for example,\n"
				+ "lame.dll) with a commercial program.  Some notable requirements of\n"
				+ "the LGPL:\n" + "\n");
		fp.printf("1. In your program, you cannot include any source code from LAME, with\n"
				+ "   the exception of files whose only purpose is to describe the library\n"
				+ "   interface (such as lame.h).\n" + "\n");
		fp.printf("2. Any modifications of LAME must be released under the LGPL.\n"
				+ "   The LAME project (www.mp3dev.org) would appreciate being\n"
				+ "   notified of any modifications.\n" + "\n");
		fp.printf("3. You must give prominent notice that your program is:\n"
				+ "      A. using LAME (including version number)\n"
				+ "      B. LAME is under the LGPL\n"
				+ "      C. Provide a copy of the LGPL.  (the file COPYING contains the LGPL)\n"
				+ "      D. Provide a copy of LAME source, or a pointer where the LAME\n"
				+ "         source can be obtained (such as http://sourceforge.net/projects/jsidplay2/)\n"
				+ "   An example of prominent notice would be an \"About the LAME encoding engine\"\n"
				+ "   button in some pull down menu within the executable of your program.\n"
				+ "\n");
		fp.printf("4. If you determine that distribution of LAME requires a patent license,\n"
				+ "   you must obtain such license.\n" + "\n" + "\n");
		fp.printf("*** IMPORTANT NOTE ***\n"
				+ "\n"
				+ "The decoding functions provided in LAME use the mpglib decoding engine which\n"
				+ "is under the GPL.  They may not be used by any program not released under the\n"
				+ "GPL unless you obtain such permission from the MPG123 project (www.mpg123.de).\n"
				+ "\n");
	}

	/**
	 * PURPOSE: Writes command line syntax to the file specified by fp
	 */
	public final void usage(final PrintStream fp, final String ProgramName) {
		// print general syntax
		lame_version_print(fp);
		fp.printf(
				"usage: %s [options] <infile> [outfile]\n"
						+ "\n"
						+ "    <infile> and/or <outfile> can be \"-\", which means stdin/stdout.\n"
						+ "\n"
						+ "Try:\n"
						+ "     \"%s --help\"           for general usage information\n"
						+ " or:\n"
						+ "     \"%s --preset help\"    for information on suggested predefined settings\n"
						+ " or:\n"
						+ "     \"%s --longhelp\"\n"
						+ "  or \"%s -?\"              for a complete options list\n\n",
				ProgramName, ProgramName, ProgramName, ProgramName, ProgramName);
	}

	/**
	 * PURPOSE: Writes command line syntax to the file specified by fp but only
	 * the most important ones, to fit on a vt100 terminal
	 */
	private void short_help(final LameGlobalFlags gfp, final PrintStream fp,
			final String ProgramName) {
		/* print short syntax help */
		lame_version_print(fp);
		fp.printf(
				"usage: %s [options] <infile> [outfile]\n"
						+ "\n"
						+ "    <infile> and/or <outfile> can be \"-\", which means stdin/stdout.\n"
						+ "\n" + "RECOMMENDED:\n"
						+ "    lame -V 2 input.wav output.mp3\n" + "\n",
				ProgramName);
		fp.printf(
				"OPTIONS:\n"
						+ "    -b bitrate      set the bitrate, default 128 kbps\n"
						+ "    -h              higher quality, but a little slower.  Recommended.\n"
						+ "    -f              fast mode (lower quality)\n"
						+ "    -V n            quality setting for VBR.  default n=%d\n"
						+ "                    0=high quality,bigger files. 9=smaller files\n",
				gfp.VBR_q);
		fp.printf("    --preset type   type must be \"medium\", \"standard\", \"extreme\", \"insane\",\n"
				+ "                    or a value for an average desired bitrate and depending\n"
				+ "                    on the value specified, appropriate quality settings will\n"
				+ "                    be used.\n"
				+ "                    \"--preset help\" gives more info on these\n"
				+ "\n");
		fp.printf("    --longhelp      full list of options\n" + "\n"
				+ "    --license       print License information\n\n");
	}

	private void long_help(final LameGlobalFlags gfp, final PrintStream fp,
			final String ProgramName, final int lessmode) {
		// print long syntax help
		lame_version_print(fp);
		fp.printf(
				"usage: %s [options] <infile> [outfile]\n"
						+ "\n"
						+ "    <infile> and/or <outfile> can be \"-\", which means stdin/stdout.\n"
						+ "\n" + "RECOMMENDED:\n"
						+ "    lame -V2 input.wav output.mp3\n" + "\n",
				ProgramName);
		fp.printf("OPTIONS:\n"
				+ "  Input options:\n"
				+ "    --scale <arg>   scale input (multiply PCM data) by <arg>\n"
				+ "    --scale-l <arg> scale channel 0 (left) input (multiply PCM data) by <arg>\n"
				+ "    --scale-r <arg> scale channel 1 (right) input (multiply PCM data) by <arg>\n"
				+ "    --mp1input      input file is a MPEG Layer I   file\n"
				+ "    --mp2input      input file is a MPEG Layer II  file\n"
				+ "    --mp3input      input file is a MPEG Layer III file\n"
				+ "    --nogap <file1> <file2> <...>\n"
				+ "                    gapless encoding for a set of contiguous files\n"
				+ "    --nogapout <dir>\n"
				+ "                    output dir for gapless encoding (must precede --nogap)\n"
				+ "    --nogaptags     allow the use of VBR tags in gapless encoding\n");
		fp.printf("\n"
				+ "  Input options for RAW PCM:\n"
				+ "    -r              input is raw pcm\n"
				+ "    -x              force byte-swapping of input\n"
				+ "    -s sfreq        sampling frequency of input file (kHz) - default 44.1 kHz\n"
				+ "    --bitwidth w    input bit width is w (default 16)\n"
				+ "    --signed        input is signed (default)\n"
				+ "    --unsigned      input is unsigned\n"
				+ "    --little-endian input is little-endian (default)\n"
				+ "    --big-endian    input is big-endian\n");

		fp.printf("  Operational options:\n"
				+ "    -a              downmix from stereo to mono file for mono encoding\n"
				+ "    -m <mode>       (j)oint, (s)imple, (f)orce, (d)dual-mono, (m)ono\n"
				+ "                    default is (j) or (s) depending on bitrate\n"
				+ "                    joint  = joins the best possible of MS and LR stereo\n"
				+ "                    simple = force LR stereo on all frames\n"
				+ "                    force  = force MS stereo on all frames.\n"
				+ "    --preset type   type must be \"medium\", \"standard\", \"extreme\", \"insane\",\n"
				+ "                    or a value for an average desired bitrate and depending\n"
				+ "                    on the value specified, appropriate quality settings will\n"
				+ "                    be used.\n"
				+ "                    \"--preset help\" gives more info on these\n"
				+ "    --comp  <arg>   choose bitrate to achive a compression ratio of <arg>\n");
		fp.printf("    --replaygain-fast   compute RG fast but slightly inaccurately (default)\n"
				+ "    --replaygain-accurate   compute RG more accurately and find the peak sample\n"
				+ "    --noreplaygain  disable ReplayGain analysis\n"
				+ "    --clipdetect    enable --replaygain-accurate and print a message whether\n"
				+ "                    clipping occurs and how far the waveform is from full scale\n");
		fp.printf("    --freeformat    produce a free format bitstream\n"
				+ "    --decode        input=mp3 file, output=wav\n"
				+ "    -t              disable writing wav header when using --decode\n");

		fp.printf("  Verbosity:\n"
				+ "    --disptime <arg>print progress report every arg seconds\n"
				+ "    -S              don't print progress report, VBR histograms\n"
				+ "    --nohist        disable VBR histogram display\n"
				+ "    --silent        don't print anything on screen\n"
				+ "    --quiet         don't print anything on screen\n"
				+ "    --brief         print more useful information\n"
				+ "    --verbose       print a lot of useful information\n"
				+ "\n");
		fp.printf("  Noise shaping & psycho acoustic algorithms:\n"
				+ "    -q <arg>        <arg> = 0...9.  Default  -q 5 \n"
				+ "                    -q 0:  Highest quality, very slow \n"
				+ "                    -q 9:  Poor quality, but fast \n"
				+ "    -h              Same as -q 2.   Recommended.\n"
				+ "    -f              Same as -q 7.   Fast, ok quality\n");

		fp.printf("  CBR (constant bitrate, the default) options:\n"
				+ "    -b <bitrate>    set the bitrate in kbps, default 128 kbps\n"
				+ "    --cbr           enforce use of constant bitrate\n"
				+ "\n"
				+ "  ABR options:\n"
				+ "    --abr <bitrate> specify average bitrate desired (instead of quality)\n"
				+ "\n");
		fp.printf(
				"  VBR options:\n"
						+ "    -V n            quality setting for VBR.  default n=%d\n"
						+ "                    0=high quality,bigger files. 9=smaller files\n"
						+ "    -v              the same as -V 4\n"
						+ "    --vbr-old       use old variable bitrate (VBR) routine\n"
						+ "    --vbr-new       use new variable bitrate (VBR) routine (default)\n",
				gfp.VBR_q);
		fp.printf("    -b <bitrate>    specify minimum allowed bitrate, default  32 kbps\n"
				+ "    -B <bitrate>    specify maximum allowed bitrate, default 320 kbps\n"
				+ "    -F              strictly enforce the -b option, for use with players that\n"
				+ "                    do not support low bitrate mp3\n"
				+ "    -t              disable writing LAME Tag\n"
				+ "    -T              enable and force writing LAME Tag\n");

		fp.printf("  ATH related:\n"
				+ "    --noath         turns ATH down to a flat noise floor\n"
				+ "    --athshort      ignore GPSYCHO for short blocks, use ATH only\n"
				+ "    --athonly       ignore GPSYCHO completely, use ATH only\n"
				+ "    --athtype n     selects between different ATH types [0-4]\n"
				+ "    --athlower x    lowers ATH by x dB\n");
		fp.printf("    --athaa-type n  ATH auto adjust: 0 'no' else 'loudness based'\n"
				+
				/**
				 * OBSOLETE
				 * "    --athaa-loudapprox n   n=1 total energy or n=2 equal loudness curve\n"
				 */
				"    --athaa-sensitivity x  activation offset in -/+ dB for ATH auto-adjustment\n"
				+ "\n");

		fp.printf("  PSY related:\n"
				+ "    --short         use short blocks when appropriate\n"
				+ "    --noshort       do not use short blocks\n"
				+ "    --allshort      use only short blocks\n");
		fp.printf("    --temporal-masking x   x=0 disables, x=1 enables temporal masking effect\n"
				+ "    --nssafejoint   M/S switching criterion\n"
				+ "    --nsmsfix <arg> M/S switching tuning [effective 0-3.5]\n"
				+ "    --interch x     adjust inter-channel masking ratio\n"
				+ "    --ns-bass x     adjust masking for sfbs  0 -  6 (long)  0 -  5 (short)\n"
				+ "    --ns-alto x     adjust masking for sfbs  7 - 13 (long)  6 - 10 (short)\n"
				+ "    --ns-treble x   adjust masking for sfbs 14 - 21 (long) 11 - 12 (short)\n");
		fp.printf("    --ns-sfb21 x    change ns-treble by x dB for sfb21\n"
				+ "    --shortthreshold x,y  short block switching threshold,\n"
				+ "                          x for L/R/M channel, y for S channel\n"
				+ "  Noise Shaping related:\n"
				+ "    --substep n     use pseudo substep noise shaping method types 0-2\n");

		fp.printf("  experimental switches:\n"
				+ "    -X n[,m]        selects between different noise measurements\n"
				+ "                    n for long block, m for short. if m is omitted, m = n\n"
				+ "    -Y              lets LAME ignore noise in sfb21, like in CBR\n"
				+ "    -Z [n]          currently no effects\n");

		fp.printf("  MP3 header/stream options:\n"
				+ "    -e <emp>        de-emphasis n/5/c  (obsolete)\n"
				+ "    -c              mark as copyright\n"
				+ "    -o              mark as non-original\n"
				+ "    -p              error protection.  adds 16 bit checksum to every frame\n"
				+ "                    (the checksum is computed correctly)\n"
				+ "    --nores         disable the bit reservoir\n"
				+ "    --strictly-enforce-ISO   comply as much as possible to ISO MPEG spec\n"
				+ "\n");
		fp.printf("  Filter options:\n"
				+ "  --lowpass <freq>        frequency(kHz), lowpass filter cutoff above freq\n"
				+ "  --lowpass-width <freq>  frequency(kHz) - default 15%% of lowpass freq\n"
				+ "  --highpass <freq>       frequency(kHz), highpass filter cutoff below freq\n"
				+ "  --highpass-width <freq> frequency(kHz) - default 15%% of highpass freq\n");
		fp.printf("  --resample <sfreq>  sampling frequency of output file(kHz)- default=automatic\n");

		fp.printf("  ID3 tag options:\n"
				+ "    --tt <title>    audio/song title (max 30 chars for version 1 tag)\n"
				+ "    --ta <artist>   audio/song artist (max 30 chars for version 1 tag)\n"
				+ "    --tl <album>    audio/song album (max 30 chars for version 1 tag)\n"
				+ "    --ty <year>     audio/song year of issue (1 to 9999)\n"
				+ "    --tc <comment>  user-defined text (max 30 chars for v1 tag, 28 for v1.1)\n"
				+ "    --tn <track[/total]>   audio/song track number and (optionally) the total\n"
				+ "                           number of tracks on the original recording. (track\n"
				+ "                           and total each 1 to 255. just the track number\n"
				+ "                           creates v1.1 tag, providing a total forces v2.0).\n"
				+ "    --tg <genre>    audio/song genre (name or number in list)\n"
				+ "    --ti <file>     audio/song albumArt (jpeg/png/gif file, 128KB max, v2.3)\n"
				+ "    --tv <id=value> user-defined frame specified by id and value (v2.3 tag)\n");
		fp.printf("    --add-id3v2     force addition of version 2 tag\n"
				+ "    --id3v1-only    add only a version 1 tag\n"
				+ "    --id3v2-only    add only a version 2 tag\n"
				+ "    --space-id3v1   pad version 1 tag with spaces instead of nulls\n"
				+ "    --pad-id3v2     same as '--pad-id3v2-size 128'\n"
				+ "    --pad-id3v2-size <value> adds version 2 tag, pad with extra <value> bytes\n"
				+ "    --genre-list    print alphabetically sorted ID3 genre list and exit\n"
				+ "    --ignore-tag-errors  ignore errors in values passed for tags\n"
				+ "\n");
		fp.printf("    Note: A version 2 tag will NOT be added unless one of the input fields\n"
				+ "    won't fit in a version 1 tag (e.g. the title string is longer than 30\n"
				+ "    characters), or the '--add-id3v2' or '--id3v2-only' options are used,\n"
				+ "    or output is redirected to stdout.\n"
				+ "\nMisc:\n    --license       print License information\n\n");

		display_bitrates(fp);
	}

	public final void display_bitrates(final PrintStream fp) {
		display_bitrate(fp, "1", 1, 1);
		display_bitrate(fp, "2", 2, 0);
		display_bitrate(fp, "2.5", 4, 0);
		fp.println();
	}

	private void display_bitrate(final PrintStream fp, final String version,
			final int d, final int indx) {
		int nBitrates = 14;
		if (d == 4)
			nBitrates = 8;

		fp.printf(
				"\nMPEG-%-3s layer III sample frequencies (kHz):  %2d  %2d  %g\n"
						+ "bitrates (kbps):", version, 32 / d, 48 / d, 44.1 / d);
		for (int i = 1; i <= nBitrates; i++)
			fp.printf(" %2d", Tables.bitrate_table[indx][i]);
		fp.println();
	}

	/**
	 * PURPOSE: Writes presetting info to #stdout#
	 */
	private void presets_longinfo_dm(final PrintStream msgfp) {
		msgfp.printf("\n"
				+ "The --preset switches are aliases over LAME settings.\n"
				+ "\n" + "\n");
		msgfp.printf("To activate these presets:\n" + "\n"
				+ "   For VBR modes (generally highest quality):\n" + "\n");
		msgfp.printf("     \"--preset medium\" This preset should provide near transparency\n"
				+ "                             to most people on most music.\n"
				+ "\n"
				+ "     \"--preset standard\" This preset should generally be transparent\n"
				+ "                             to most people on most music and is already\n"
				+ "                             quite high in quality.\n"
				+ "\n");
		msgfp.printf("     \"--preset extreme\" If you have extremely good hearing and similar\n"
				+ "                             equipment, this preset will generally provide\n"
				+ "                             slightly higher quality than the \"standard\"\n"
				+ "                             mode.\n" + "\n");
		msgfp.printf("   For CBR 320kbps (highest quality possible from the --preset switches):\n"
				+ "\n"
				+ "     \"--preset insane\"  This preset will usually be overkill for most\n"
				+ "                             people and most situations, but if you must\n"
				+ "                             have the absolute highest quality with no\n"
				+ "                             regard to filesize, this is the way to go.\n"
				+ "\n");
		msgfp.printf("   For ABR modes (high quality per given bitrate but not as high as VBR):\n"
				+ "\n"
				+ "     \"--preset <kbps>\"  Using this preset will usually give you good\n"
				+ "                             quality at a specified bitrate. Depending on the\n"
				+ "                             bitrate entered, this preset will determine the\n");
		msgfp.printf("                             optimal settings for that particular situation.\n"
				+ "                             While this approach works, it is not nearly as\n"
				+ "                             flexible as VBR, and usually will not attain the\n"
				+ "                             same level of quality as VBR at higher bitrates.\n"
				+ "\n");
		msgfp.printf("The following options are also available for the corresponding profiles:\n"
				+ "\n"
				+ "   <fast>        standard\n"
				+ "   <fast>        extreme\n"
				+ "                 insane\n"
				+ "   <cbr> (ABR Mode) - The ABR Mode is implied. To use it,\n"
				+ "                      simply specify a bitrate. For example:\n"
				+ "                      \"--preset 185\" activates this\n"
				+ "                      preset and uses 185 as an average kbps.\n"
				+ "\n");
		msgfp.printf("   \"fast\" - Enables the fast VBR mode for a particular profile.\n"
				+ "\n");
		msgfp.printf("   \"cbr\"  - If you use the ABR mode (read above) with a significant\n"
				+ "            bitrate such as 80, 96, 112, 128, 160, 192, 224, 256, 320,\n"
				+ "            you can use the \"cbr\" option to force CBR mode encoding\n"
				+ "            instead of the standard abr mode. ABR does provide higher\n"
				+ "            quality but CBR may be useful in situations such as when\n"
				+ "            streaming an mp3 over the internet may be important.\n"
				+ "\n");
		msgfp.printf("    For example:\n" + "\n"
				+ "    \"--preset fast standard <input file> <output file>\"\n"
				+ " or \"--preset cbr 192 <input file> <output file>\"\n"
				+ " or \"--preset 172 <input file> <output file>\"\n"
				+ " or \"--preset extreme <input file> <output file>\"\n"
				+ "\n" + "\n");
		msgfp.printf("A few aliases are also available for ABR mode:\n"
				+ "phone => 16kbps/mono        phon+/lw/mw-eu/sw => 24kbps/mono\n"
				+ "mw-us => 40kbps/mono        voice => 56kbps/mono\n"
				+ "fm/radio/tape => 112kbps    hifi => 160kbps\n"
				+ "cd => 192kbps               studio => 256kbps\n");
	}

	private int presets_set(final LameGlobalFlags gfp, final int fast,
			final int cbr, String preset_name, final String ProgramName) {
		int mono = 0;

		if ((preset_name.equals("help")) && (fast < 1) && (cbr < 1)) {
			lame_version_print(System.out);
			presets_longinfo_dm(System.out);
			return -1;
		}

		/* aliases for compatibility with old presets */

		if (preset_name.equals("phone")) {
			preset_name = "16";
			mono = 1;
		}
		if ((preset_name.equals("phon+")) || (preset_name.equals("lw"))
				|| (preset_name.equals("mw-eu")) || (preset_name.equals("sw"))) {
			preset_name = "24";
			mono = 1;
		}
		if (preset_name.equals("mw-us")) {
			preset_name = "40";
			mono = 1;
		}
		if (preset_name.equals("voice")) {
			preset_name = "56";
			mono = 1;
		}
		if (preset_name.equals("fm")) {
			preset_name = "112";
		}
		if ((preset_name.equals("radio")) || (preset_name.equals("tape"))) {
			preset_name = "112";
		}
		if (preset_name.equals("hifi")) {
			preset_name = "160";
		}
		if (preset_name.equals("cd")) {
			preset_name = "192";
		}
		if (preset_name.equals("studio")) {
			preset_name = "256";
		}

		if (preset_name.equals("medium")) {
			pre.lame_set_VBR_q(gfp, 4);
			if (fast > 0) {
				gfp.VBR = VbrMode.vbr_mtrh;
			} else {
				gfp.VBR = VbrMode.vbr_rh;
			}
			return 0;
		}

		if (preset_name.equals("standard")) {
			pre.lame_set_VBR_q(gfp, 2);
			if (fast > 0) {
				gfp.VBR = VbrMode.vbr_mtrh;
			} else {
				gfp.VBR = VbrMode.vbr_rh;
			}
			return 0;
		}

		else if (preset_name.equals("extreme")) {
			pre.lame_set_VBR_q(gfp, 0);
			if (fast > 0) {
				gfp.VBR = VbrMode.vbr_mtrh;
			} else {
				gfp.VBR = VbrMode.vbr_rh;
			}
			return 0;
		}

		else if ((preset_name.equals("insane")) && (fast < 1)) {

			gfp.preset = Lame.INSANE;
			pre.apply_preset(gfp, Lame.INSANE, 1);

			return 0;
		}

		/* Generic ABR Preset */
		if (((Integer.valueOf(preset_name)) > 0) && (fast < 1)) {
			if ((Integer.valueOf(preset_name)) >= 8
					&& (Integer.valueOf(preset_name)) <= 320) {
				gfp.preset = Integer.valueOf(preset_name);
				pre.apply_preset(gfp, Integer.valueOf(preset_name), 1);

				if (cbr == 1)
					gfp.VBR = VbrMode.vbr_off;

				if (mono == 1) {
					gfp.mode = MPEGMode.MONO;
				}

				return 0;

			} else {
				lame_version_print(System.err);
				System.err
						.printf("Error: The bitrate specified is out of the valid range for this preset\n"
								+ "\n"
								+ "When using this mode you must enter a value between \"32\" and \"320\"\n"
								+ "\n"
								+ "For further information try: \"%s --preset help\"\n",
								ProgramName);
				return -1;
			}
		}

		lame_version_print(System.err);
		System.err
				.printf("Error: You did not enter a valid profile and/or options with --preset\n"
						+ "\n"
						+ "Available profiles are:\n"
						+ "\n"
						+ "   <fast>        medium\n"
						+ "   <fast>        standard\n"
						+ "   <fast>        extreme\n"
						+ "                 insane\n"
						+ "          <cbr> (ABR Mode) - The ABR Mode is implied. To use it,\n"
						+ "                             simply specify a bitrate. For example:\n"
						+ "                             \"--preset 185\" activates this\n"
						+ "                             preset and uses 185 as an average kbps.\n"
						+ "\n");
		System.err
				.printf("    Some examples:\n"
						+ "\n"
						+ " or \"%s --preset fast standard <input file> <output file>\"\n"
						+ " or \"%s --preset cbr 192 <input file> <output file>\"\n"
						+ " or \"%s --preset 172 <input file> <output file>\"\n"
						+ " or \"%s --preset extreme <input file> <output file>\"\n"
						+ "\n"
						+ "For further information try: \"%s --preset help\"\n",
						ProgramName, ProgramName, ProgramName, ProgramName,
						ProgramName);
		return -1;
	}

	/**
	 * LAME is a simple frontend which just uses the file extension to determine
	 * the file type. Trying to analyze the file contents is well beyond the
	 * scope of LAME and should not be added.
	 */
	private sound_file_format filename_to_type(String FileName) {
		int len = FileName.length();

		if (len < 4)
			return sound_file_format.sf_unknown;

		FileName = FileName.substring(len - 4);
		if (FileName.equalsIgnoreCase(".mpg"))
			return sound_file_format.sf_mp123;
		if (FileName.equalsIgnoreCase(".mp1"))
			return sound_file_format.sf_mp123;
		if (FileName.equalsIgnoreCase(".mp2"))
			return sound_file_format.sf_mp123;
		if (FileName.equalsIgnoreCase(".mp3"))
			return sound_file_format.sf_mp123;
		if (FileName.equalsIgnoreCase(".wav"))
			return sound_file_format.sf_wave;
		if (FileName.equalsIgnoreCase(".aif"))
			return sound_file_format.sf_aiff;
		if (FileName.equalsIgnoreCase(".raw"))
			return sound_file_format.sf_raw;
		if (FileName.equalsIgnoreCase(".ogg"))
			return sound_file_format.sf_ogg;
		return GetAudio.sound_file_format.sf_unknown;
	}

	private int resample_rate(double freq) {
		if (freq >= 1.e3)
			freq *= 1.e-3;

		switch ((int) freq) {
		case 8:
			return 8000;
		case 11:
			return 11025;
		case 12:
			return 12000;
		case 16:
			return 16000;
		case 22:
			return 22050;
		case 24:
			return 24000;
		case 32:
			return 32000;
		case 44:
			return 44100;
		case 48:
			return 48000;
		default:
			System.err.printf("Illegal resample frequency: %.3f kHz\n", freq);
			return 0;
		}
	}

	private int set_id3_albumart(final LameGlobalFlags gfp,
			final String file_name) {
		int ret = -1;
		RandomAccessFile fpi = null;

		if (file_name == null) {
			return 0;
		}
		try {
			fpi = new RandomAccessFile(file_name, "r");
			try {
				int size = (int) (fpi.length() & Integer.MAX_VALUE);
				byte[] albumart = new byte[size];
				fpi.readFully(albumart);
				ret = id3.id3tag_set_albumart(gfp, albumart, size) ? 0 : 4;
			} catch (IOException e) {
				ret = 3;
			} finally {
				try {
					fpi.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		} catch (FileNotFoundException e1) {
			ret = 1;
		}
		switch (ret) {
		case 1:
			System.err.printf("Could not find: '%s'.\n", file_name);
			break;
		case 2:
			System.err
					.printf("Insufficient memory for reading the albumart.\n");
			break;
		case 3:
			System.err.printf("Read error: '%s'.\n", file_name);
			break;
		case 4:
			System.err
					.printf("Unsupported image: '%s'.\nSpecify JPEG/PNG/GIF image (128KB maximum)\n",
							file_name);
			break;
		default:
			break;
		}
		return ret;
	}

	private enum ID3TAG_MODE {
		ID3TAG_MODE_DEFAULT, ID3TAG_MODE_V1_ONLY, ID3TAG_MODE_V2_ONLY
	};

	public static class NoGap {
		int num_nogap;
	}

	public int parse_args(final LameGlobalFlags gfp,
			final ArrayList<String> argv, final StringBuilder inPath,
			final StringBuilder outPath, final String[] nogap_inPath,
			final NoGap ng) {
		/* set to 1 if we parse an input file name */
		int input_file = 0;
		int autoconvert = 0;
		double val;
		int nogap = 0;
		/* set to 1 to use VBR tags in NOGAP mode */
		int nogap_tags = 0;
		final String ProgramName = "lame";
		int count_nogap = 0;
		/* is RG explicitly disabled by the user */
		int noreplaygain = 0;
		ID3TAG_MODE id3tag_mode = ID3TAG_MODE.ID3TAG_MODE_DEFAULT;

		inPath.setLength(0);
		outPath.setLength(0);
		/* turn on display options. user settings may turn them off below */
		silent = 0;
		ignore_tag_errors = false;
		brhist = true;
		mp3_delay = 0;
		mp3_delay_set = false;
		print_clipping_info = false;
		disable_wav_header = false;
		id3.id3tag_init(gfp);

		/* process args */
		for (int i = 0; i < argv.size(); i++) {
			char c;
			String token;
			int tokenPos = 0;
			String arg;
			String nextArg;
			int argUsed;

			token = argv.get(i);
			if (token.charAt(tokenPos++) == '-') {
				argUsed = 0;
				nextArg = i + 1 < argv.size() ? argv.get(i + 1) : "";

				if (token.length() - tokenPos == 0) {
					/* The user wants to use stdin and/or stdout. */
					input_file = 1;
					if (inPath.length() == 0) {
						inPath.setLength(0);
						inPath.append(argv.get(i));
					} else if (outPath.length() == 0) {
						outPath.setLength(0);
						outPath.append(argv.get(i));
					}
				}
				if (token.charAt(tokenPos) == '-') { /* GNU style */
					tokenPos++;

					if (token.substring(tokenPos).equalsIgnoreCase("resample")) {
						argUsed = 1;
						gfp.out_samplerate = resample_rate(Double
								.parseDouble(nextArg));

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"vbr-old")) {
						gfp.VBR = VbrMode.vbr_rh;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"vbr-new")) {
						gfp.VBR = VbrMode.vbr_mtrh;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"vbr-mtrh")) {
						gfp.VBR = VbrMode.vbr_mtrh;

					} else if (token.substring(tokenPos)
							.equalsIgnoreCase("cbr")) {
						gfp.VBR = VbrMode.vbr_off;

					} else if (token.substring(tokenPos)
							.equalsIgnoreCase("abr")) {
						argUsed = 1;
						gfp.VBR = VbrMode.vbr_abr;
						gfp.VBR_mean_bitrate_kbps = Integer.valueOf(nextArg);
						/*
						 * values larger than 8000 are bps (like Fraunhofer), so
						 * it's strange to get 320000 bps MP3 when specifying
						 * 8000 bps MP3
						 */
						if (gfp.VBR_mean_bitrate_kbps >= 8000)
							gfp.VBR_mean_bitrate_kbps = (gfp.VBR_mean_bitrate_kbps + 500) / 1000;

						gfp.VBR_mean_bitrate_kbps = Math.min(
								gfp.VBR_mean_bitrate_kbps, 320);
						gfp.VBR_mean_bitrate_kbps = Math.max(
								gfp.VBR_mean_bitrate_kbps, 8);

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"r3mix")) {
						gfp.preset = Lame.R3MIX;
						pre.apply_preset(gfp, Lame.R3MIX, 1);

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"bitwidth")) {
						argUsed = 1;
						in_bitwidth = Integer.valueOf(nextArg);

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"signed")) {
						in_signed = true;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"unsigned")) {
						in_signed = false;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"little-endian")) {
						in_endian = ByteOrder.LITTLE_ENDIAN;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"big-endian")) {
						in_endian = ByteOrder.BIG_ENDIAN;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"mp1input")) {
						input_format = GetAudio.sound_file_format.sf_mp1;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"mp2input")) {
						input_format = GetAudio.sound_file_format.sf_mp2;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"mp3input")) {
						input_format = GetAudio.sound_file_format.sf_mp3;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"ogginput")) {
						System.err
								.printf("sorry, vorbis support in LAME is deprecated.\n");
						return -1;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"phone")) {
						if (presets_set(gfp, 0, 0, token, ProgramName) < 0)
							return -1;
						System.err
								.printf("Warning: --phone is deprecated, use --preset phone instead!");

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"voice")) {
						if (presets_set(gfp, 0, 0, token, ProgramName) < 0)
							return -1;
						System.err
								.printf("Warning: --voice is deprecated, use --preset voice instead!");

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"noshort")) {
						gfp.short_blocks = ShortBlock.short_block_dispensed;

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"short")) {
						gfp.short_blocks = ShortBlock.short_block_allowed;

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"allshort")) {
						gfp.short_blocks = ShortBlock.short_block_forced;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"decode")) {
						gfp.decode_only = true;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"decode-mp3delay")) {
						mp3_delay = Integer.valueOf(nextArg);
						mp3_delay_set = true;
						argUsed = 1;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"nores")) {
						gfp.disable_reservoir = true;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"strictly-enforce-ISO")) {
						gfp.strict_ISO = true;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"scale")) {
						argUsed = 1;
						gfp.scale = (float) Double.parseDouble(nextArg);

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"scale-l")) {
						argUsed = 1;
						gfp.scale_left = (float) Double.parseDouble(nextArg);

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"scale-r")) {
						argUsed = 1;
						gfp.scale_right = (float) Double.parseDouble(nextArg);

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"freeformat")) {
						gfp.free_format = true;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"replaygain-fast")) {
						gfp.findReplayGain = true;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"replaygain-accurate")) {
						gfp.decode_on_the_fly = true;
						gfp.findReplayGain = true;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"noreplaygain")) {
						noreplaygain = 1;
						gfp.findReplayGain = false;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"clipdetect")) {
						print_clipping_info = true;
						gfp.decode_on_the_fly = true;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"nohist")) {
						brhist = false;

						/* options for ID3 tag */
					} else if (token.substring(tokenPos).equalsIgnoreCase("tt")) {
						argUsed = 1;
						id3_tag(gfp, 't', TextEncoding.TENC_RAW, nextArg);

					} else if (token.substring(tokenPos).equalsIgnoreCase("ta")) {
						argUsed = 1;
						id3_tag(gfp, 'a', TextEncoding.TENC_RAW, nextArg);

					} else if (token.substring(tokenPos).equalsIgnoreCase("tl")) {
						argUsed = 1;
						id3_tag(gfp, 'l', TextEncoding.TENC_RAW, nextArg);

					} else if (token.substring(tokenPos).equalsIgnoreCase("ty")) {
						argUsed = 1;
						id3_tag(gfp, 'y', TextEncoding.TENC_RAW, nextArg);

					} else if (token.substring(tokenPos).equalsIgnoreCase("tc")) {
						argUsed = 1;
						id3_tag(gfp, 'c', TextEncoding.TENC_RAW, nextArg);

					} else if (token.substring(tokenPos).equalsIgnoreCase("tn")) {
						boolean ret = id3_tag(gfp, 'n', TextEncoding.TENC_RAW,
								nextArg);
						argUsed = 1;
						if (ret) {
							if (!ignore_tag_errors) {
								if (id3tag_mode == ID3TAG_MODE.ID3TAG_MODE_V1_ONLY) {
									System.err
											.printf("The track number has to be between 1 and 255 for ID3v1.\n");
									return -1;
								} else if (id3tag_mode == ID3TAG_MODE.ID3TAG_MODE_V2_ONLY) {
									/*
									 * track will be stored as-is in ID3v2 case,
									 * so no problem here
									 */
								} else {
									if (silent < 10) {
										System.err
												.printf("The track number has to be between 1 and 255 for ID3v1, ignored for ID3v1.\n");
									}
								}
							}
						}

					} else if (token.substring(tokenPos).equalsIgnoreCase("tg")) {
						id3_tag(gfp, 'g', TextEncoding.TENC_RAW, nextArg);
						argUsed = 1;

					} else if (token.substring(tokenPos).equalsIgnoreCase("tv")) {
						argUsed = 1;
						if (id3_tag(gfp, 'v', TextEncoding.TENC_RAW, nextArg)) {
							if (silent < 10) {
								System.err.printf(
										"Invalid field value: '%s'. Ignored\n",
										nextArg);
							}
						}

					} else if (token.substring(tokenPos).equalsIgnoreCase("ti")) {
						argUsed = 1;
						if (set_id3_albumart(gfp, nextArg) != 0) {
							if (!ignore_tag_errors) {
								return -1;
							}
						}

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"ignore-tag-errors")) {
						ignore_tag_errors = true;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"add-id3v2")) {
						id3.id3tag_add_v2(gfp);

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"id3v1-only")) {
						id3.id3tag_v1_only(gfp);
						id3tag_mode = ID3TAG_MODE.ID3TAG_MODE_V1_ONLY;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"id3v2-only")) {
						id3.id3tag_v2_only(gfp);
						id3tag_mode = ID3TAG_MODE.ID3TAG_MODE_V2_ONLY;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"space-id3v1")) {
						id3.id3tag_space_v1(gfp);

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"pad-id3v2")) {
						id3.id3tag_pad_v2(gfp);

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"pad-id3v2-size")) {
						int n = Integer.valueOf(nextArg);
						n = n <= 128000 ? n : 128000;
						n = n >= 0 ? n : 0;
						id3.id3tag_set_pad(gfp, n);
						argUsed = 1;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"genre-list")) {
						id3.id3tag_genre_list(new GenreListHandler() {

							public void genre_list_handler(int num, String name) {
								System.out.printf("%3d %s\n", num, name);
							}

						});
						return -2;

						// XXX Unsupported: some experimental switches for
						// setting ID3 tags

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"lowpass")) {
						val = Double.parseDouble(nextArg);
						argUsed = 1;
						if (val < 0) {
							gfp.lowpassfreq = -1;
						} else {
							/* useful are 0.001 kHz...50 kHz, 50 Hz...50000 Hz */
							if (val < 0.001 || val > 50000.) {
								System.err
										.printf("Must specify lowpass with --lowpass freq, freq >= 0.001 kHz\n");
								return -1;
							}
							gfp.lowpassfreq = (int) (val
									* (val < 50. ? 1.e3 : 1.e0) + 0.5);
						}

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"lowpass-width")) {
						val = Double.parseDouble(nextArg);
						argUsed = 1;
						/* useful are 0.001 kHz...16 kHz, 16 Hz...50000 Hz */
						if (val < 0.001 || val > 50000.) {
							System.err
									.printf("Must specify lowpass width with --lowpass-width freq, freq >= 0.001 kHz\n");
							return -1;
						}
						gfp.lowpassfreq = (int) (val
								* (val < 16. ? 1.e3 : 1.e0) + 0.5);

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"highpass")) {
						val = Double.parseDouble(nextArg);
						argUsed = 1;
						if (val < 0.0) {
							gfp.highpassfreq = -1;
						} else {
							/* useful are 0.001 kHz...16 kHz, 16 Hz...50000 Hz */
							if (val < 0.001 || val > 50000.) {
								System.err
										.printf("Must specify highpass with --highpass freq, freq >= 0.001 kHz\n");
								return -1;
							}
							gfp.highpassfreq = (int) (val
									* (val < 16. ? 1.e3 : 1.e0) + 0.5);
						}

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"highpass-width")) {
						val = Double.parseDouble(nextArg);
						argUsed = 1;
						/* useful are 0.001 kHz...16 kHz, 16 Hz...50000 Hz */
						if (val < 0.001 || val > 50000.) {
							System.err
									.printf("Must specify highpass width with --highpass-width freq, freq >= 0.001 kHz\n");
							return -1;
						}
						gfp.highpasswidth = (int) val;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"comp")) {
						argUsed = 1;
						val = Double.parseDouble(nextArg);
						if (val < 1.0) {
							System.err
									.printf("Must specify compression ratio >= 1.0\n");
							return -1;
						}
						gfp.compression_ratio = (float) val;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"notemp")) {
						gfp.useTemporal = false;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"interch")) {
						argUsed = 1;
						gfp.interChRatio = (float) Double.parseDouble(nextArg);

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"temporal-masking")) {
						argUsed = 1;
						gfp.useTemporal = Integer.valueOf(nextArg) != 0;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"nssafejoint")) {
						gfp.exp_nspsytune = gfp.exp_nspsytune | 2;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"nsmsfix")) {
						argUsed = 1;
						gfp.msfix = (float) Double.parseDouble(nextArg);

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"ns-bass")) {
						argUsed = 1;
						{
							double d;
							int k;
							d = Double.parseDouble(nextArg);
							k = (int) (d * 4);
							if (k < -32)
								k = -32;
							if (k > 31)
								k = 31;
							if (k < 0)
								k += 64;
							gfp.exp_nspsytune = gfp.exp_nspsytune | (k << 2);
						}

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"ns-alto")) {
						argUsed = 1;
						{
							double d;
							int k;
							d = Double.parseDouble(nextArg);
							k = (int) (d * 4);
							if (k < -32)
								k = -32;
							if (k > 31)
								k = 31;
							if (k < 0)
								k += 64;
							gfp.exp_nspsytune = gfp.exp_nspsytune | (k << 8);
						}

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"ns-treble")) {
						argUsed = 1;
						{
							double d;
							int k;
							d = Double.parseDouble(nextArg);
							k = (int) (d * 4);
							if (k < -32)
								k = -32;
							if (k > 31)
								k = 31;
							if (k < 0)
								k += 64;
							gfp.exp_nspsytune = gfp.exp_nspsytune | (k << 14);
						}

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"ns-sfb21")) {
						/*
						 * to be compatible with Naoki's original code, ns-sfb21
						 * specifies how to change ns-treble for sfb21
						 */
						argUsed = 1;
						{
							double d;
							int k;
							d = Double.parseDouble(nextArg);
							k = (int) (d * 4);
							if (k < -32)
								k = -32;
							if (k > 31)
								k = 31;
							if (k < 0)
								k += 64;
							gfp.exp_nspsytune = gfp.exp_nspsytune | (k << 20);
						}

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"nspsytune2")) {

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"quiet")
							|| token.substring(tokenPos).equalsIgnoreCase(
									"silent")) {
						silent = 10; /* on a scale from 1 to 10 be very silent */

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"brief")) {
						silent = -5; /* print few info on screen */

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"verbose")) {
						silent = -10; /* print a lot on screen */

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"version")
							|| token.substring(tokenPos).equalsIgnoreCase(
									"license")) {
						print_license(System.out);
						return -2;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"help")
							|| token.substring(tokenPos).equalsIgnoreCase(
									"usage")) {
						short_help(gfp, System.out, ProgramName);
						return -2;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"longhelp")) {
						long_help(gfp, System.out, ProgramName, 0 /* lessmode=NO */);
						return -2;

					} else if (token.substring(tokenPos).equalsIgnoreCase("?")) {
						long_help(gfp, System.out, ProgramName, 1 /* lessmode=YES */);
						return -2;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"preset")
							|| token.substring(tokenPos).equalsIgnoreCase(
									"alt-preset")) {
						argUsed = 1;
						{
							int fast = 0, cbr = 0;

							while ((nextArg.equals("fast"))
									|| (nextArg.equals("cbr"))) {

								if ((nextArg.equals("fast")) && (fast < 1))
									fast = 1;
								if ((nextArg.equals("cbr")) && (cbr < 1))
									cbr = 1;

								argUsed++;
								nextArg = i + argUsed < argv.size() ? argv
										.get(i + argUsed) : "";
							}

							if (presets_set(gfp, fast, cbr, nextArg,
									ProgramName) < 0)
								return -1;
						}

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"disptime")) {
						argUsed = 1;
						update_interval = (float) Double.parseDouble(nextArg);

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"nogaptags")) {
						nogap_tags = 1;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"nogapout")) {
						outPath.setLength(0);
						outPath.append(nextArg);
						argUsed = 1;

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"nogap")) {
						nogap = 1;

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"tune")) { /* without helptext */
						argUsed = 1;
						{
							gfp.tune_value_a = (float) Double
									.parseDouble(nextArg);
							gfp.tune = true;
						}

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"shortthreshold")) {
						{
							float x, y;
							Scanner sc = new Scanner(nextArg);
							x = sc.nextFloat();
							if (!sc.hasNext()) {
								y = x;
							} else {
								sc.nextByte();
								y = sc.nextFloat();
							}
							argUsed = 1;
							gfp.internal_flags.nsPsy.attackthre = x;
							gfp.internal_flags.nsPsy.attackthre_s = y;
						}

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"maskingadjust")) { /* without helptext */
						argUsed = 1;
						gfp.maskingadjust = (float) Double.parseDouble(nextArg);

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"maskingadjustshort")) { /* without helptext */
						argUsed = 1;
						gfp.maskingadjust_short = (float) Double
								.parseDouble(nextArg);

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"athcurve")) { /* without helptext */
						argUsed = 1;
						gfp.ATHcurve = (float) Double.parseDouble(nextArg);

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"no-preset-tune")) { /* without helptext */

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"substep")) {
						argUsed = 1;
						gfp.internal_flags.substep_shaping = Integer
								.valueOf(nextArg);

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"sbgain")) { /* without helptext */
						argUsed = 1;
						gfp.internal_flags.subblock_gain = Integer
								.valueOf(nextArg);

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"sfscale")) { /* without helptext */
						gfp.internal_flags.noise_shaping = 2;

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"noath")) {
						gfp.noATH = true;

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"athonly")) {
						gfp.ATHonly = true;

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"athshort")) {
						gfp.ATHshort = true;

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"athlower")) {
						argUsed = 1;
						gfp.ATHlower = -(float) Double.parseDouble(nextArg) / 10.0f;

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"athtype")) {
						argUsed = 1;
						gfp.ATHtype = Integer.valueOf(nextArg);

					} else if (INTERNAL_OPTS
							&& token.substring(tokenPos).equalsIgnoreCase(
									"athaa-type")) {
						/*
						 * switch for developing, no DOCU
						 */
						argUsed = 1;
						/*
						 * once was 1:Gaby, 2:Robert, 3:Jon, else:off
						 */
						gfp.athaa_type = Integer.valueOf(nextArg);
						/*
						 * now: 0:off else:Jon
						 */

					} else if (token.substring(tokenPos).equalsIgnoreCase(
							"athaa-sensitivity")) {
						argUsed = 1;
						gfp.athaa_sensitivity = (float) Double
								.parseDouble(nextArg);

					} else {
						{
							System.err.printf("%s: unrecognized option --%s\n",
									ProgramName, token);
							return -1;
						}
					}
					i += argUsed;

				} else {
					while (tokenPos < token.length()) {
						c = token.charAt(tokenPos++);

						arg = tokenPos < token.length() ? token : nextArg;
						switch (c) {
						case 'm':
							argUsed = 1;

							switch (arg.charAt(0)) {
							case 's':
								gfp.mode = MPEGMode.STEREO;
								break;
							case 'd':
								gfp.mode = MPEGMode.DUAL_CHANNEL;
								break;
							case 'f':
								gfp.force_ms = true;
								/* FALLTHROUGH */
							case 'j':
								gfp.mode = MPEGMode.JOINT_STEREO;
								break;
							case 'm':
								gfp.mode = MPEGMode.MONO;
								break;
							case 'a':
								gfp.mode = MPEGMode.JOINT_STEREO;
								break;
							default:
								System.err
										.printf("%s: -m mode must be s/d/j/f/m not %s\n",
												ProgramName, arg);
								return -1;
							}
							break;

						case 'V':
							argUsed = 1;
							/* to change VBR default look in lame.h */
							if (gfp.VBR == VbrMode.vbr_off) {
								gfp.VBR_q = VbrMode.vbr_default.ordinal();
								gfp.VBR_q_frac = 0;
							}
							gfp.VBR_q = (int) (float) Double.parseDouble(arg);
							gfp.VBR_q_frac = (float) Double.parseDouble(arg)
									- gfp.VBR_q;
							break;
						case 'v':
							/* to change VBR default look in lame.h */
							if (gfp.VBR == VbrMode.vbr_off)
								gfp.VBR = VbrMode.vbr_mtrh;
							break;

						case 'q':
							argUsed = 1;
							{
								int tmp_quality = Integer.valueOf(arg);

								/*
								 * XXX should we move this into
								 * lame_set_quality()?
								 */
								if (tmp_quality < 0)
									tmp_quality = 0;
								if (tmp_quality > 9)
									tmp_quality = 9;

								gfp.quality = tmp_quality;
							}
							break;
						case 'f':
							gfp.quality = 7;
							break;
						case 'h':
							gfp.quality = 2;
							break;

						case 's':
							argUsed = 1;
							val = Double.parseDouble(arg);
							gfp.in_samplerate = (int) (val
									* (val <= 192 ? 1.e3 : 1.e0) + 0.5);
							break;
						case 'b':
							argUsed = 1;
							gfp.brate = Integer.valueOf(arg);

							if (gfp.brate > 320) {
								gfp.disable_reservoir = true;
							}
							gfp.VBR_min_bitrate_kbps = gfp.brate;
							break;
						case 'B':
							argUsed = 1;
							gfp.VBR_max_bitrate_kbps = Integer.valueOf(arg);
							break;
						case 'F':
							gfp.VBR_hard_min = 1;
							break;
						case 't': /* dont write VBR tag */
							gfp.bWriteVbrTag = false;
							disable_wav_header = true;
							break;
						case 'T': /* do write VBR tag */
							gfp.bWriteVbrTag = true;
							nogap_tags = 1;
							disable_wav_header = false;
							break;
						case 'r': /* force raw pcm input file */
							input_format = sound_file_format.sf_raw;
							break;
						case 'x': /* force byte swapping */
							swapbytes = true;
							break;
						case 'p':
							/*
							 * (jo) error_protection: add crc16 information to
							 * stream
							 */
							gfp.error_protection = true;
							break;
						case 'a':
							/*
							 * autoconvert input file from stereo to mono - for
							 * mono mp3 encoding
							 */
							autoconvert = 1;
							gfp.mode = MPEGMode.MONO;
							break;
						case 'S':
							silent = 10;
							break;
						case 'X':
							/*
							 * experimental switch -X: the differnt types of
							 * quant compare are tough to communicate to
							 * endusers, so they shouldn't bother to toy around
							 * with them
							 */
						{
							int x, y;
							Scanner sc = new Scanner(arg);
							x = sc.nextInt();
							if (!sc.hasNext()) {
								y = x;
							} else {
								sc.nextByte();
								y = sc.nextInt();
							}
							argUsed = 1;
							if (INTERNAL_OPTS) {
								gfp.quant_comp = x;
								gfp.quant_comp_short = y;
							}
						}
							break;
						case 'Y':
							gfp.experimentalY = true;
							break;
						case 'Z':
							/*
							 * experimental switch -Z: this switch is obsolete
							 */
						{
							int n = 1;
							Scanner sc = new Scanner(arg);
							n = sc.nextInt();
							if (INTERNAL_OPTS) {
								gfp.experimentalZ = n;
							}
						}
							break;
						case 'e':
							argUsed = 1;

							switch (arg.charAt(0)) {
							case 'n':
								gfp.emphasis = 0;
								break;
							case '5':
								gfp.emphasis = 1;
								break;
							case 'c':
								gfp.emphasis = 3;
								break;
							default:
								System.err.printf(
										"%s: -e emp must be n/5/c not %s\n",
										ProgramName, arg);
								return -1;
							}
							break;
						case 'c':
							gfp.copyright = 1;
							break;
						case 'o':
							gfp.original = 0;
							break;

						case '?':
							long_help(gfp, System.out, ProgramName, 0 /*
																	 * LESSMODE=NO
																	 */);
							return -1;

						default:
							System.err.printf("%s: unrecognized option -%c\n",
									ProgramName, c);
							return -1;
						}
						if (argUsed != 0) {
							if (arg == token)
								token = ""; /* no more from token */
							else
								++i; /* skip arg we used */
							arg = "";
							argUsed = 0;
						}
					}
				}
			} else {
				if (nogap != 0) {
					if ((ng != null) && (count_nogap < ng.num_nogap)) {
						nogap_inPath[count_nogap++] = argv.get(i);
						input_file = 1;
					} else {
						/* sorry, calling program did not allocate enough space */
						System.err
								.printf("Error: 'nogap option'.  Calling program does not allow nogap option, or\n"
										+ "you have exceeded maximum number of input files for the nogap option\n");
						ng.num_nogap = -1;
						return -1;
					}
				} else {
					/* normal options: inputfile [outputfile] */
					if (inPath.length() == 0) {
						inPath.setLength(0);
						inPath.append(argv.get(i));
						input_file = 1;
					} else {
						if (outPath.length() == 0) {
							outPath.setLength(0);
							outPath.append(argv.get(i));
						} else {
							System.err.printf("%s: excess arg %s\n",
									ProgramName, argv.get(i));
							return -1;
						}
					}
				}
			}
		} /* loop over args */

		if (0 == input_file) {
			usage(System.out, ProgramName);
			return -1;
		}

		if (inPath.toString().charAt(0) == '-')
			silent = (silent <= 1 ? 1 : silent);

		if (outPath.length() == 0 && count_nogap == 0) {
			outPath.setLength(0);
			outPath.append(inPath.substring(0, inPath.length() - 4));
			if (gfp.decode_only) {
				outPath.append(".mp3.wav");
			} else {
				outPath.append(".mp3");
			}
		}

		/* RG is enabled by default */
		if (0 == noreplaygain)
			gfp.findReplayGain = true;

		/* disable VBR tags with nogap unless the VBR tags are forced */
		if (nogap != 0 && gfp.bWriteVbrTag && nogap_tags == 0) {
			System.out
					.println("Note: Disabling VBR Xing/Info tag since it interferes with --nogap\n");
			gfp.bWriteVbrTag = false;
		}

		/* some file options not allowed with stdout */
		if (outPath.toString().charAt(0) == '-') {
			gfp.bWriteVbrTag = false; /* turn off VBR tag */
		}

		/* if user did not explicitly specify input is mp3, check file name */
		if (input_format == sound_file_format.sf_unknown)
			input_format = filename_to_type(inPath.toString());

		if (input_format == sound_file_format.sf_ogg) {
			System.err.printf("sorry, vorbis support in LAME is deprecated.\n");
			return -1;
		}
		/* default guess for number of channels */
		if (autoconvert != 0)
			gfp.num_channels = 2;
		else if (MPEGMode.MONO == gfp.mode)
			gfp.num_channels = 1;
		else
			gfp.num_channels = 2;

		if (gfp.free_format) {
			if (gfp.brate < 8 || gfp.brate > 640) {
				System.err
						.printf("For free format, specify a bitrate between 8 and 640 kbps\n");
				System.err.printf("with the -b <bitrate> option\n");
				return -1;
			}
		}
		if (ng != null)
			ng.num_nogap = count_nogap;
		return 0;
	}

}
