package picocli.examples.optionaloptionparams;

import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.IParameterConsumer;
import picocli.CommandLine.InitializationException;
import picocli.CommandLine.Model.ArgSpec;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Model.OptionSpec;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;

import java.util.Arrays;
import java.util.List;
import java.util.Stack;
import java.util.concurrent.Callable;

/**
 * This class demonstrates how to use a custom IParameterConsumer
 * to handle complex situations where the default picocli parser is insufficient.
 * <p>
 * We want to handle cases like this:
 * </p><pre>
 *  myapp --debug 8080 a.txt b.txt # debug port=8080
 *  myapp --debug a.txt b.txt      # debug port=12345 (the fallback value)
 *  myapp a.txt b.txt              # no debugging
 * </pre>
 * <p>
 * A custom IParamaterConsumer is needed so that input like
 * {@code myapp --debug a.txt} does not result in an error like this:
 * {@code "Invalid value for option '--debug': 'a.txt' is not an int"}.
 * </p>
 */
@Command(name = "myapp")
public class OptionalOptionParamDemo implements Callable<Integer> {
    @Option(names = "--debug", arity = "0..1", fallbackValue = "12345",
            parameterConsumer = CustomConsumer.class,
            description = "The port where to wait for debug connections; " +
                    "if no port is specified, ${FALLBACK-VALUE} is used. " +
                    "If this option is not specified at all, then no debugging port is opened."
    )
    int port;

    @Parameters()
    List<String> files;

    @Override
    public Integer call() {
        System.out.printf("Port=%d, files=%s%n", port, files);
        return 0;
    }

    public static void main(String[] ignored) {
        String[][] cases = new String[][] {
                {"--debug=999", "a.txt", "b.txt"},
                {"--debug", "999", "a.txt", "b.txt"},
                {"--debug", "--", "a.txt", "b.txt"},
                {"--debug=", "a.txt", "b.txt"},
                {"--debug", "a.txt", "b.txt"},
                {"a.txt", "b.txt"},
        };

        for (String[] args: cases) {
            System.out.println(Arrays.toString(args));
            new CommandLine(new OptionalOptionParamDemo()).execute(args);
            System.out.println();
        }
    }

    static class CustomConsumer implements IParameterConsumer {
        @Override
        public void consumeParameters(Stack<String> args, ArgSpec argSpec, CommandSpec commandSpec) {
            String arg = args.pop();
            try {
                int port = Integer.parseInt(arg);
                argSpec.setValue(port);
            } catch (Exception ex) {
                String fallbackValue = (argSpec.isOption()) ? ((OptionSpec) argSpec).fallbackValue() : null;
                try {
                    int fallbackPort = Integer.parseInt(fallbackValue);
                    argSpec.setValue(fallbackPort);
                } catch (Exception badFallbackValue) {
                    throw new InitializationException("FallbackValue for --debug must be an int", badFallbackValue);
                }
                args.push(arg); // put it back
            }
        }
    }
}