package jdiff;

import java.io.*;
import java.util.*;
import com.sun.javadoc.*;

/** 
 * Class to handle options for JDiff.
 *
 * See the file LICENSE.txt for copyright details.
 * @author Matthew Doar, mdoar@pobox.com
 */
public class Options {

    /** Default constructor. */
    public Options() {
    }

    /**
     * Returns the "length" of a given option. If an option takes no
     * arguments, its length is one. If it takes one argument, its
     * length is two, and so on. This method is called by Javadoc to
     * parse the options it does not recognize. It then calls
     * {@link #validOptions} to validate them.
     * <blockquote>
     * <b>Note:</b><br>
     * The options arrive as case-sensitive strings. For options that
     * are not case-sensitive, use toLowerCase() on the option string
     * before comparing it.
     * </blockquote>
     *
     * @param option  a String containing an option
     * @return an int telling how many components that option has
     */
    public static int optionLength(String option) {
        String opt = option.toLowerCase();
        
        // Standard options
        if (opt.equals("-authorid"))  return 2;
        if (opt.equals("-versionid")) return 2;
        if (opt.equals("-d"))         return 2;
        if (opt.equals("-classlist")) return 1;
        if (opt.equals("-title"))     return 2;
        if (opt.equals("-docletid"))  return 1;
        if (opt.equals("-evident"))   return 2;
        if (opt.equals("-skippkg"))   return 2;
        if (opt.equals("-skipclass")) return 2;
        if (opt.equals("-execdepth")) return 2;
        if (opt.equals("-help"))      return 1;
        if (opt.equals("-version"))      return 1;
        if (opt.equals("-package"))   return 1;
        if (opt.equals("-protected")) return 1;
        if (opt.equals("-public"))    return 1;
        if (opt.equals("-private"))   return 1;
        if (opt.equals("-sourcepath")) return 2;
        
        // Options to control JDiff
        if (opt.equals("-apiname"))    return 2;
        if (opt.equals("-oldapi"))    return 2;
        if (opt.equals("-newapi"))    return 2;

        // Options to control the location of the XML files
        if (opt.equals("-apidir"))       return 2;
        if (opt.equals("-oldapidir"))    return 2;
        if (opt.equals("-newapidir"))    return 2;
        if (opt.equals("-usercommentsdir"))    return 2;


        // Options for the exclusion level for classes and members
        if (opt.equals("-excludeclass"))    return 2;
        if (opt.equals("-excludemember"))    return 2;

        if (opt.equals("-firstsentence"))    return 1;
        if (opt.equals("-docchanges"))    return 1;
        if (opt.equals("-packagesonly"))    return 1;
        if (opt.equals("-showallchanges"))    return 1;

        // Option to change the location for the existing Javadoc
        // documentation for the new API. Default is "../"
        if (opt.equals("-javadocnew"))    return 2;
        // Option to change the location for the existing Javadoc
        // documentation for the old API. Default is null.
        if (opt.equals("-javadocold"))    return 2;

        if (opt.equals("-baseuri"))    return 2;

        // Option not to suggest comments at all
        if (opt.equals("-nosuggest"))    return 2;

        // Option to enable checking that the comments end with a period.
        if (opt.equals("-checkcomments"))    return 1;
        // Option to retain non-printing characters in comments.
        if (opt.equals("-retainnonprinting"))    return 1;
        // Option for the name of the exclude tag
        if (opt.equals("-excludetag"))    return 2;
        // Generate statistical output
        if (opt.equals("-stats"))    return 1;

        // Set the browser window title
        if (opt.equals("-windowtitle"))    return 2;
        // Set the report title
        if (opt.equals("-doctitle"))    return 2;

        return 0;
    }//optionLength()

   /**
    * After parsing the available options using {@link #optionLength},
    * Javadoc invokes this method with an array of options-arrays, where
    * the first item in any array is the option, and subsequent items in
    * that array are its arguments. So, if -print is an option that takes
    * no arguments, and -copies is an option that takes 1 argument, then
    * <pre>
    *     -print -copies 3
    * </pre>
    * produces an array of arrays that looks like:
    * <pre>
    *      option[0][0] = -print
    *      option[1][0] = -copies
    *      option[1][1] = 3
    * </pre>
    * (By convention, command line switches start with a "-", but
    * they don't have to.)
    * <p>
    * <b>Note:</b><br>
    * Javadoc passes <i>all</i>parameters to this method, not just
    * those that Javadoc doesn't recognize. The only way to
    * identify unexpected arguments is therefore to check for every
    * Javadoc parameter as well as doclet parameters.
    *
    * @param options   an array of String arrays, one per option
    * @param reporter  a DocErrorReporter for generating error messages
    * @return true if no errors were found, and all options are
    *         valid
    */
    public static boolean validOptions(String[][] options,
                                       DocErrorReporter reporter) {
        final DocErrorReporter errOut = reporter;
        
        // A nice object-oriented way of handling errors. An instance of this
        // class puts out an error message and keeps track of whether or not
        // an error was found.
        class ErrorHandler {
            boolean noErrorsFound = true;
            void msg(String msg) {
                noErrorsFound = false;
                errOut.printError(msg);
            }
        }
        
        ErrorHandler err = new ErrorHandler();
        if (trace)
            System.out.println("Command line arguments: "); 
        for (int i = 0; i < options.length; i++) {
            for (int j = 0; j < options[i].length; j++) {
                Options.cmdOptions += " " + options[i][j];
                if (trace)
                    System.out.print(" " + options[i][j]); 
            }            
        }
        if (trace)
            System.out.println(); 

        for (int i = 0; i < options.length; i++) {
            if (options[i][0].toLowerCase().equals("-apiname")) {
                if (options[i].length < 2) {
                    err.msg("No version identifier specified after -apiname option.");
                } else if (JDiff.compareAPIs) {
                    err.msg("Use the -apiname option, or the -oldapi and -newapi options, but not both.");
                } else { 
                    String filename = options[i][1];
                    RootDocToXML.apiIdentifier = filename;
                    filename = filename.replace(' ', '_');
                    RootDocToXML.outputFileName =  filename + ".xml";
                    JDiff.writeXML = true;
                    JDiff.compareAPIs = false;
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-apidir")) {
                if (options[i].length < 2) {
                    err.msg("No directory specified after -apidir option.");
                } else {
		    RootDocToXML.outputDirectory = options[i][1];
                }
                continue;
            }
	    if (options[i][0].toLowerCase().equals("-oldapi")) {
                if (options[i].length < 2) {
                    err.msg("No version identifier specified after -oldapi option.");
                } else if (JDiff.writeXML) {
                    err.msg("Use the -apiname or -oldapi option, but not both.");
                } else { 
                    String filename = options[i][1];
                    filename = filename.replace(' ', '_');
                    JDiff.oldFileName =  filename + ".xml";
                    JDiff.writeXML = false;
                    JDiff.compareAPIs = true;
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-oldapidir")) {
                if (options[i].length < 2) {
                    err.msg("No directory specified after -oldapidir option.");
                } else { 
                	JDiff.oldDirectory = options[i][1];
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-newapi")) {
                if (options[i].length < 2) {
                    err.msg("No version identifier specified after -newapi option.");
                } else if (JDiff.writeXML) {
                    err.msg("Use the -apiname or -newapi option, but not both.");
                } else { 
                    String filename = options[i][1];
                    filename = filename.replace(' ', '_');
                    JDiff.newFileName =  filename + ".xml";
                    JDiff.writeXML = false;
                    JDiff.compareAPIs = true;
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-newapidir")) {
                if (options[i].length < 2) {
                    err.msg("No directory specified after -newapidir option.");
                } else { 
                	JDiff.newDirectory = options[i][1];
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-usercommentsdir")) {
                if (options[i].length < 2) {
                    err.msg("Android: No directory specified after -usercommentsdir option.");
                } else { 
                    HTMLReportGenerator.commentsDir = options[i][1];
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-d")) {
                if (options[i].length < 2) {
                    err.msg("No directory specified after -d option.");
                } else {
                    HTMLReportGenerator.outputDir = options[i][1];
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-javadocnew")) {
                if (options[i].length < 2) {
                    err.msg("No location specified after -javadocnew option.");
                } else {
                    HTMLReportGenerator.newDocPrefix = options[i][1];
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-javadocold")) {
                if (options[i].length < 2) {
                    err.msg("No location specified after -javadocold option.");
                } else {
                    HTMLReportGenerator.oldDocPrefix = options[i][1];
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-baseuri")) {
                if (options[i].length < 2) {
                    err.msg("No base location specified after -baseURI option.");
                } else {
                    RootDocToXML.baseURI = options[i][1];
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-excludeclass")) {
                if (options[i].length < 2) {
                    err.msg("No level (public|protected|package|private) specified after -excludeclass option.");
                } else { 
                    String level = options[i][1];
                    if (level.compareTo("public") != 0 &&
                        level.compareTo("protected") != 0 &&
                        level.compareTo("package") != 0 &&
                        level.compareTo("private") != 0) {
                        err.msg("Level specified after -excludeclass option must be one of (public|protected|package|private).");
                    } else {
                        RootDocToXML.classVisibilityLevel = level;
                    }
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-excludemember")) {
                if (options[i].length < 2) {
                    err.msg("No level (public|protected|package|private) specified after -excludemember option.");
                } else { 
                    String level = options[i][1];
                    if (level.compareTo("public") != 0 &&
                        level.compareTo("protected") != 0 &&
                        level.compareTo("package") != 0 &&
                        level.compareTo("private") != 0) {
                        err.msg("Level specified after -excludemember option must be one of (public|protected|package|private).");
                    } else {
                        RootDocToXML.memberVisibilityLevel = level;
                    }
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-firstsentence")) {
                RootDocToXML.saveAllDocs = false;
                continue;
            }
            if (options[i][0].toLowerCase().equals("-docchanges")) {
                HTMLReportGenerator.reportDocChanges = true;
                Diff.noDocDiffs = false;
                continue;
            }
            if (options[i][0].toLowerCase().equals("-packagesonly")) {
                RootDocToXML.packagesOnly = true;
                continue;
            }
            if (options[i][0].toLowerCase().equals("-showallchanges")) {
                Diff.showAllChanges = true;
                continue;
            }
            if (options[i][0].toLowerCase().equals("-nosuggest")) {
                if (options[i].length < 2) {
                    err.msg("No level (all|remove|add|change) specified after -nosuggest option.");
                } else { 
                    String level = options[i][1];
                    if (level.compareTo("all") != 0 &&
                        level.compareTo("remove") != 0 &&
                        level.compareTo("add") != 0 &&
                        level.compareTo("change") != 0) {
                        err.msg("Level specified after -nosuggest option must be one of (all|remove|add|change).");
                    } else {
                        if (level.compareTo("removal") == 0)
                            HTMLReportGenerator.noCommentsOnRemovals = true;
                        else if (level.compareTo("add") == 0)
                            HTMLReportGenerator.noCommentsOnAdditions = true;
                        else if (level.compareTo("change") == 0)
                            HTMLReportGenerator.noCommentsOnChanges = true;
                        else if (level.compareTo("all") == 0) {
                            HTMLReportGenerator.noCommentsOnRemovals = true;
                            HTMLReportGenerator.noCommentsOnAdditions = true;
                            HTMLReportGenerator.noCommentsOnChanges = true;
                        }
                    }
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-checkcomments")) {
                APIHandler.checkIsSentence = true;
                continue;
            }
            if (options[i][0].toLowerCase().equals("-retainnonprinting")) {
                RootDocToXML.stripNonPrintables = false;
                continue;
            }
            if (options[i][0].toLowerCase().equals("-excludetag")) {
                if (options[i].length < 2) {
                    err.msg("No exclude tag specified after -excludetag option.");
                } else { 
                    RootDocToXML.excludeTag = options[i][1];
                    RootDocToXML.excludeTag = RootDocToXML.excludeTag.trim();
                    RootDocToXML.doExclude = true;
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-stats")) {
                HTMLReportGenerator.doStats = true;
                continue;
            }
            if (options[i][0].toLowerCase().equals("-doctitle")) {
                if (options[i].length < 2) {
                    err.msg("No HTML text specified after -doctitle option.");
                } else { 
                    HTMLReportGenerator.docTitle = options[i][1];
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-windowtitle")) {
                if (options[i].length < 2) {
                    err.msg("No text specified after -windowtitle option.");
                } else { 
                    HTMLReportGenerator.windowTitle = options[i][1];
                }
                continue;
            }
            if (options[i][0].toLowerCase().equals("-version")) {
                System.out.println("JDiff version: " + JDiff.version);
                System.exit(0);
            }
            if (options[i][0].toLowerCase().equals("-help")) {
                usage();
                System.exit(0);
            }
        }//for
        if (!JDiff.writeXML && !JDiff.compareAPIs) {
            err.msg("First use the -apiname option to generate an XML file for one API.");
            err.msg("Then use the -apiname option again to generate another XML file for a different version of the API.");
            err.msg("Finally use the -oldapi option and -newapi option to generate a report about how the APIs differ.");
        }
        return err.noErrorsFound;
    }// validOptions()

    /** Display the arguments for JDiff. */
    public static void usage() {
        System.err.println("JDiff version: " + JDiff.version);
        System.err.println("");
        System.err.println("Valid JDiff arguments:");
        System.err.println("");
        System.err.println("  -apiname <Name of a version>");
        System.err.println("  -oldapi <Name of a version>");
        System.err.println("  -newapi <Name of a version>");
        
        System.err.println("  Optional Arguments");
        System.err.println();
        System.err.println("  -d <directory> Destination directory for output HTML files");
        System.err.println("  -oldapidir <directory> Location of the XML file for the old API");
        System.err.println("  -newapidir <directory> Location of the XML file for the new API");
        System.err.println("  -sourcepath <location of Java source files>");
        System.err.println("  -javadocnew <location of existing Javadoc files for the new API>");
        System.err.println("  -javadocold <location of existing Javadoc files for the old API>");
        System.err.println("  -usercommentsdir <directory> Path to dir containing the user_comments* file(s)");
        
        System.err.println("  -baseURI <base> Use \"base\" as the base location of the various DTDs and Schemas used by JDiff");
        System.err.println("  -excludeclass [public|protected|package|private] Exclude classes which are not public, protected etc");
        System.err.println("  -excludemember [public|protected|package|private] Exclude members which are not public, protected etc");
        
        System.err.println("  -firstsentence Save only the first sentence of each comment block with the API.");
        System.err.println("  -docchanges Report changes in Javadoc comments between the APIs");
        System.err.println("  -nosuggest [all|remove|add|change] Do not add suggested comments to all, or the removed, added or chabged sections");
        System.err.println("  -checkcomments Check that comments are sentences");
        System.err.println("  -stripnonprinting Remove non-printable characters from comments.");
        System.err.println("  -excludetag <tag> Define the Javadoc tag which implies exclusion");
        System.err.println("  -stats Generate statistical output");
        System.err.println("  -help       (generates this output)");
        System.err.println("");
        System.err.println("For more help, see jdiff.html");
    }
    
    /** All the options passed on the command line. Logged to XML. */
    public static String cmdOptions = "";

    /** Set to enable increased logging verbosity for debugging. */
    private static boolean trace = false;
}
