/*
 * The MIT License
 *
 * Copyright (c) 2016 Marcelo "Ataxexe" Guimarães <ataxexe@devnull.tools>
 *
 * Permission  is hereby granted, free of charge, to any person obtaining
 * a  copy  of  this  software  and  associated  documentation files (the
 * "Software"),  to  deal  in the Software without restriction, including
 * without  limitation  the  rights to use, copy, modify, merge, publish,
 * distribute,  sublicense,  and/or  sell  copies of the Software, and to
 * permit  persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * The  above  copyright  notice  and  this  permission  notice  shall be
 * included  in  all  copies  or  substantial  portions  of the Software.
 *
 * THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY OF ANY KIND,
 * EXPRESS  OR  IMPLIED,  INCLUDING  BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN  NO  EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM,  DAMAGES  OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT  OR  OTHERWISE,  ARISING  FROM,  OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE   OR   THE   USE   OR   OTHER   DEALINGS  IN  THE  SOFTWARE.
 */

package tools.devnull.boteco.message;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * A default implementation of a command extracted by a {@link CommandExtractor}.
 */
public class ExtractedCommand implements MessageCommand {

  private final IncomeMessage incomeMessage;
  private final String name;
  private final String rawArguments;
  private final Map<Class<?>, Function<String, ?>> functions;
  private final Map<String, Runnable> actions;
  private Consumer<String> defaultAction = (string) -> {
  };

  public ExtractedCommand(IncomeMessage incomeMessage, String name, String rawArguments) {
    this.incomeMessage = incomeMessage;
    this.name = name;
    this.rawArguments = rawArguments;
    this.functions = new HashMap<>();
    this.actions = new HashMap<>();

    this.functions.put(String.class, Function.identity());
    this.functions.put(Integer.class, Integer::parseInt);
    this.functions.put(int.class, Integer::parseInt);
    this.functions.put(Long.class, Long::parseLong);
    this.functions.put(long.class, Long::parseLong);
    this.functions.put(Double.class, Double::parseDouble);
    this.functions.put(double.class, Double::parseDouble);
    this.functions.put(List.class, string -> string.isEmpty() ?
        Collections.emptyList() :
        new ArrayList<>(Arrays.asList(string.split("\\s+")))
    );
  }

  @Override
  public String name() {
    return name;
  }

  @Override
  public <T> T as(Class<T> type) {
    return convert(rawArguments, type);
  }

  private <T> T convert(String string, Class<T> type) {
    Function<String, ?> function = functions.get(type);
    if (function == null) {
      function = new MessageCommandConverter<>(this.incomeMessage, type);
    }
    return (T) function.apply(string);
  }

  @Override
  public <T> MessageCommand on(String actionName, Class<T> parameterType, Consumer<T> consumer) {
    this.actions.put(actionName,
        () -> consumer.accept(convert(resolveParameterString(rawArguments), parameterType)));
    return this;
  }

  private String resolveParameterString(String string) {
    return string.contains(" ") ? string.replaceFirst("\\S+\\s", "").trim() : "";
  }

  @Override
  public MessageCommand on(String actionName, Consumer<String> action) {
    return on(actionName, String.class, action);
  }

  @Override
  public MessageCommand on(String actionName, Runnable action) {
    this.actions.put(actionName, action);
    return this;
  }

  @Override
  public void orElse(Consumer<String> action) {
    this.defaultAction = action;
    execute();
  }

  @Override
  public void orElseReturn(String message) {
    orElse((string) -> this.incomeMessage.reply(message));
    execute();
  }

  @Override
  public void execute() {
    String actionName = rawArguments.split("\\s+")[0];
    if (this.actions.containsKey(actionName)) {
      this.actions.get(actionName).run();
    } else {
      this.defaultAction.accept(rawArguments);
    }
  }

}

@@ -1505,16 +1505,52 @@
 unction;
+%0Aimport java.util.stream.Collectors;
 %0A%0A/**%0A *
@@ -1956,28 +1956,8 @@
 tion
- = (string) -%3E %7B%0A  %7D
 ;%0A%0A 
@@ -2782,16 +2782,185 @@
 %0A    );%0A
+    this.defaultAction = string -%3E this.incomeMessage.reply(%22Invalid action, possible actions: %5Cn%22 +%0A      actions.keySet().stream().collect(Collectors.joining(%22%5Cn%22)));%0A
   %7D%0A%0A  @

add a default response