/*
 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

package sun.jvm.hotspot;

import java.util.Arrays;
import java.util.List;

public class SAGetopt {

    private String[] _argv;

    private int _optind;    // index in arguments array
    private int _optopt;    // index within an argument
    private String _optarg; // last option argument
    private boolean _optreset; // special handling of first call

    public SAGetopt(String[] args) {
        _argv  = args.clone();
        _optind   = 0;
        _optopt   = 1;
        _optarg   = null;
        _optreset = true;
    }

    public String getOptarg() {
        return _optarg;
    }

    public int getOptind() {
        return _optind;
    }

    private void extractOptarg(String opt) {
        // Argument expected
        if (_optind > _argv.length) {
            throw new SAGetoptException("Not enough arguments for '" + opt + "'");
        }

        if (! _argv[_optind].isEmpty() && _argv[_optind].charAt(0) == '-') {
            throw new SAGetoptException("Argument is expected for '" + opt + "'");
        }

        _optarg = _argv[_optind];
        _optind += 1;
    }

    private String processLongOptions(String carg, String[] longOptStr) {
        List<String> los = Arrays.asList(longOptStr);
        String[] ca = carg.split("=", 2);

        if (los.contains(ca[0])) {
            if (ca.length > 1) {
                throw new SAGetoptException("Argument is not expected for '" + ca[0] + "'");
            }
            return carg;
        }

        if (los.contains(ca[0] + "=")) {
            if (ca.length > 1) {
                // GNU style options --file=name
                _optarg = ca[1];
            }
            else {
                // Mixed style options --file name
                try {
                    extractOptarg(ca[0]);
                } catch (ArrayIndexOutOfBoundsException e) {
                    throw new SAGetoptException("Argument is expected for '" + ca[0] + "'");
                }
            }

            return ca[0];
        }

        throw new SAGetoptException("Invalid option '" + ca[0] + "'");
    }

    public String next(String optStr, String[] longOptStr) {

        if (_optind >= _argv.length || _argv[_optind] == null) {
            // All arguments processed
            return null;
        }

        String carg = _argv[_optind];
        _optarg = null;

        if (_optreset) {
            // End of option batch like '-abc' reached, expect option to start from '-'

            if (carg.isEmpty() || carg.charAt(0) != '-' || carg.equals("--")) {
                // Stop processing on -- or first non-option argument;
                return null;
            }

            if (carg.startsWith("--")) {
                // Handle long options, it can't be combined so it's simple
                if (longOptStr == null || longOptStr.length == 0) {
                    // No long options expected, stop options processing
                    return null;
                }
                ++ _optind;

                // at this point carg contains at least one character besides --
                carg = carg.substring(2);
                return processLongOptions(carg, longOptStr);
            }

            if (optStr == null || optStr.length() == 0) {
                // No short options
                return null;
            }

            // At this point carg[0] contains '-'
            _optreset = false;
            _optopt = 1;
        }

        char ch = carg.charAt(_optopt);

        // adjust pointer to next character
        _optopt += 1;

        // Okay, ready to process options like
        // -abc -d bla -ef

        int chIndex = optStr.indexOf(ch);
        if (chIndex == -1) {
            throw new SAGetoptException("Invalid option '" + ch + "'");
        }

        if (_optopt >= carg.length()) {
            _optind += 1;
            _optreset = true;
        }

        if (chIndex < optStr.length()-1 && optStr.charAt(chIndex+1) == ':') {
            // Argument expected
            extractOptarg(String.valueOf(ch));
        }

        return String.valueOf(ch);
    }
}