package keris.lang;

import java.util.*;


public module ARGUMENTS {
    
    interface ArgumentParser {
        Object parse(String string) throws ArgumentParserException;
    }
    
    class ArgumentParserException extends Exception {
        public ArgumentParserException() {
            super();
        }
        public ArgumentParserException(String message) {
            super(message);
        }
    }
    
    class DefaultParser implements ArgumentParser {
        public Object parse(String string) {
            return string;
        }
    }
    
    class EqualityParser implements ArgumentParser {
        public Object parse(String string) throws ArgumentParserException {
            int eqls = string.indexOf('=');
            if (eqls < 0)
                throw new ArgumentParserException(
                    "missing '=' in argument '" + string + "'");
            return new String[]{
                string.substring(0, eqls).trim(),
                string.substring(eqls + 1).trim()};
        }
    }
    
    class SetParser implements ArgumentParser {
    
        protected ArgumentParser elementParser;
        protected char separator;
        
        public SetParser(ArgumentParser elementParser, char separator) {
            this.elementParser = elementParser;
            this.separator = separator;
        }
        
        public Object parse(String string) throws ArgumentParserException {
            Vector res = new Vector();
            int start = 0;
            int end = string.indexOf(separator);
            while (end >= 0) {
                if (end > start)
                    res.add(elementParser.parse(string.substring(start, end)));
                start = ++end;
                end = string.indexOf(separator, end);
            }
            return res.toArray(new Object[res.size()]);
        }
    }
    
    public ArgumentParser defaultParser = new DefaultParser();
    
    public char optionPrefix = '-';
    
    private HashMap optionParsers = new HashMap();
    private HashMap prefixParsers = new HashMap();
    
    private HashMap options = new HashMap();
    private Vector others = new Vector();
    
    
    void addOption(String option) {
        optionParsers.put(option, null);
    }
    
    void addOption(String option, ArgumentParser parser) {
        optionParsers.put(option, parser);
    }
    
    void addOptionPrefix(String prefix) {
        prefixParsers.put(prefix, defaultParser);
    }
    
    void addOptionPrefix(String prefix, ArgumentParser parser) {
        prefixParsers.put(prefix, parser);
    }
    
    void addOptionValue(String key, Object value) {
        Object[] current = (Object[])options.get(key);
        if (current == null)
            options.put(key, new Object[]{value});
        else {
            Object[] next = new Object[current.length + 1];
            System.arraycopy(current, 0, next, 0, current.length);
            next[current.length] = value;
            options.put(key, next);
        }
    }
    
    void parse(String[] args) throws ArgumentParserException {
        if (args == null)
            return;
        String[] prefixes = (String[])prefixParsers.keySet().toArray(
            new String[prefixParsers.size()]);
        int i = 0;
        while (i < args.length)
            if ((args[i] == null) || (args[i].length() == 0))
                i++;
            else if (args[i].charAt(0) != optionPrefix)
                others.add(args[i++]);
            else if (optionParsers.containsKey(args[i])) {
                ArgumentParser parser = (ArgumentParser)
                    optionParsers.get(args[i]);
                if (parser == null)
                    addOptionValue(args[i++], null);
                else if ((i + 1) == args.length)
                    throw new ArgumentParserException(
                        "missing argument for '" + args[i] + "'");
                else
                    addOptionValue(args[i++], parser.parse(args[i++]));
            } else {
                int j = 0;
                while (j < prefixes.length) {
                    if (args[i].startsWith(prefixes[j]))
                        break;
                }
                if (j < prefixes.length) {
                    String arg = args[i++].substring(prefixes[j].length());
                    ArgumentParser parser = (ArgumentParser)
                        prefixParsers.get(prefixes[j]);
                    addOptionValue(prefixes[j], parser.parse(arg));
                } else
                    throw new ArgumentParserException(
                        "unknown option '" + args[i] + "'");
            }
    }
    
    boolean optionSet(String option) {
        return options.containsKey(option);
    }
    
    Object optionValue(String option) {
        Object[] res = (Object[])options.get(option);
        return (res == null) ? null : res[res.length - 1];
    }
    
    Object optionValue(String option, String prop) {
        Object[] res = (Object[])options.get(option);
        if (res == null)
            return null;
        for (int i = 0; i < res.length; i++) {
            Object[] pair = (Object[])res[i];
            if (pair[0].equals(prop))
                return pair[1];
        }
        return null;
    }
    
    Object[] optionValues(String option) {
        return (Object[])options.get(option);
    }
    
    String[] arguments() {
        return (String[])others.toArray(new String[others.size()]);
    }
    
    void clear() {
        others.clear();
        options.clear();
    }
}
