package io.burt.jmespath.function;

import org.junit.Before;
import org.junit.Test;
import org.junit.Ignore;

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

import io.burt.jmespath.Adapter;
import io.burt.jmespath.JmesPathType;
import io.burt.jmespath.node.ExpressionReferenceNode;
import io.burt.jmespath.node.PropertyNode;
import io.burt.jmespath.node.CurrentNode;
import io.burt.jmespath.jcf.JcfRuntime;

import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.containsString;

public class FunctionRegistryTest {
  private Adapter<Object> runtime = new JcfRuntime();

  private Object callFunction(String name, List<FunctionArgument<Object>> args) {
    return runtime.getFunction(name).call(runtime, args);
  }

  private List<FunctionArgument<Object>> createValueArguments(Object... values) {
    List<FunctionArgument<Object>> arguments = new ArrayList<>();
    for (Object value : values) {
      arguments.add(FunctionArgument.of(value));
    }
    return arguments;
  }

  private static class TestFunction extends BaseFunction {
    public TestFunction(String name, ArgumentConstraint argumentConstraints) {
      super(name, argumentConstraints);
    }

    @Override
    protected <T> T callFunction(Adapter<T> runtime, List<FunctionArgument<T>> arguments) {
      return arguments.get(0).value();
    }
  }

  @Test
  public void theDefaultRegistryContainsTheDefaultFunctions() {
    FunctionRegistry registry = FunctionRegistry.defaultRegistry();
    Object result;
    result = callFunction("to_string", createValueArguments(1L));
    assertThat(result, is((Object) "1"));
    result = callFunction("to_number", createValueArguments("1"));
    assertThat(result, is((Object) 1L));
  }

  @Test
  public void aCustomRegistryDoesNotContainTheDefaultFunctions() {
    FunctionRegistry registry = new FunctionRegistry(
      new TestFunction("foo", ArgumentConstraints.typeOf(JmesPathType.STRING))
    );
    try {
      callFunction("to_number", createValueArguments(1L));
    } catch (FunctionCallException fce) {
      assertThat(fce.getMessage(), containsString("Unknown function: \"to_number\""));
    }
  }

  @Test
  public void aCustomRegistryContainsTheProvidedFunctions() {
    FunctionRegistry customRegistry = new FunctionRegistry(
      new TestFunction("foo", ArgumentConstraints.typeOf(JmesPathType.STRING)),
      new TestFunction("bar", ArgumentConstraints.typeOf(JmesPathType.NUMBER))
    );
    runtime = new JcfRuntime(customRegistry);
    Object result;
    result = callFunction("foo", createValueArguments("hello"));
    assertThat(result, is((Object) "hello"));
    result = callFunction("bar", createValueArguments(42L));
    assertThat(result, is((Object) 42L));
  }

  @Test
  public void theLastFunctionIsUsedWhenThereAreDuplicatedNames() {
    FunctionRegistry customRegistry = new FunctionRegistry(
      new TestFunction("foo", ArgumentConstraints.typeOf(JmesPathType.STRING)),
      new TestFunction("foo", ArgumentConstraints.typeOf(JmesPathType.NUMBER))
    );
    runtime = new JcfRuntime(customRegistry);
    callFunction("foo", createValueArguments(3L));
  }

  @Test
  public void extendIsUsedToCreateRegistriesWithExtraFunctions() {
    FunctionRegistry defaultRegistry = FunctionRegistry.defaultRegistry();
    FunctionRegistry extendedRegistry = defaultRegistry.extend(
      new TestFunction("foo", ArgumentConstraints.typeOf(JmesPathType.STRING)),
      new TestFunction("bar", ArgumentConstraints.typeOf(JmesPathType.NUMBER))
    );
    runtime = new JcfRuntime(extendedRegistry);
    Object result;
    result = callFunction("to_number", createValueArguments("3"));
    assertThat(result, is((Object) 3L));
    result = callFunction("foo", createValueArguments("hello"));
    assertThat(result, is((Object) "hello"));
    result = callFunction("bar", createValueArguments(42L));
    assertThat(result, is((Object) 42L));
  }

  @Test
  public void functionsCanBeOverriddenWithExtend() {
    FunctionRegistry defaultRegistry = FunctionRegistry.defaultRegistry();
    FunctionRegistry extendedRegistry = defaultRegistry.extend(
      new TestFunction("to_number", ArgumentConstraints.typeOf(JmesPathType.STRING))
    );
    runtime = new JcfRuntime(extendedRegistry);
    Object result = callFunction("to_number", createValueArguments("hello"));
    assertThat(result, is((Object) "hello"));
  }
}

@@ -617,30 +617,23 @@
 atchers.
-containsString
+equalTo
 ;%0A%0Apubli
@@ -1544,76 +1544,8 @@
 ) %7B%0A
-    FunctionRegistry registry = FunctionRegistry.defaultRegistry();%0A
     
@@ -1551,32 +1551,32 @@
  Object result;%0A
+
     result = cal
@@ -1876,17 +1876,23 @@
 egistry 
-r
+customR
 egistry 
@@ -2009,185 +2009,46 @@
     
-try %7B%0A      callFunction(%22to_number%22, createValueArguments(1L));%0A    %7D catch (FunctionCallException fce) %7B%0A      assertThat(fce.getMessage(), containsString(%22Unknown f
+assertThat(customRegistry.getF
 unction
-: %5C
+(
 %22to_
@@ -2057,20 +2057,31 @@
 mber
-%5C%22%22));%0A    %7D
+%22), is(equalTo(null)));
 %0A  %7D

Fix FunctionRegistry tests