/*
 * @(#)AbstractOption.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.Assert;
import com.jscape.util.ListIterator;

/**
 * Abstract option implementation. //todo : add Java style option
 *
 * @author Alex
 * @version 1.0
 * @see <a href="AbstractOption.java.html">Source code</a>
 */
public abstract class AbstractOption
        implements Option {

    /**
     * Open brace.
     */
    protected static final String OPEN_BRACE = "[";
    /**
     * Close brace.
     */
    protected static final String CLOSE_BRACE = "]";
    /**
     * Open parameter brace.
     */
    protected static final String OPEN_PARAMETER_BRACE = "<";
    /**
     * Close parameter brace.
     */
    protected static final String CLOSE_PARAMETER_BRACE = ">";

    /**
     * Name.
     */
    protected String name;
    /**
     * Required.
     */
    protected boolean required;
    /**
     * Position.
     */
    protected int position;

    /**
     * Creates a new option instance.
     *
     * @param name     option name
     * @param required required flag
     * @param position option position in line
     */
    protected AbstractOption(String name, boolean required, int position) {
        Assert.isValidString(name);
        Assert.inRangeInclusive(position, 0, ANY_POSITION, "bad position");

        this.name = name;
        this.required = required;
        this.position = position;
    }

    /**
     * Returns option name.
     *
     * @return option name
     */
    public String getName() {
        return this.name;
    }

    /**
     * @see com.jscape.util.cli.Option#isRequired()
     */
    public boolean isRequired() {
        return this.required;
    }

    /**
     * @see com.jscape.util.cli.Option#getPosition()
     */
    public int getPosition() {
        return this.position;
    }

    /**
     * @see Option#parse(com.jscape.util.ListIterator)
     */
    public String parse(ListIterator i) {
        Assert.notNull(i);

        return (this.position != ANY_POSITION) ? parsePositionedOption(i) : parseFreeOption(i);
    }

    /**
     * @param that that
     *
     * @return int
     */
    public int compareTo(Option that) {
        return Integer.valueOf(getPosition()).compareTo(that.getPosition());
    }

    /**
     * @param o Object instance
     *
     * @return int
     */
    public int compareTo(Object o) {
        return compareTo((Option) o);
    }

    /**
     * @param o Object instance
     *
     * @return boolean value
     */
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        final AbstractOption that = (AbstractOption) o;

        if (this.position != that.position) {
            return false;
        }
        if (this.required != that.required) {
            return false;
        }

        return this.name.equals(that.name);
    }

    /**
     * @return int
     */
    public int hashCode() {
        int result = this.name.hashCode();
        result = 29 * result + (this.required ? 1 : 0);
        result = 29 * result + this.position;

        return result;
    }

    /**
     * @return String
     */
    public String toString() {
        return this.required ? getOptionString() : (OPEN_BRACE + getOptionString() + CLOSE_BRACE);
    }

    /**
     * Parses free-positioned option.
     *
     * @param i parameters iterator
     *
     * @return found option value or <code>null</code> if option was not found
     */
    protected String parseFreeOption(ListIterator i) {
        i.reset();

        while (i.hasNext()) {
            Argument argument = (Argument) i.next();

            if (found(argument.value) && !argument.parsed) {
                return optionFound(argument, i);
            }
        }

        return optionMissed();
    }

    /**
     * Parses positioned option.
     *
     * @param i parameters iterator
     *
     * @return found option value or <code>null</code> if option was not found
     */
    protected String parsePositionedOption(ListIterator i) {
        try {
            i.index(this.position);
            Argument argument = (Argument) i.value();

            if (found(argument.value) && !argument.parsed) {
                return optionFound(argument, i);
            }

            return optionMissed();
        } catch (Exception e) {
            return optionMissed();
        }
    }

    /**
     * Checks if option is found.
     *
     * @param option option to check
     *
     * @return <code>true</code> if option is found;
     *         <code>false</code> otherwise
     */
    protected boolean found(String option) {
        return this.name.equals(option);
    }

    /**
     * Determines what to do if option has been found.
     *
     * @param argument founded option
     * @param i        parameters iterator
     *
     * @return option value
     */
    protected String optionFound(Argument argument, ListIterator i) {
        argument.parsed = true;
        return argument.value;
    }

    /**
     * Determines what to do if option is missed.
     *
     * @return <code>null</code> or throws corresponded exception
     */
    protected String optionMissed() {
        if (this.required) {
            throw new IllegalArgumentException("Missing required option(s).");
        } else {
            return null;
        }
    }

    /**
     * Returns concrete option string representation.
     *
     * @return concrete option string representation
     */
    protected String getOptionString() {
        return getName();
    }

}
