/*
 * Copyright (c) 2021
 * NDE Netzdesign und -entwicklung AG, Hamburg, Germany
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library 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 Library General Public
 * License along with this program (see the file LICENSE.txt for more
 * details); if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 */

package org.acplt.oncrpc.apps.jrpcgen;

import java.io.File;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;

public final class JrpcgenOptions {

	public enum ParseResult {
		VALID,
		INVALID,
		HELP_REQUESTED,
		VERSION_REQUESTED,
		LAST
	}
	
	public class Arguments {
		
		private String[] arguments;
		private int argumentIndex;

		public ParseResult parse(PrintStream out) {
			/*
			 * The optimistic way. 
			 */
			ParseResult result = ParseResult.VALID;

			//
			// First parse the command line (options)...
			//
			String[] args = arguments;
			int argc = arguments.length;
			int argIdx = 0;
			
			for ( ; (result == ParseResult.VALID) && (argIdx < argc); ++argIdx ) {
				//
				// Check to see whether this is an option...
				//
				String arg = args[argIdx];
				if ( (arg.length() > 0)
						&& (arg.charAt(0) != '-') ) {
					break;
				}
				//
				// ...and which option is it?
				//
				if ( arg.equals("-d") ) {
					// -d <dir>
					if ( ++argIdx >= argc ) {
						out.println("jrpcgen: missing directory");
						result = ParseResult.INVALID;
					}
					destinationDir = new File(args[argIdx]);
				} else if ( arg.equals("-package")
						|| arg.equals("-p") ) {
					// -p <package name>
					if ( ++argIdx >= argc ) {
						out.println("jrpcgen: missing package name");
						result = ParseResult.INVALID;
					}
					packageName = args[argIdx];
				} else if ( arg.equals("-c") ) {
					// -c <class name>
					if ( ++argIdx >= argc ) {
						out.println("jrpcgen: missing client class name");
						result = ParseResult.INVALID;
					}
					clientClass = args[argIdx];
				} else if ( arg.equals("-s") ) {
					// -s <class name>
					if ( ++argIdx >= argc ) {
						out.println("jrpcgen: missing server class name");
						result = ParseResult.INVALID;
					}
					serverClass = args[argIdx];
				} else if ( arg.equals("-ser") ) {
					makeSerializable = true;
				} else if ( arg.equals("-bean") ) {
					makeSerializable = true;
					makeBean = true;
				} else if ( arg.equals("-initstrings") ) {
					initStrings = true;
				} else if ( arg.equals("-noclamp") ) {
					clampProgAndVers = false;
				} else if ( arg.equals("-withcallinfo") ) {
					withCallInfo = true;
				} else if ( arg.equals("-debug") ) {
					debug = true;
				} else if ( arg.equals("-nobackup") ) {
					noBackups = true;
				} else if ( arg.equals("-noclient") ) {
					noClient = true;
				} else if ( arg.equals("-noserver") ) {
					noServer = true;               
				} else if (arg.equals("-noxdr")) {
					noXdr = true;
				} else if (arg.equals("-t")) {
					serverTcpOnly = true;
				} else if (arg.equals("-u")) {
					serverUdpOnly = true;
				} else if ( arg.equalsIgnoreCase("-noToString") ) {
					noToString = true;
				} else if ( arg.equalsIgnoreCase("-noEquals") ) {
					noEquals = true;
				} else if ( arg.equalsIgnoreCase("-noEnum") ) {
					noEnum = true;
				} else if ( arg.equalsIgnoreCase("-noValueCtor") ) {
					noValueCtor = true;                            
				} else if ( arg.equals("-parseonly") ) {
					parseOnly = true;
				} else if ( arg.equals("-verbose") ) {
					verbose = true;
				} else if ( arg.equals("-version") ) {
					result = ParseResult.VERSION_REQUESTED;
				} else if ( arg.equals("-help") || arg.equals("-?") ) {
					result = ParseResult.HELP_REQUESTED;
				} else if ( arg.equals("--") ) {
					//
					// End of options...
					//
					result = ParseResult.LAST;
				} else {
					//
					// It's an unknown option!
					//
					out.println("Unrecognized option: " + arg);
					result = ParseResult.INVALID;
				} /* endif (Evaluate current option) */
			} /* endfor (Loop over the argument list) */

			/*
			 * Remind the current argument index.
			 */
			argumentIndex = argIdx;
			
			return result;
		}
		
		public String getXFilename() {
			/*
			 * Does the index point to the last
			 * argument?
			 */
			if (argumentIndex == (arguments.length - 1))
				return arguments[argumentIndex++];
			
			return null;
		}
		
		private Arguments(String[] arguments) {
			this.arguments = arguments;
			this.argumentIndex = 0;
		}
	}
	
	public boolean javaEnumerationsEnabled() {
		return (noEnum == false);
	}
	
	public boolean generateToStringMethod() {
		return (noToString == false);
	}
	
	public boolean generateEqualsMethod() {
		return (noEquals == false);
	}
	
	public boolean generateValueConstructor() {
		return (noValueCtor == false);
	}
	
	public boolean generateJavaFiles() {
		return (! parseOnly);
	}
	
	public boolean generateClientStub() {
		return generateJavaFiles() && (noClient == false);		
	}
	
	public boolean generateServerStub() {
		return generateJavaFiles() && (noServer == false);
	}
	
	public boolean generateProgramConstants() {
		return generateClientStub() || generateServerStub();
	}
	
	public boolean generateXdrTypes() {
		return generateJavaFiles() && (noXdr == false);
	}
	
	public boolean generateTcpTransport() {
		return serverTcpOnly || (! serverUdpOnly);
	}
	
	public boolean generateUdpTransport() {
		return serverUdpOnly || (! serverTcpOnly);
	}
	
	
	@Override
	public String toString() {
		return "JrpcgenOptions [startDate=" + startDate + ", noBackups=" + noBackups + ", clampProgAndVers="
				+ clampProgAndVers + ", withCallInfo=" + withCallInfo + ", debug=" + debug + ", verbose=" + verbose
				+ ", parseOnly=" + parseOnly + ", destinationDir=" + destinationDir + ", packageName=" + packageName
				+ ", baseClassname=" + baseClassname + ", noClient=" + noClient + ", noServer=" + noServer
				+ ", serverClass=" + serverClass + ", noXdr=" + noXdr + ", serverTcpOnly=" + serverTcpOnly
				+ ", serverUdpOnly=" + serverUdpOnly + ", clientClass=" + clientClass + ", makeSerializable="
				+ makeSerializable + ", makeBean=" + makeBean + ", initStrings=" + initStrings + ", noToString="
				+ noToString + ", noEquals=" + noEquals + ", noEnum=" + noEnum + ", noValueCtor=" + noValueCtor + "]";
	}


	/**
	 * String containing date/time when a jrpcgen run was started. This string
	 * is used in the headers of the generated source code files.
	 */
	public String startDate =
			(new SimpleDateFormat()).format(new Date());

	/**
	 * Disable automatic backup of old source code files, if <code>true</code>.
	 */
	public boolean noBackups = false;

	/**
	 * Clamp version and program number in client method stubs to the
	 * version and program number specified in the x-file.
	 */
	public boolean clampProgAndVers = true;

	/**
	 * Supply (additional) call information to server method stubs.
	 */
	public boolean withCallInfo = false;

	/**
	 * Enable diagnostic messages when parsing the x-file.
	 */
	public boolean debug = false;

	/**
	 * Verbosity flag. If <code>true</code>, then jrpcgen will report about
	 * the steps it is taking when generating all the source code files.
	 */
	public boolean verbose = false;

	/**
	 * Parse x-file only but do not create source code files if set to
	 * <code>true</code>.
	 */
	public boolean parseOnly = false;

	/**
	 * Destination directory where to place the generated files.
	 */
	public File destinationDir = new File(".");

	/**
	 * Specifies package name for generated source code, if not
	 * <code>null</code>. If <code>null</code>, then no package statement
	 * is emitted.
	 */
	public String packageName = null;

	/**
	 * Name of class containing global constants. It is derived from the
	 * filename with the extension (".x") and path removed.
	 */
	public String baseClassname = null;

	/**
	 * Do not generate source code for the client proxy stub if
	 * <code>true</code>.
	 */
	public boolean noClient = false;

	/**
	 * Do not generate source code for the server proxy stub if
	 * <code>true</code>.
	 */
	public boolean noServer = false;

	/**
	 * Name of class containing the ONC/RPC server stubs.
	 */
	public String serverClass = null;

	/**
	 * Do not generate XDR datatype classes if <code>true</code>.
	 */
	public boolean noXdr = false;

	/**
	 * Create TCP-Server only <code>true</code>.
	 */
	public boolean serverTcpOnly = false;

	/**
	 * Create TCP-Server only <code>true</code>.
	 */
	public boolean serverUdpOnly = false;

	/**
	 * Name of class containing the ONC/RPC client stubs.
	 */
	public String clientClass = null;

	/**
	 * Enable tagging of XDR classes as being Serializable
	 */
	public boolean makeSerializable = false;

	/**
	 * Enable generation of accessors in order to use XDR classes as beans.
	 */
	public boolean makeBean = false;

	/**
	 * Enable automatic initialization of String with empty Strings
	 * instead of null reference.
	 */
	public boolean initStrings = false;

	/**
	 * inhibit generation of toString() methods.
	 */
	public boolean noToString=false;

	/**
	 * inhibit generation of equals() methods.
	 */
	public boolean noEquals=false;
	
	/**
	 * inhibit generation of Java enumertions. This
	 * is mainly for legacy support, where applications
	 * still expect interfaces as representation of an
	 * enumeration.
	 */
	public boolean noEnum=false;

	/**
	 * inhibit generation of value constructor that is expecting all members.
	 * only with struct
	 */
	public boolean noValueCtor=false;
	
	public static void printOptions(PrintStream out) {
		out.println("  -c <classname>  specify class name of client proxy stub");
		out.println("  -d <dir>        specify directory where to place generated source code files");
		out.println("  -p <package>    specify package name for generated source code files");
		out.println("  -s <classname>  specify class name of server proxy stub");
		out.println("  -t              create TCP Server only");
		out.println("  -u              create UDP Server only");
		out.println("  -ser            tag generated XDR classes as serializable");
		out.println("  -bean           generate accessors for usage as bean, implies -ser");
		out.println("  -noclamp        do not clamp version number in client method stubs");
		out.println("  -withcallinfo   supply call information to server method stubs");
		out.println("  -initstrings    initialize all strings to be empty instead of null");
		out.println("  -nobackup       do not make backups of old source code files");
		out.println("  -noclient       do not create client proxy stub");
		out.println("  -noserver       do not create server proxy stub");        
		out.println("  -noxdr          do not create XDR datatype classes");
		out.println("  -noToString     do not create toString() methods");        
		out.println("  -noEquals       do not create equals(Object o) and hashCode() methods");
		out.println("  -noEnum         generate enumerations as interfaces instead of enums (legacy support)");
		out.println("  -noValueCtor    do not create value constructors");
		out.println("  -parseonly      parse x-file only but do not create source code files");
		out.println("  -verbose        enable verbose output about what jrpcgen is doing");
		out.println("  -version        print jrpcgen version and exit");
		out.println("  -debug          enables printing of diagnostic messages");
		out.println("  -? -help        print this help message and exit");
		out.println("  --              end options");
	}
	
	public Arguments newArguments(String[] arguments) {
		return new Arguments(arguments);
	}

	public JrpcgenOptions reset()
	{
		startDate = (new SimpleDateFormat()).format(new Date());
		noBackups = false;
		clampProgAndVers = true;
		withCallInfo = false;
		debug = false;
		verbose = false;
		parseOnly = false;
		destinationDir = new File(".");
		packageName = null;
		baseClassname = null;
		noClient = false;
		noServer = false;
		serverClass = null;
		noXdr = false;
		serverTcpOnly = false;
		serverUdpOnly = false;
		clientClass = null;
		makeSerializable = false;
		makeBean = false;
		initStrings = false;
		noToString=false;
		noEquals=false;
		noEnum=false;
		noValueCtor=false;
		
		return this;
	}

}
