/*
 * %W% %E%
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 * Use and Distribution is subject to the Java Research License available
 * at <http://www.sun.com/software/communitysource/jrl.html>.
 */

//package jvn.openjdk.compiler.guide;

import java.io.File;
import javax.lang.model.SourceVersion;
import javax.tools.OptionChecker;
import java.util.*;

/**
 * TODO describe this class!
 *
 * <p><b>This is NOT part of any API supported by Sun Microsystems.
 * If you write code that depends on this, you do so at your own
 * risk.  This code and its internal interfaces are subject to change
 * or deletion without notice.</b></p>
 *
 * @author Peter von der Ah\u00e9
 */
public class Options {

    private final List<String> recognizedOptions;
    private final List<String> classNames;
    private final List<File> files;
    private final List<String> unrecognizedOptions;

    private Options(List<String> recognizedOptions,
                    List<String> classNames,
                    List<File> files,
                    List<String> unrecognizedOptions) {
        this.recognizedOptions = recognizedOptions;
        this.classNames = classNames;
        this.files = files;
        this.unrecognizedOptions = unrecognizedOptions;
    }

    /**
     * <p>
     *   Gets a new instance representing the given arguments.  The
     *   arguments are represented in four lists <code>recognizedOptions</code>,
     *   <code>files</code>, <code>classNames</code>, and
     *   <code>unrecognizedOptions</code> according to the following rules:
     * </p>
     * <ul>
     *   <li>
     *     If an option is recognized it and its arguments are added
     *     to the <code>recognizedOptions</code> list in the given order.
     *   </li>
     *   <li>
     *     Otherwise, if an argument is the name of an existing file,
     *     the file is added to the <code>files</code> list in the
     *     given order.
     *   </li>
     *   <li>
     *     Otherwise, if an argument is a valid class name, it is
     *     added to the <code>classNames</code> list in the given order.
     *   </li>
     *   <li>
     *     Otherwise, the argument is added to the
     *     <code>unrecognizedOptions</code> list in the given order.
     *   </li>
     * </ul>
     *
     * <p>
     *   If an option is not recognized by the primary option checker,
     *   the secondary is tried.
     * </p>
     *
     * @throws IllegalArgumentException if a recognized option is not
     * supplied enough arguments
     * @param primary the primary option checker
     * @param secondary the secondary option checker
     * @param arguments an array of (command-line) arguments
     * @return a new <code>Options</code> instance
     */
    public static Options parse(OptionChecker primary,
                                OptionChecker secondary,
                                String... arguments) {
        List<String> recognizedOptions = new ArrayList<String>();
        List<String> unrecognizedOptions = new ArrayList<String>();
        List<String> classNames = new ArrayList<String>();
        List<File> files = new ArrayList<File>();

        for (int i = 0; i < arguments.length; i++) {
            String argument = arguments[i];
            int optionCount = primary.isSupportedOption(argument);
            if (optionCount < 0) {
                optionCount = secondary.isSupportedOption(argument);
            }
            if (optionCount < 0) {
                File file = new File(argument);
                if (file.exists())
                    files.add(file);
                else if (SourceVersion.isName(argument))
                    classNames.add(argument);
                else
                    unrecognizedOptions.add(argument);
            } else {
                for (int j = 0; j < optionCount+1; j++) {
                    int index = i+j;
                    if (index == arguments.length)
                        throw new IllegalArgumentException(argument);
                    recognizedOptions.add(arguments[index]);
                }
                i+=optionCount;
            }
        }

        return new Options(recognizedOptions, classNames, files, unrecognizedOptions);
    }

    /**
     * Returns the list of recognized options and their arguments.
     * @return a list of options
     */
    public List<String> getRecgonizedOptions() {
        return Collections.unmodifiableList(recognizedOptions);
    }

    /**
     * Returns the list of file names.
     * @return a list of file names
     */
    public List<File> getFiles() {
        return Collections.unmodifiableList(files);
    }

    /**
     * Returns the list of class names.
     * @return a list of class names
     */
    public List<String> getClassNames() {
        return Collections.unmodifiableList(classNames);
    }

    /**
     * Returns the list of unrecognized options.
     * @return a list of unrecognized options
     */
    public List<String> getUnrecognizedOptions() {
        return Collections.unmodifiableList(unrecognizedOptions);
    }

    @Override
    public String toString() {
        return String.format("recognizedOptions = %s; classNames = %s; " +
                             "files = %s; unrecognizedOptions = %s",
                             recognizedOptions, classNames, files, unrecognizedOptions);
    }
}

