/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.cli;

import java.util.ArrayList;

/**
 * The class GnuParser provides an implementation of the 
 * {@link Parser#flatten(Options,String[],boolean) flatten} method.
 *
 * @author John Keyes (john at integralsource.com)
 * @see Parser
 * @version $Revision: 542151 $
 */
public class GnuParser extends Parser {

    /** holder for flattened tokens */
    private ArrayList tokens = new ArrayList();

    /**
     * <p>Resets the members to their original state i.e. remove
     * all of <code>tokens</code> entries.
     */
    private void init()
    {
        tokens.clear();
    }

    /**
     * <p>This flatten method does so using the following rules:
     * <ol>
     *  <li>If an {@link Option} exists for the first character of 
     *  the <code>arguments</code> entry <b>AND</b> an {@link Option} 
     *  does not exist for the whole <code>argument</code> then
     *  add the first character as an option to the processed tokens
     *  list e.g. "-D" and add the rest of the entry to the also.</li>
     *  <li>Otherwise just add the token to the processed tokens list.
     *  </li>
     * </ol>
     * </p>
     *
     * @param options The Options to parse the arguments by.
     * @param arguments The arguments that have to be flattened.
     * @param stopAtNonOption specifies whether to stop 
     * flattening when a non option has been encountered
     * @return a String array of the flattened arguments
     */
    protected String[] flatten(Options options, String[] arguments, 
                               boolean stopAtNonOption)
    {
        init();

        boolean eatTheRest = false;
        Option currentOption = null;

        for (int i = 0; i < arguments.length; i++)
        {
            if ("--".equals(arguments[i]))
            {
                eatTheRest = true;
                tokens.add("--");
            }
            else if ("-".equals(arguments[i]))
            {
                tokens.add("-");
            }
            else if (arguments[i].startsWith("-"))
            {
                Option option = options.getOption(arguments[i]);

                // this is not an Option
                if (option == null)
                {
                    // handle special properties Option
                    Option specialOption = 
                            options.getOption(arguments[i].substring(0, 2));

                    if (specialOption != null)
                    {
                        tokens.add(arguments[i].substring(0, 2));
                        tokens.add(arguments[i].substring(2));
                    }
                    else if (stopAtNonOption)
                    {
                        eatTheRest = true;
                        tokens.add(arguments[i]);
                    }
                    else
                    {
                        tokens.add(arguments[i]);
                    }
                }
                else
                {
                    // WARNING: Findbugs reports major problems with the following code. 
                    //          As option cannot be null, currentOption cannot and 
                    //          much of the code below is never going to be run.

                    currentOption = option;

                    // special option
                    Option specialOption = 
                            options.getOption(arguments[i].substring(0, 2));

                    if ((specialOption != null) && (option == null))
                    {
                        tokens.add(arguments[i].substring(0, 2));
                        tokens.add(arguments[i].substring(2));
                    }
                    else if ((currentOption != null) && currentOption.hasArg())
                    {
                        if (currentOption.hasArg())
                        {
                            tokens.add(arguments[i]);
                            currentOption = null;
                        }
                        else if (currentOption.hasArgs())
                        {
                            tokens.add(arguments[i]);
                        }
                        else if (stopAtNonOption)
                        {
                            eatTheRest = true;
                            tokens.add("--");
                            tokens.add(arguments[i]);
                        }
                        else
                        {
                            tokens.add(arguments[i]);
                        }
                    }
                    else if (currentOption != null)
                    {
                        tokens.add(arguments[i]);
                    }
                    else if (stopAtNonOption)
                    {
                        eatTheRest = true;
                        tokens.add("--");
                        tokens.add(arguments[i]);
                    }
                    else
                    {
                        tokens.add(arguments[i]);
                    }
                }
            }
            else
            {
                tokens.add(arguments[i]);
            }

            if (eatTheRest)
            {
                for (i++; i < arguments.length; i++)
                {
                    tokens.add(arguments[i]);
                }
            }
        }

        return (String[]) tokens.toArray(new String[tokens.size()]);
    }
}
