/*
 * @(#)CommandLine.java
 *
 * Copyright (c) 2002-2003 JSCAPE
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JSCAPE. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JSCAPE.
 */

package com.jscape.util.cli;

import com.jscape.util.ListIterator;
import com.jscape.util.Parameters;

import java.util.*;

/**
 * Represents application command line.
 *
 * @author Alex
 */
public class CommandLine {

    private final Map<Option, String> options;

    /**
     * Creates a new command line instance.
     *
     * @param options valid options
     */
    public CommandLine(Option... options) {
        this.options = new HashMap<Option, String>();

        for (Option option : options) {
            this.options.put(option, null);
        }
    }

    /**
     * Creates a new command line instance.
     *
     * @param options valid options
     */
    public CommandLine(Collection<Option> options) {
        this(options.toArray(new Option[options.size()]));
    }

    /**
     * Setups command line parameters.
     *
     * @param arguments parameters
     */
    public void setArguments(String[] arguments) {
        Parameters.assertNotNull(arguments);
        parseArguments(arguments);
    }

    /**
     * Returns command line options.
     *
     * @return command line options
     */
    public Option[] getOptions() {
        Collection<Option> keys = this.options.keySet();
        return keys.toArray(new Option[keys.size()]);
    }

    /**
     * Returns option value if exists.
     *
     * @param option target option
     *
     * @return otion value or <code>null</code> if option os not present
     */
    public String getOptionValue(Option option) {
        return this.options.get(option);
    }

    /**
     * Checks if the option is present in current command line.
     *
     * @param option target option
     *
     * @return <code>true</code> if option is present;
     *         <code>false</code> otherwise
     */
    public boolean isSet(Option option) {
        return getOptionValue(option) != null;
    }

    @Override
    public String toString() {
        StringBuilder buffer = new StringBuilder();
        Option[] options = getOptions();
        Arrays.sort(options);

        for (int i = 0; i < options.length; ++i) {
            if (i > 0) {
                buffer.append(" ");
            }
            buffer.append(options[i]);
        }

        return buffer.toString();
    }

    private void parseArguments(String[] arguments) {
        ListIterator<Argument> argumentsIterator = iteratorFor(arguments);
        Set<Option> options = this.options.keySet();

        for (Option option : options) {
            String value = option.parse(argumentsIterator);
            this.options.put(option, value);
        }
    }

    private ListIterator<Argument> iteratorFor(String[] arguments) {
        List<Argument> data = new ArrayList<Argument>();

        for (String argument : arguments) {
            data.add(new Argument(argument));
        }

        return new ListIterator<Argument>(data);
    }

    public static class UnsupportedArgumentsException
            extends RuntimeException {

        public UnsupportedArgumentsException() {
        }

        public UnsupportedArgumentsException(String message) {
            super(message);
        }

        public UnsupportedArgumentsException(String message, Throwable cause) {
            super(message, cause);
        }

        public UnsupportedArgumentsException(Throwable cause) {
            super(cause.getMessage() != null ? cause.getMessage() : cause.toString(), cause);
        }
    }

}
