/*
 Copyright 2004-2008 Paul R. Holser, Jr.  All rights reserved.
 Licensed under the Academic Free License version 3.0
 */

package joptsimple;

import java.util.NoSuchElementException;

/**
 * <p>Tokenizes a short option specification string as expected by {@link
 * OptionParser#OptionParser(java.lang.String)}.</p>
 *
 * @since 1.0
 * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
 * @version $Id: OptionSpecTokenizer.java,v 1.2 2008/05/01 15:38:45 pholser Exp $
 */
class OptionSpecTokenizer {
    private static final char POSIXLY_CORRECT_MARKER = '+';

    private String specification;
    private int index;

    OptionSpecTokenizer( String specification ) {
        if ( specification == null )
            throw new NullPointerException( "null option specification" );

        this.specification = specification;
    }

    boolean hasMore() {
        return index < specification.length();
    }

    OptionSpec next() {
        if ( !hasMore() )
            throw new NoSuchElementException();

        OptionSpec spec;

        String optionCandidate = String.valueOf( specification.charAt( index++ ) );

        if ( ParserRules.RESERVED_FOR_EXTENSIONS.equals( optionCandidate ) ) {
            spec = handleReservedForExtensionsToken();

            if ( spec != null )
                return spec;
        }

        ParserRules.checkLegalOption( optionCandidate );

        if ( !hasMore() )
            spec = new NoArgumentOptionSpec( optionCandidate );
        else if ( specification.charAt( index ) == ':' )
            spec = handleArgumentAcceptingOption( optionCandidate );
        else
            spec = new NoArgumentOptionSpec( optionCandidate );

        return spec;
    }

    void configure( OptionParser parser ) {
        adjustForPosixlyCorrect( parser );

        while ( hasMore() )
            parser.recognize( next() );
    }

    private void adjustForPosixlyCorrect( OptionParser parser ) {
        if ( POSIXLY_CORRECT_MARKER == specification.charAt( 0 ) ) {
            parser.posixlyCorrect( true );
            specification = specification.substring( 1 );
        }
    }

    private OptionSpec handleReservedForExtensionsToken() {
        if ( !hasMore() )
            return new NoArgumentOptionSpec( ParserRules.RESERVED_FOR_EXTENSIONS );

        if ( specification.charAt( index ) == ';' ) {
            ++index;
            return new AlternativeLongOptionSpec();
        }

        return null;
    }

    private OptionSpec handleArgumentAcceptingOption( String candidate ) {
        index++;

        if ( hasMore() && specification.charAt( index ) == ':' ) {
            index++;
            return new OptionalArgumentOptionSpec( candidate );
        }

        return new RequiredArgumentOptionSpec( candidate );
    }
}
