/**
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 *
 *    Copyright 2014 Edgar Espina
 */
package io.jooby;

import javax.annotation.Nonnull;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class Route {

  public interface Aware {
    void setRoute(Route route);
  }

  public interface Decorator {
    @Nonnull Handler apply(@Nonnull Handler next);

    @Nonnull default Decorator then(@Nonnull Decorator next) {
      return h -> apply(next.apply(h));
    }

    @Nonnull default Handler then(@Nonnull Handler next) {
      return ctx -> apply(next).apply(ctx);
    }
  }

  public interface Before extends Decorator {
    @Nonnull @Override default Handler apply(@Nonnull Handler next) {
      return ctx -> {
        before(ctx);
        return next.apply(ctx);
      };
    }

    void before(@Nonnull Context ctx) throws Exception;
  }

  public interface After {

    @Nonnull default After then(@Nonnull After next) {
      return (ctx, result) -> apply(ctx, next.apply(ctx, result));
    }

    @Nonnull Object apply(@Nonnull Context ctx, Object result) throws Exception;
  }

  public interface Handler extends Serializable {

    @Nonnull Object apply(@Nonnull Context ctx) throws Exception;

    @Nonnull default Object execute(@Nonnull Context ctx) {
      try {
        return apply(ctx);
      } catch (Throwable x) {
        ctx.sendError(x);
        return x;
      }
    }

    @Nonnull default Handler then(After next) {
      return ctx -> next.apply(ctx, apply(ctx));
    }
  }

  public static final Handler NOT_FOUND = ctx -> ctx.sendError(new Err(StatusCode.NOT_FOUND));

  public static final Handler METHOD_NOT_ALLOWED = ctx -> ctx.sendError(new Err(StatusCode.METHOD_NOT_ALLOWED));

  public static final Handler FAVICON = ctx -> ctx.sendStatusCode(StatusCode.NOT_FOUND);

  private final Map<String, Parser> parsers;

  private String pattern;

  private String method;

  private List<String> pathKeys;

  private Handler handler;

  private Handler pipeline;

  private Renderer renderer;

  private Type returnType;

  public Route(String method, String pattern, List<String> pathKeys, Type returnType,
      Handler handler, Handler pipeline, Renderer renderer, Map<String, Parser> parsers) {
    this.method = method.toUpperCase();
    this.pattern = pattern;
    this.returnType = returnType;
    this.handler = handler;
    this.pipeline = pipeline;
    this.renderer = renderer;
    this.pathKeys = pathKeys;
    this.parsers = parsers;
  }

  public Route(String method, String pattern, Type returnType,
      Handler handler, Handler pipeline, Renderer renderer, Map<String, Parser> parsers) {
    this(method, pattern, Router.pathKeys(pattern), returnType, handler, pipeline, renderer, parsers);
  }

  public @Nonnull String pattern() {
    return pattern;
  }

  public  @Nonnull String method() {
    return method;
  }

  public @Nonnull List<String> pathKeys() {
    return pathKeys;
  }

  public @Nonnull Handler handler() {
    return handler;
  }

  public @Nonnull Handler pipeline() {
    return pipeline;
  }

  public Route pipeline(Route.Handler pipeline) {
    this.pipeline = pipeline;
    return this;
  }

  public @Nonnull Renderer renderer() {
    return renderer;
  }

  public @Nonnull Type returnType() {
    return returnType;
  }

  public Route returnType(@Nonnull Type returnType) {
    this.returnType = returnType;
    return this;
  }

  public @Nonnull Parser parser(MediaType contentType) {
    return parsers.getOrDefault(contentType.value(), Parser.UNSUPPORTED_MEDIA_TYPE);
  }

  @Override public String toString() {
    return method + " " + pattern;
  }
}

@@ -2651,32 +2651,41 @@
 %0A  public Route(
+@Nonnull 
 String method, S
@@ -2674,32 +2674,41 @@
 l String method,
+ @Nonnull
  String pattern,
@@ -2707,16 +2707,25 @@
 pattern,
+ @Nonnull
  List%3CSt
@@ -2739,16 +2739,31 @@
 athKeys,
+%0A      @Nonnull
  Type re
@@ -2763,38 +2763,41 @@
 Type returnType,
-%0A     
+ @Nonnull
  Handler handler
@@ -2789,32 +2789,41 @@
 Handler handler,
+ @Nonnull
  Handler pipelin
@@ -2816,32 +2816,47 @@
 andler pipeline,
+%0A      @Nonnull
  Renderer render
@@ -2850,32 +2850,41 @@
 nderer renderer,
+ @Nonnull
  Map%3CString, Par
@@ -3167,16 +3167,25 @@
 c Route(
+@Nonnull 
 String m
@@ -3190,16 +3190,25 @@
  method,
+ @Nonnull
  String 
@@ -3215,16 +3215,25 @@
 pattern,
+ @Nonnull
  Type re
@@ -3248,16 +3248,25 @@
 ,%0A      
+@Nonnull 
 Handler 
@@ -3273,16 +3273,25 @@
 handler,
+ @Nonnull
  Handler
@@ -3300,16 +3300,25 @@
 ipeline,
+ @Nonnull
  Rendere
@@ -3328,16 +3328,31 @@
 enderer,
+%0A      @Nonnull
  Map%3CStr
@@ -3809,24 +3809,33 @@
 %7D%0A%0A  public 
+@Nonnull 
 Route pipeli
@@ -4039,32 +4039,32 @@
 eturnType;%0A  %7D%0A%0A
-
   public Route r
@@ -4052,24 +4052,33 @@
 %7D%0A%0A  public 
+@Nonnull 
 Route return

add nonnull annotations to Route