/**
 * Apache License
 * Version 2.0, January 2004
 * http://www.apache.org/licenses/
 *
 * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 *
 * 1. Definitions.
 *
 * "License" shall mean the terms and conditions for use, reproduction,
 * and distribution as defined by Sections 1 through 9 of this document.
 *
 * "Licensor" shall mean the copyright owner or entity authorized by
 * the copyright owner that is granting the License.
 *
 * "Legal Entity" shall mean the union of the acting entity and all
 * other entities that control, are controlled by, or are under common
 * control with that entity. For the purposes of this definition,
 * "control" means (i) the power, direct or indirect, to cause the
 * direction or management of such entity, whether by contract or
 * otherwise, or (ii) ownership of fifty percent (50%) or more of the
 * outstanding shares, or (iii) beneficial ownership of such entity.
 *
 * "You" (or "Your") shall mean an individual or Legal Entity
 * exercising permissions granted by this License.
 *
 * "Source" form shall mean the preferred form for making modifications,
 * including but not limited to software source code, documentation
 * source, and configuration files.
 *
 * "Object" form shall mean any form resulting from mechanical
 * transformation or translation of a Source form, including but
 * not limited to compiled object code, generated documentation,
 * and conversions to other media types.
 *
 * "Work" shall mean the work of authorship, whether in Source or
 * Object form, made available under the License, as indicated by a
 * copyright notice that is included in or attached to the work
 * (an example is provided in the Appendix below).
 *
 * "Derivative Works" shall mean any work, whether in Source or Object
 * form, that is based on (or derived from) the Work and for which the
 * editorial revisions, annotations, elaborations, or other modifications
 * represent, as a whole, an original work of authorship. For the purposes
 * of this License, Derivative Works shall not include works that remain
 * separable from, or merely link (or bind by name) to the interfaces of,
 * the Work and Derivative Works thereof.
 *
 * "Contribution" shall mean any work of authorship, including
 * the original version of the Work and any modifications or additions
 * to that Work or Derivative Works thereof, that is intentionally
 * submitted to Licensor for inclusion in the Work by the copyright owner
 * or by an individual or Legal Entity authorized to submit on behalf of
 * the copyright owner. For the purposes of this definition, "submitted"
 * means any form of electronic, verbal, or written communication sent
 * to the Licensor or its representatives, including but not limited to
 * communication on electronic mailing lists, source code control systems,
 * and issue tracking systems that are managed by, or on behalf of, the
 * Licensor for the purpose of discussing and improving the Work, but
 * excluding communication that is conspicuously marked or otherwise
 * designated in writing by the copyright owner as "Not a Contribution."
 *
 * "Contributor" shall mean Licensor and any individual or Legal Entity
 * on behalf of whom a Contribution has been received by Licensor and
 * subsequently incorporated within the Work.
 *
 * 2. Grant of Copyright License. Subject to the terms and conditions of
 * this License, each Contributor hereby grants to You a perpetual,
 * worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 * copyright license to reproduce, prepare Derivative Works of,
 * publicly display, publicly perform, sublicense, and distribute the
 * Work and such Derivative Works in Source or Object form.
 *
 * 3. Grant of Patent License. Subject to the terms and conditions of
 * this License, each Contributor hereby grants to You a perpetual,
 * worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 * (except as stated in this section) patent license to make, have made,
 * use, offer to sell, sell, import, and otherwise transfer the Work,
 * where such license applies only to those patent claims licensable
 * by such Contributor that are necessarily infringed by their
 * Contribution(s) alone or by combination of their Contribution(s)
 * with the Work to which such Contribution(s) was submitted. If You
 * institute patent litigation against any entity (including a
 * cross-claim or counterclaim in a lawsuit) alleging that the Work
 * or a Contribution incorporated within the Work constitutes direct
 * or contributory patent infringement, then any patent licenses
 * granted to You under this License for that Work shall terminate
 * as of the date such litigation is filed.
 *
 * 4. Redistribution. You may reproduce and distribute copies of the
 * Work or Derivative Works thereof in any medium, with or without
 * modifications, and in Source or Object form, provided that You
 * meet the following conditions:
 *
 * (a) You must give any other recipients of the Work or
 * Derivative Works a copy of this License; and
 *
 * (b) You must cause any modified files to carry prominent notices
 * stating that You changed the files; and
 *
 * (c) You must retain, in the Source form of any Derivative Works
 * that You distribute, all copyright, patent, trademark, and
 * attribution notices from the Source form of the Work,
 * excluding those notices that do not pertain to any part of
 * the Derivative Works; and
 *
 * (d) If the Work includes a "NOTICE" text file as part of its
 * distribution, then any Derivative Works that You distribute must
 * include a readable copy of the attribution notices contained
 * within such NOTICE file, excluding those notices that do not
 * pertain to any part of the Derivative Works, in at least one
 * of the following places: within a NOTICE text file distributed
 * as part of the Derivative Works; within the Source form or
 * documentation, if provided along with the Derivative Works; or,
 * within a display generated by the Derivative Works, if and
 * wherever such third-party notices normally appear. The contents
 * of the NOTICE file are for informational purposes only and
 * do not modify the License. You may add Your own attribution
 * notices within Derivative Works that You distribute, alongside
 * or as an addendum to the NOTICE text from the Work, provided
 * that such additional attribution notices cannot be construed
 * as modifying the License.
 *
 * You may add Your own copyright statement to Your modifications and
 * may provide additional or different license terms and conditions
 * for use, reproduction, or distribution of Your modifications, or
 * for any such Derivative Works as a whole, provided Your use,
 * reproduction, and distribution of the Work otherwise complies with
 * the conditions stated in this License.
 *
 * 5. Submission of Contributions. Unless You explicitly state otherwise,
 * any Contribution intentionally submitted for inclusion in the Work
 * by You to the Licensor shall be under the terms and conditions of
 * this License, without any additional terms or conditions.
 * Notwithstanding the above, nothing herein shall supersede or modify
 * the terms of any separate license agreement you may have executed
 * with Licensor regarding such Contributions.
 *
 * 6. Trademarks. This License does not grant permission to use the trade
 * names, trademarks, service marks, or product names of the Licensor,
 * except as required for reasonable and customary use in describing the
 * origin of the Work and reproducing the content of the NOTICE file.
 *
 * 7. Disclaimer of Warranty. Unless required by applicable law or
 * agreed to in writing, Licensor provides the Work (and each
 * Contributor provides its Contributions) on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied, including, without limitation, any warranties or conditions
 * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 * PARTICULAR PURPOSE. You are solely responsible for determining the
 * appropriateness of using or redistributing the Work and assume any
 * risks associated with Your exercise of permissions under this License.
 *
 * 8. Limitation of Liability. In no event and under no legal theory,
 * whether in tort (including negligence), contract, or otherwise,
 * unless required by applicable law (such as deliberate and grossly
 * negligent acts) or agreed to in writing, shall any Contributor be
 * liable to You for damages, including any direct, indirect, special,
 * incidental, or consequential damages of any character arising as a
 * result of this License or out of the use or inability to use the
 * Work (including but not limited to damages for loss of goodwill,
 * work stoppage, computer failure or malfunction, or any and all
 * other commercial damages or losses), even if such Contributor
 * has been advised of the possibility of such damages.
 *
 * 9. Accepting Warranty or Additional Liability. While redistributing
 * the Work or Derivative Works thereof, You may choose to offer,
 * and charge a fee for, acceptance of support, warranty, indemnity,
 * or other liability obligations and/or rights consistent with this
 * License. However, in accepting such obligations, You may act only
 * on Your own behalf and on Your sole responsibility, not on behalf
 * of any other Contributor, and only if You agree to indemnify,
 * defend, and hold each Contributor harmless for any liability
 * incurred by, or claims asserted against, such Contributor by reason
 * of your accepting any such warranty or additional liability.
 *
 * END OF TERMS AND CONDITIONS
 *
 * APPENDIX: How to apply the Apache License to your work.
 *
 * To apply the Apache License to your work, attach the following
 * boilerplate notice, with the fields enclosed by brackets "{}"
 * replaced with your own identifying information. (Don't include
 * the brackets!)  The text should be enclosed in the appropriate
 * comment syntax for the file format. We also recommend that a
 * file or class name and description of purpose be included on the
 * same "printed page" as the copyright notice for easier
 * identification within third-party archives.
 *
 * Copyright 2014 Edgar Espina
 *
 * 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.
 */
package org.jooby.requery;

import com.google.inject.name.Names;
import io.requery.sql.KotlinEntityDataStore;
import static java.util.Objects.requireNonNull;

import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;

import javax.inject.Provider;
import javax.sql.DataSource;

import org.jooby.Env;
import org.jooby.Env.ServiceKey;
import org.jooby.Jooby.Module;
import org.jooby.jdbc.Jdbc;
import org.slf4j.bridge.SLF4JBridgeHandler;

import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.util.Types;
import com.typesafe.config.Config;

import io.requery.EntityStore;
import io.requery.TransactionListener;
import io.requery.async.CompletableEntityStore;
import io.requery.async.CompletionStageEntityStore;
import io.requery.meta.EntityModel;
import io.requery.reactivex.ReactiveEntityStore;
import io.requery.reactivex.ReactiveSupport;
import io.requery.reactor.ReactorEntityStore;
import io.requery.sql.Configuration;
import io.requery.sql.ConfigurationBuilder;
import io.requery.sql.EntityDataStore;
import io.requery.sql.EntityStateListener;
import io.requery.sql.SchemaModifier;
import io.requery.sql.StatementListener;
import io.requery.sql.TableCreationMode;

/**
 *
requery

 *

 * Safe, clean and efficient database access via
 * Requery.
 *

 *
 *
usage

 *
 *
{@code
 * {
 *   use(new Jdbc());
 *
 *   use(new Requery(Models.DEFAULT));
 *
 *   get("/people", () -> {
 *     EntityStore store = require(EntityStore.class);
 *     return store.select(Person.class)
 *        .where(Person.ID.eq(req.param("id").intValue()))
 *        .get()
 *        .first();
 *   });
 * }
 * }

 *
 *

 * This module requires a {@link DataSource} connection. That's why you also need the
 * {@link Jdbc} module.
 *

 *
 *
code generation

 *
 *
maven

 *

 * We do provide code generation via Maven profile. All you have to do is to write a
 * requery.activator file inside the src/etc folder. File presence
 * triggers Requery annotation processor and generated contents.
 *

 *
 *

 * Generated content can be found at: target/generated-sources. You can change the
 * default output location by setting the build property requery.output in your
 * pom.xml.
 *

 *
 *
gradle

 *

 * Please refer to requery
 * documentation for Gradle.
 *

 *
 *
schema generation

 *
 *
{@code
 * {
 *   use(new Requery(Models.DEFAULT)
 *     .schema(TableCreationMode.DROP_CREATE)
 *   );
 * }
 * }

 *
 *

 * Optionally, schema generation could be set from .conf file via requery.schema
 * property.
 *

 *
 *
listeners

 *
 *
{@code
 * public class MyListener implements EntityStateListener<Person> {
 *    @Inject
 *    public MyListener(Dependency dep) {
 *      this.dep = dep;
 *    }
 *
 *    ...
 * }
 *
 * {
 *   use(new Requery(Models.DEFAULT)
 *     .entityStateListener(MyListener.class)
 *   );
 * }
 * }

 *
 *

 * Support for {@link TransactionListener} and {@link StatementListener} is also provided:
 *

 *
 *
{@code
 * {
 *   use(new Requery(Models.DEFAULT)
 *     .statementListener(MyStatementListener.class)
 *     .transactionListener(TransactionListener.class)
 *   );
 * }
 * }

 *
 *

 * You can add as many listener as you need. Each listener will be created by Guice
 *

 *
 *
Type-Safe injection

 *

 * If you love DAO like classes, we are happy to tell you that it you easily inject
 * type-safe {@link EntityStore}:
 *

 *
 *
{@code
 *
 * public class PersonDAO {
 *   private EntityStore<Persistable, Person> store;
 *
 *   @Inject
 *   public PersonDAO(EntityStore<<Persistable, Person> store) {
 *     this.store = store;
 *   }
 * }
 * }

 *
 *

 * Please note we don't inject a raw {@link EntityStore}. Instead we ask for a
 * Person {@link EntityStore}. You can safely inject a {@link EntityStore} per each of
 * your domain objects.
 *

 *
 *
async and reactive idioms

 *
 *

 * Rxjava:
 *

 *
 *
{@code
 * {
 *   use(Requery.reactive(Models.DEFAULT));
 *
 *   get("/", () -> {
 *     ReactiveEntityStore store = require(ReactiveEntityStore.class);
 *     // work with reactive store
 *   });
 * }
 * }

 *
 *

 * Reactor:
 *

 *
 *
{@code
 * {
 *   use(Requery.reactor(Models.DEFAULT));
 *
 *   get("/", () -> {
 *     ReactorEntityStore store = require(ReactorEntityStore.class);
 *     // work with reactor store
 *   });
 * }
 * }

 *
 *

 * Java 8:
 *

 *
 *
{@code
 * {
 *   use(Requery.completionStage(Models.DEFAULT));
 *
 *   get("/", () -> {
 *     CompletionStageEntityStore store = require(CompletionStageEntityStore.class);
 *     // work with reactor store
 *   });
 * }
 * }

 *
 *
advanced configuration

 *

 * Advanced configuration is available via callback function:
 *

 *
 *
{@code
 * {
 *   use(new Requery(Models.DEFAULT)
 *     .doWith(builder -> {
 *       builder.useDefaultLogging();
 *       ....
 *     })
 *   );
 * }
 * }

 *
 * @author edgar
 */
@SuppressWarnings({"rawtypes", "unchecked"})
public class Requery implements Module {

  static {
    if (!SLF4JBridgeHandler.isInstalled()) {
      SLF4JBridgeHandler.removeHandlersForRootLogger();
      SLF4JBridgeHandler.install();
    }
  }

  private String name;

  private final EntityModel model;

  private TableCreationMode schema;

  private final Function<Configuration, Object> store;

  private final Class<?> storeType;

  private List<Class> states = new LinkedList<>();

  private List<Class> statements = new LinkedList<>();

  private List<Class> transactions = new LinkedList<>();

  private BiConsumer<Config, ConfigurationBuilder> callback;

  private Requery(final String name, final Class<?> storeType, final EntityModel model,
      final Function<Configuration, Object> store) {
    this.name = name;
    this.storeType = storeType;
    this.model = model;
    this.store = store;
  }

  /**
   * Creates a new {@link Requery} module.
   *
   * @param name Database name.
   * @param model Entity model.
   */
  public Requery(final String name, final EntityModel model) {
    this(name, EntityStore.class, model, c -> new EntityDataStore<>(c));
  }

  /**
   * Creates a new {@link Requery} module.
   *
   * @param model Entity model.
   */
  public Requery(final EntityModel model) {
    this("db", model);
  }

  /**
   * Advanced configuration callback:
   *
   *
{@code
   *
   *   use(new Requery(Models.DEFAULT)
   *     .doWith((conf, builder) -> {
   *       builder.useDefaultLogging();
   *     });
   *
   * }

   *
   * @param configurer Configurer callback.
   * @return This module.
   */
  public Requery doWith(final BiConsumer<Config, ConfigurationBuilder> configurer) {
    this.callback = requireNonNull(configurer, "Configurer callback required.");
    return this;
  }

  /**
   * Advanced configuration callback:
   *
   *
{@code
   *
   *   use(new Requery(Models.DEFAULT)
   *     .doWith(builder -> {
   *       builder.useDefaultLogging();
   *     })
   *   );
   *
   * }

   *
   * @param configurer Configurer callback.
   * @return This module.
   */
  public Requery doWith(final Consumer<ConfigurationBuilder> configurer) {
    requireNonNull(configurer, "Configurer callback required.");
    return doWith((c, b) -> configurer.accept(b));
  }

  /**
   * Run the give schema command at application startup time.
   *
   * @param schema Command to run.
   * @return This module.
   */
  public Requery schema(final TableCreationMode schema) {
    this.schema = schema;
    return this;
  }

  /**
   * Add an {@link EntityStateListener}. The listener will be created by Guice.
   *
   * @param listener Guice entity listener.
   * @return This module.
   */
  public Requery entityStateListener(final Class<? extends EntityStateListener<?>> listener) {
    this.states.add(listener);
    return this;
  }

  /**
   * Add an {@link StatementListener}. The listener will be created by Guice.
   *
   * @param listener Guice statement listener.
   * @return This module.
   */
  public Requery statementListener(final Class<? extends StatementListener> listener) {
    this.statements.add(listener);
    return this;
  }

  /**
   * Add an {@link TransactionListener}. The listener will be created by Guice.
   *
   * @param listener Guice transaction listener.
   * @return This module.
   */
  public Requery transactionListener(final Class<? extends TransactionListener> listener) {
    this.transactions.add(listener);
    return this;
  }

  @Override
  public void configure(final Env env, final Config conf, final Binder binder) {
    Key<DataSource> dskey = Key.get(DataSource.class, Names.named(name));
    DataSource dataSource = env.get(dskey)
        .orElseThrow(() -> new NoSuchElementException("DataSource missing: " + dskey));

    ConfigurationBuilder builder = new ConfigurationBuilder(dataSource, model);
    if (callback != null) {
      callback.accept(conf, builder);
    }
    Configuration configuration = builder.build();
    Object newStore = store.apply(configuration);

    ServiceKey keys = env.serviceKey();
    Consumer bind = k -> binder.bind((Key) k).toInstance(newStore);

    /**
     * For each model class we publish a type-safe EntityStore, so users can easily inject stores
     * for specific types.
     */
    model.getTypes().forEach(it -> {
      // Person(result) extends AbstractPerson implements Persistable (base)
      Class target = it.getClassType();
      Class base = target.getInterfaces()[0];
      // bind: Store<Model>
      bind.accept(Key.get(Types.newParameterizedType(storeType, target)));
      // bind: Store<Persistable, Model>
      bind.accept(Key.get(Types.newParameterizedType(storeType, base, target)));
    });
    keys.generate(storeType, model.getName(), bind);

    env.onStart(registry -> {
      schema(conf, schema, schema -> new SchemaModifier(dataSource, model).createTables(schema));
      states
          .forEach(t -> builder.addEntityStateListener((EntityStateListener) registry.require(t)));
      statements
          .forEach(t -> builder.addStatementListener((StatementListener) registry.require(t)));
      transactions.forEach(t -> builder
          .addTransactionListenerFactory(() -> (TransactionListener) registry.require(t)));
    });
  }

  /**
   * Creates a Requery module with RxJava data store.
   *
   *
{@code
   * {
   *   use(Requery.reactive(Models.DEFAULT));
   *
   *   get("/", () -> {
   *     ReactiveEntityStore store = require(ReactiveEntityStore.class);
   *     // work with reactive store
   *   });
   * }
   * }

   *
   * @param name Database name.
   * @param model Entity model.
   * @return A new {@link Requery} module.
   */
  public static Requery reactive(final String name, final EntityModel model) {
    return new Requery(name, ReactiveEntityStore.class, model,
        conf -> ReactiveSupport.toReactiveStore(new EntityDataStore<>(conf)));
  }

  /**
   * Creates a Requery module with RxJava data store.
   *
   *
{@code
   * {
   *   use(Requery.reactive(Models.DEFAULT));
   *
   *   get("/", () -> {
   *     ReactiveEntityStore store = require(ReactiveEntityStore.class);
   *     // work with reactive store
   *   });
   * }
   * }

   *
   * @param model Entity model.
   * @return A new {@link Requery} module.
   */
  public static Requery reactive(final EntityModel model) {
    return reactive("db", model);
  }

  /**
   * Creates a Requery module with Reactor data store.
   *
   *
{@code
   * {
   *   use(Requery.reactor(Models.DEFAULT));
   *
   *   get("/", () -> {
   *     ReactorEntityStore store = require(ReactorEntityStore.class);
   *     // work with reactive store
   *   });
   * }
   * }

   *
   * @param name Database name.
   * @param model Entity model.
   * @return A new {@link Requery} module.
   */
  public static Requery reactor(final String name, final EntityModel model) {
    return new Requery(name, ReactorEntityStore.class, model,
        conf -> new ReactorEntityStore<>(new EntityDataStore<>(conf)));
  }

  /**
   * Creates a Requery module with Reactor data store.
   *
   *
{@code
   * {
   *   use(Requery.reactor(Models.DEFAULT));
   *
   *   get("/", () -> {
   *     ReactorEntityStore store = require(ReactorEntityStore.class);
   *     // work with reactive store
   *   });
   * }
   * }

   *
   * @param model Entity model.
   * @return A new {@link Requery} module.
   */
  public static Requery reactor(final EntityModel model) {
    return reactor("db", model);
  }

  /**
   * Creates an async Requery module with Java 8 data store.
   *
   *
{@code
   * {
   *   use(Requery.completionStage(Models.DEFAULT));
   *
   *   get("/", () -> {
   *     CompletionStageEntityStore store = require(CompletionStageEntityStore.class);
   *     // work with reactive store
   *   });
   * }
   * }

   *
   * @param name Database name.
   * @param model Entity model.
   * @return A new {@link Requery} module.
   */
  public static Requery completionStage(final String name, final EntityModel model) {
    return new Requery(name, CompletionStageEntityStore.class, model,
        conf -> new CompletableEntityStore(new EntityDataStore<>(conf)));
  }

  /**
   * Creates an async Requery module with Java 8 data store.
   *
   *
{@code
   * {
   *   use(Requery.completionStage(Models.DEFAULT));
   *
   *   get("/", () -> {
   *     CompletionStageEntityStore store = require(CompletionStageEntityStore.class);
   *     // work with reactive store
   *   });
   * }
   * }

   *
   * @param model Entity model.
   * @return A new {@link Requery} module.
   */
  public static Requery completionStage(final EntityModel model) {
    return completionStage("db", model);
  }

  /**
   * Creates a Kotlin Requery module.
   *
   *
{@code
   * {
   *   use(Requery.kotlin(Models.DEFAULT));
   *
   *   get("/", () -> {
   *     KotlinEntityDataStore store = require(KotlinEntityDataStore.class);
   *     // work with kotlin store
   *   });
   * }
   * }

   *
   * @param name Database name.
   * @param model Entity model.
   * @return A new {@link Requery} module.
   */
  public static Requery kotlin(final String name, final EntityModel model) {
    return new Requery(name, KotlinEntityDataStore.class, model,
        conf -> new KotlinEntityDataStore<>(conf));
  }

  /**
   * Creates a Kotlin Requery module.
   *
   *
{@code
   * {
   *   use(Requery.kotlin(Models.DEFAULT));
   *
   *   get("/", () -> {
   *     KotlinEntityDataStore store = require(KotlinEntityDataStore.class);
   *     // work with kotlin store
   *   });
   * }
   * }

   *
   * @param model Entity model.
   * @return A new {@link Requery} module.
   */
  public static Requery kotlin(final EntityModel model) {
    return kotlin("db", model);
  }

  private void schema(final Config conf, final TableCreationMode schema,
      final Consumer<TableCreationMode> callback) {
    if (schema != null) {
      callback.accept(schema);
    }
    if (conf.hasPath("requery.schema")) {
      callback.accept(TableCreationMode.valueOf(conf.getString("requery.schema").toUpperCase()));
    }
  }
}


/**
 *                                  Apache License
 *                            Version 2.0, January 2004
 *                         http://www.apache.org/licenses/
 *
 *    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 *
 *    1. Definitions.
 *
 *       "License" shall mean the terms and conditions for use, reproduction,
 *       and distribution as defined by Sections 1 through 9 of this document.
 *
 *       "Licensor" shall mean the copyright owner or entity authorized by
 *       the copyright owner that is granting the License.
 *
 *       "Legal Entity" shall mean the union of the acting entity and all
 *       other entities that control, are controlled by, or are under common
 *       control with that entity. For the purposes of this definition,
 *       "control" means (i) the power, direct or indirect, to cause the
 *       direction or management of such entity, whether by contract or
 *       otherwise, or (ii) ownership of fifty percent (50%) or more of the
 *       outstanding shares, or (iii) beneficial ownership of such entity.
 *
 *       "You" (or "Your") shall mean an individual or Legal Entity
 *       exercising permissions granted by this License.
 *
 *       "Source" form shall mean the preferred form for making modifications,
 *       including but not limited to software source code, documentation
 *       source, and configuration files.
 *
 *       "Object" form shall mean any form resulting from mechanical
 *       transformation or translation of a Source form, including but
 *       not limited to compiled object code, generated documentation,
 *       and conversions to other media types.
 *
 *       "Work" shall mean the work of authorship, whether in Source or
 *       Object form, made available under the License, as indicated by a
 *       copyright notice that is included in or attached to the work
 *       (an example is provided in the Appendix below).
 *
 *       "Derivative Works" shall mean any work, whether in Source or Object
 *       form, that is based on (or derived from) the Work and for which the
 *       editorial revisions, annotations, elaborations, or other modifications
 *       represent, as a whole, an original work of authorship. For the purposes
 *       of this License, Derivative Works shall not include works that remain
 *       separable from, or merely link (or bind by name) to the interfaces of,
 *       the Work and Derivative Works thereof.
 *
 *       "Contribution" shall mean any work of authorship, including
 *       the original version of the Work and any modifications or additions
 *       to that Work or Derivative Works thereof, that is intentionally
 *       submitted to Licensor for inclusion in the Work by the copyright owner
 *       or by an individual or Legal Entity authorized to submit on behalf of
 *       the copyright owner. For the purposes of this definition, "submitted"
 *       means any form of electronic, verbal, or written communication sent
 *       to the Licensor or its representatives, including but not limited to
 *       communication on electronic mailing lists, source code control systems,
 *       and issue tracking systems that are managed by, or on behalf of, the
 *       Licensor for the purpose of discussing and improving the Work, but
 *       excluding communication that is conspicuously marked or otherwise
 *       designated in writing by the copyright owner as "Not a Contribution."
 *
 *       "Contributor" shall mean Licensor and any individual or Legal Entity
 *       on behalf of whom a Contribution has been received by Licensor and
 *       subsequently incorporated within the Work.
 *
 *    2. Grant of Copyright License. Subject to the terms and conditions of
 *       this License, each Contributor hereby grants to You a perpetual,
 *       worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 *       copyright license to reproduce, prepare Derivative Works of,
 *       publicly display, publicly perform, sublicense, and distribute the
 *       Work and such Derivative Works in Source or Object form.
 *
 *    3. Grant of Patent License. Subject to the terms and conditions of
 *       this License, each Contributor hereby grants to You a perpetual,
 *       worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 *       (except as stated in this section) patent license to make, have made,
 *       use, offer to sell, sell, import, and otherwise transfer the Work,
 *       where such license applies only to those patent claims licensable
 *       by such Contributor that are necessarily infringed by their
 *       Contribution(s) alone or by combination of their Contribution(s)
 *       with the Work to which such Contribution(s) was submitted. If You
 *       institute patent litigation against any entity (including a
 *       cross-claim or counterclaim in a lawsuit) alleging that the Work
 *       or a Contribution incorporated within the Work constitutes direct
 *       or contributory patent infringement, then any patent licenses
 *       granted to You under this License for that Work shall terminate
 *       as of the date such litigation is filed.
 *
 *    4. Redistribution. You may reproduce and distribute copies of the
 *       Work or Derivative Works thereof in any medium, with or without
 *       modifications, and in Source or Object form, provided that You
 *       meet the following conditions:
 *
 *       (a) You must give any other recipients of the Work or
 *           Derivative Works a copy of this License; and
 *
 *       (b) You must cause any modified files to carry prominent notices
 *           stating that You changed the files; and
 *
 *       (c) You must retain, in the Source form of any Derivative Works
 *           that You distribute, all copyright, patent, trademark, and
 *           attribution notices from the Source form of the Work,
 *           excluding those notices that do not pertain to any part of
 *           the Derivative Works; and
 *
 *       (d) If the Work includes a "NOTICE" text file as part of its
 *           distribution, then any Derivative Works that You distribute must
 *           include a readable copy of the attribution notices contained
 *           within such NOTICE file, excluding those notices that do not
 *           pertain to any part of the Derivative Works, in at least one
 *           of the following places: within a NOTICE text file distributed
 *           as part of the Derivative Works; within the Source form or
 *           documentation, if provided along with the Derivative Works; or,
 *           within a display generated by the Derivative Works, if and
 *           wherever such third-party notices normally appear. The contents
 *           of the NOTICE file are for informational purposes only and
 *           do not modify the License. You may add Your own attribution
 *           notices within Derivative Works that You distribute, alongside
 *           or as an addendum to the NOTICE text from the Work, provided
 *           that such additional attribution notices cannot be construed
 *           as modifying the License.
 *
 *       You may add Your own copyright statement to Your modifications and
 *       may provide additional or different license terms and conditions
 *       for use, reproduction, or distribution of Your modifications, or
 *       for any such Derivative Works as a whole, provided Your use,
 *       reproduction, and distribution of the Work otherwise complies with
 *       the conditions stated in this License.
 *
 *    5. Submission of Contributions. Unless You explicitly state otherwise,
 *       any Contribution intentionally submitted for inclusion in the Work
 *       by You to the Licensor shall be under the terms and conditions of
 *       this License, without any additional terms or conditions.
 *       Notwithstanding the above, nothing herein shall supersede or modify
 *       the terms of any separate license agreement you may have executed
 *       with Licensor regarding such Contributions.
 *
 *    6. Trademarks. This License does not grant permission to use the trade
 *       names, trademarks, service marks, or product names of the Licensor,
 *       except as required for reasonable and customary use in describing the
 *       origin of the Work and reproducing the content of the NOTICE file.
 *
 *    7. Disclaimer of Warranty. Unless required by applicable law or
 *       agreed to in writing, Licensor provides the Work (and each
 *       Contributor provides its Contributions) on an "AS IS" BASIS,
 *       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 *       implied, including, without limitation, any warranties or conditions
 *       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 *       PARTICULAR PURPOSE. You are solely responsible for determining the
 *       appropriateness of using or redistributing the Work and assume any
 *       risks associated with Your exercise of permissions under this License.
 *
 *    8. Limitation of Liability. In no event and under no legal theory,
 *       whether in tort (including negligence), contract, or otherwise,
 *       unless required by applicable law (such as deliberate and grossly
 *       negligent acts) or agreed to in writing, shall any Contributor be
 *       liable to You for damages, including any direct, indirect, special,
 *       incidental, or consequential damages of any character arising as a
 *       result of this License or out of the use or inability to use the
 *       Work (including but not limited to damages for loss of goodwill,
 *       work stoppage, computer failure or malfunction, or any and all
 *       other commercial damages or losses), even if such Contributor
 *       has been advised of the possibility of such damages.
 *
 *    9. Accepting Warranty or Additional Liability. While redistributing
 *       the Work or Derivative Works thereof, You may choose to offer,
 *       and charge a fee for, acceptance of support, warranty, indemnity,
 *       or other liability obligations and/or rights consistent with this
 *       License. However, in accepting such obligations, You may act only
 *       on Your own behalf and on Your sole responsibility, not on behalf
 *       of any other Contributor, and only if You agree to indemnify,
 *       defend, and hold each Contributor harmless for any liability
 *       incurred by, or claims asserted against, such Contributor by reason
 *       of your accepting any such warranty or additional liability.
 *
 *    END OF TERMS AND CONDITIONS
 *
 *    APPENDIX: How to apply the Apache License to your work.
 *
 *       To apply the Apache License to your work, attach the following
 *       boilerplate notice, with the fields enclosed by brackets "{}"
 *       replaced with your own identifying information. (Don't include
 *       the brackets!)  The text should be enclosed in the appropriate
 *       comment syntax for the file format. We also recommend that a
 *       file or class name and description of purpose be included on the
 *       same "printed page" as the copyright notice for easier
 *       identification within third-party archives.
 *
 *    Copyright 2014 Edgar Espina
 *
 *    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.
 */
package org.jooby.unbescape;

import static java.util.Objects.requireNonNull;

import org.jooby.Env;
import org.jooby.Jooby.Module;
import org.unbescape.css.CssEscape;
import org.unbescape.css.CssStringEscapeLevel;
import org.unbescape.css.CssStringEscapeType;
import org.unbescape.html.HtmlEscape;
import org.unbescape.html.HtmlEscapeLevel;
import org.unbescape.html.HtmlEscapeType;
import org.unbescape.javascript.JavaScriptEscape;
import org.unbescape.javascript.JavaScriptEscapeLevel;
import org.unbescape.javascript.JavaScriptEscapeType;
import org.unbescape.json.JsonEscape;
import org.unbescape.json.JsonEscapeLevel;
import org.unbescape.json.JsonEscapeType;
import org.unbescape.uri.UriEscape;

import com.google.inject.Binder;
import com.typesafe.config.Config;

/**
 *
unbescape

 *

 * Unbescape is a Java library aimed at
 * performing fully-featured and high-performance escape and unescape operations for:
 * HTML, JavaScript and lot more.
 *

 *
 *
exports

 *

 *
html escaper.

 *
js escaper.

 *
json escaper.

 *
css escaper.

 *
uri escaper.

 *
queryParam escaper.

 *
uriFragmentId escaper.

 *

 *
 *
usage

 *
{@code
 * {
 *   use(new XSS());
 *
 *   post("/", req -> {
 *     String safeHtml = req.param("text", "html").value();
 *   });
 * }
 * }

 *
 *

 * Nested context are supported by providing multiple encoders:
 *

 *
 *
{@code
 * {
 *   use(new XSS());
 *
 *   post("/", req -> {
 *     String safeHtml = req.param("text", "js", "html", "uri").value();
 *   });
 * }
 * }

 *
 *

 * Encoders run in the order they are provided.
 *

 *
 * @author edgar
 * @since 1.0.0
 */
public class XSS implements Module {

  private HtmlEscapeType htmltype = HtmlEscapeType.HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL;
  private HtmlEscapeLevel htmllevel = HtmlEscapeLevel.LEVEL_3_ALL_NON_ALPHANUMERIC;

  private JavaScriptEscapeLevel jslevel = JavaScriptEscapeLevel.LEVEL_3_ALL_NON_ALPHANUMERIC;
  private JavaScriptEscapeType jstype = JavaScriptEscapeType.SINGLE_ESCAPE_CHARS_DEFAULT_TO_XHEXA_AND_UHEXA;

  private JsonEscapeLevel jsonlevel = JsonEscapeLevel.LEVEL_3_ALL_NON_ALPHANUMERIC;
  private JsonEscapeType jsontype = JsonEscapeType.SINGLE_ESCAPE_CHARS_DEFAULT_TO_UHEXA;

  private CssStringEscapeType csstype = CssStringEscapeType.BACKSLASH_ESCAPES_DEFAULT_TO_COMPACT_HEXA;
  private CssStringEscapeLevel csslevel = CssStringEscapeLevel.LEVEL_3_ALL_NON_ALPHANUMERIC;

  /**
   * Set JavaScript escape type and level.
   *
   * @param type Type.
   * @param level Level.
   * @return This module.
   */
  public XSS js(final JavaScriptEscapeType type, final JavaScriptEscapeLevel level) {
    this.jslevel = requireNonNull(level, "Level required.");
    this.jstype = requireNonNull(type, "Type required.");
    return this;
  }

  /**
   * Set HTML escape type and level.
   *
   * @param type Type.
   * @param level Level.
   * @return This module.
   */
  public XSS html(final HtmlEscapeType type, final HtmlEscapeLevel level) {
    this.htmllevel = requireNonNull(level, "Level required.");
    this.htmltype = requireNonNull(type, "Type required.");
    return this;
  }

  /**
   * Set JSON escape type and level.
   *
   * @param type Type.
   * @param level Level.
   * @return This module.
   */
  public XSS json(final JsonEscapeType type, final JsonEscapeLevel level) {
    this.jsonlevel = requireNonNull(level, "Level required.");
    this.jsontype = requireNonNull(type, "Type required.");
    return this;
  }

  /**
   * Set CSS escape type and level.
   *
   * @param type Type.
   * @param level Level.
   * @return This module.
   */
  public XSS css(final CssStringEscapeType type, final CssStringEscapeLevel level) {
    this.csslevel = requireNonNull(level, "Level required.");
    this.csstype = requireNonNull(type, "Type required.");
    return this;
  }

  @Override
  public void configure(final Env env, final Config conf, final Binder binder) {
    env.xss("html", it -> HtmlEscape.escapeHtml(it, htmltype, htmllevel))
        .xss("js", it -> JavaScriptEscape.escapeJavaScript(it, jstype, jslevel))
        .xss("json", it -> JsonEscape.escapeJson(it, jsontype, jsonlevel))
        .xss("css", it -> CssEscape.escapeCssString(it, csstype, csslevel))
        .xss("uri", UriEscape::escapeUriPath)
        .xss("queryParam", UriEscape::escapeUriQueryParam)
        .xss("uriFragmentId", UriEscape::escapeUriFragmentId);
  }

}


/**
 * Apache License
 * Version 2.0, January 2004
 * http://www.apache.org/licenses/
 *
 * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 *
 * 1. Definitions.
 *
 * "License" shall mean the terms and conditions for use, reproduction,
 * and distribution as defined by Sections 1 through 9 of this document.
 *
 * "Licensor" shall mean the copyright owner or entity authorized by
 * the copyright owner that is granting the License.
 *
 * "Legal Entity" shall mean the union of the acting entity and all
 * other entities that control, are controlled by, or are under common
 * control with that entity. For the purposes of this definition,
 * "control" means (i) the power, direct or indirect, to cause the
 * direction or management of such entity, whether by contract or
 * otherwise, or (ii) ownership of fifty percent (50%) or more of the
 * outstanding shares, or (iii) beneficial ownership of such entity.
 *
 * "You" (or "Your") shall mean an individual or Legal Entity
 * exercising permissions granted by this License.
 *
 * "Source" form shall mean the preferred form for making modifications,
 * including but not limited to software source code, documentation
 * source, and configuration files.
 *
 * "Object" form shall mean any form resulting from mechanical
 * transformation or translation of a Source form, including but
 * not limited to compiled object code, generated documentation,
 * and conversions to other media types.
 *
 * "Work" shall mean the work of authorship, whether in Source or
 * Object form, made available under the License, as indicated by a
 * copyright notice that is included in or attached to the work
 * (an example is provided in the Appendix below).
 *
 * "Derivative Works" shall mean any work, whether in Source or Object
 * form, that is based on (or derived from) the Work and for which the
 * editorial revisions, annotations, elaborations, or other modifications
 * represent, as a whole, an original work of authorship. For the purposes
 * of this License, Derivative Works shall not include works that remain
 * separable from, or merely link (or bind by name) to the interfaces of,
 * the Work and Derivative Works thereof.
 *
 * "Contribution" shall mean any work of authorship, including
 * the original version of the Work and any modifications or additions
 * to that Work or Derivative Works thereof, that is intentionally
 * submitted to Licensor for inclusion in the Work by the copyright owner
 * or by an individual or Legal Entity authorized to submit on behalf of
 * the copyright owner. For the purposes of this definition, "submitted"
 * means any form of electronic, verbal, or written communication sent
 * to the Licensor or its representatives, including but not limited to
 * communication on electronic mailing lists, source code control systems,
 * and issue tracking systems that are managed by, or on behalf of, the
 * Licensor for the purpose of discussing and improving the Work, but
 * excluding communication that is conspicuously marked or otherwise
 * designated in writing by the copyright owner as "Not a Contribution."
 *
 * "Contributor" shall mean Licensor and any individual or Legal Entity
 * on behalf of whom a Contribution has been received by Licensor and
 * subsequently incorporated within the Work.
 *
 * 2. Grant of Copyright License. Subject to the terms and conditions of
 * this License, each Contributor hereby grants to You a perpetual,
 * worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 * copyright license to reproduce, prepare Derivative Works of,
 * publicly display, publicly perform, sublicense, and distribute the
 * Work and such Derivative Works in Source or Object form.
 *
 * 3. Grant of Patent License. Subject to the terms and conditions of
 * this License, each Contributor hereby grants to You a perpetual,
 * worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 * (except as stated in this section) patent license to make, have made,
 * use, offer to sell, sell, import, and otherwise transfer the Work,
 * where such license applies only to those patent claims licensable
 * by such Contributor that are necessarily infringed by their
 * Contribution(s) alone or by combination of their Contribution(s)
 * with the Work to which such Contribution(s) was submitted. If You
 * institute patent litigation against any entity (including a
 * cross-claim or counterclaim in a lawsuit) alleging that the Work
 * or a Contribution incorporated within the Work constitutes direct
 * or contributory patent infringement, then any patent licenses
 * granted to You under this License for that Work shall terminate
 * as of the date such litigation is filed.
 *
 * 4. Redistribution. You may reproduce and distribute copies of the
 * Work or Derivative Works thereof in any medium, with or without
 * modifications, and in Source or Object form, provided that You
 * meet the following conditions:
 *
 * (a) You must give any other recipients of the Work or
 * Derivative Works a copy of this License; and
 *
 * (b) You must cause any modified files to carry prominent notices
 * stating that You changed the files; and
 *
 * (c) You must retain, in the Source form of any Derivative Works
 * that You distribute, all copyright, patent, trademark, and
 * attribution notices from the Source form of the Work,
 * excluding those notices that do not pertain to any part of
 * the Derivative Works; and
 *
 * (d) If the Work includes a "NOTICE" text file as part of its
 * distribution, then any Derivative Works that You distribute must
 * include a readable copy of the attribution notices contained
 * within such NOTICE file, excluding those notices that do not
 * pertain to any part of the Derivative Works, in at least one
 * of the following places: within a NOTICE text file distributed
 * as part of the Derivative Works; within the Source form or
 * documentation, if provided along with the Derivative Works; or,
 * within a display generated by the Derivative Works, if and
 * wherever such third-party notices normally appear. The contents
 * of the NOTICE file are for informational purposes only and
 * do not modify the License. You may add Your own attribution
 * notices within Derivative Works that You distribute, alongside
 * or as an addendum to the NOTICE text from the Work, provided
 * that such additional attribution notices cannot be construed
 * as modifying the License.
 *
 * You may add Your own copyright statement to Your modifications and
 * may provide additional or different license terms and conditions
 * for use, reproduction, or distribution of Your modifications, or
 * for any such Derivative Works as a whole, provided Your use,
 * reproduction, and distribution of the Work otherwise complies with
 * the conditions stated in this License.
 *
 * 5. Submission of Contributions. Unless You explicitly state otherwise,
 * any Contribution intentionally submitted for inclusion in the Work
 * by You to the Licensor shall be under the terms and conditions of
 * this License, without any additional terms or conditions.
 * Notwithstanding the above, nothing herein shall supersede or modify
 * the terms of any separate license agreement you may have executed
 * with Licensor regarding such Contributions.
 *
 * 6. Trademarks. This License does not grant permission to use the trade
 * names, trademarks, service marks, or product names of the Licensor,
 * except as required for reasonable and customary use in describing the
 * origin of the Work and reproducing the content of the NOTICE file.
 *
 * 7. Disclaimer of Warranty. Unless required by applicable law or
 * agreed to in writing, Licensor provides the Work (and each
 * Contributor provides its Contributions) on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied, including, without limitation, any warranties or conditions
 * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 * PARTICULAR PURPOSE. You are solely responsible for determining the
 * appropriateness of using or redistributing the Work and assume any
 * risks associated with Your exercise of permissions under this License.
 *
 * 8. Limitation of Liability. In no event and under no legal theory,
 * whether in tort (including negligence), contract, or otherwise,
 * unless required by applicable law (such as deliberate and grossly
 * negligent acts) or agreed to in writing, shall any Contributor be
 * liable to You for damages, including any direct, indirect, special,
 * incidental, or consequential damages of any character arising as a
 * result of this License or out of the use or inability to use the
 * Work (including but not limited to damages for loss of goodwill,
 * work stoppage, computer failure or malfunction, or any and all
 * other commercial damages or losses), even if such Contributor
 * has been advised of the possibility of such damages.
 *
 * 9. Accepting Warranty or Additional Liability. While redistributing
 * the Work or Derivative Works thereof, You may choose to offer,
 * and charge a fee for, acceptance of support, warranty, indemnity,
 * or other liability obligations and/or rights consistent with this
 * License. However, in accepting such obligations, You may act only
 * on Your own behalf and on Your sole responsibility, not on behalf
 * of any other Contributor, and only if You agree to indemnify,
 * defend, and hold each Contributor harmless for any liability
 * incurred by, or claims asserted against, such Contributor by reason
 * of your accepting any such warranty or additional liability.
 *
 * END OF TERMS AND CONDITIONS
 *
 * APPENDIX: How to apply the Apache License to your work.
 *
 * To apply the Apache License to your work, attach the following
 * boilerplate notice, with the fields enclosed by brackets "{}"
 * replaced with your own identifying information. (Don't include
 * the brackets!)  The text should be enclosed in the appropriate
 * comment syntax for the file format. We also recommend that a
 * file or class name and description of purpose be included on the
 * same "printed page" as the copyright notice for easier
 * identification within third-party archives.
 *
 * Copyright 2014 Edgar Espina
 *
 * 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.
 */
package org.jooby.internal.apitool;

import org.jooby.Route;

import static org.jooby.internal.apitool.Insn.ldcFor;

import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

class Lambda {
  public final String name;
  public final String pattern;
  public final String desc;
  public final String implementationName;
  public final String owner;
  public final String declaringClass;
  public final Optional<MethodNode> method;
  public final String tag;

  private Lambda(String declaringClass, MethodInsnNode method, Handle h) {
    this(declaringClass, h.getOwner(), h.getDesc(), h.getName(), method.name, null, null);
  }

  private Lambda(String declaringClass, String owner, String desc, String implementationName,
      String name, String pattern, MethodNode method) {
    this(declaringClass, owner, desc, implementationName, name, pattern, method, null);
  }

  private Lambda(String declaringClass, String owner, String desc, String implementationName,
      String name, String pattern, MethodNode method, String tag) {
    this.declaringClass = declaringClass;
    this.owner = owner;
    this.desc = desc;
    this.implementationName = implementationName;
    this.name = name.equals("use") || name.equals("all") ? "*" : name;
    this.pattern = pattern;
    this.method = Optional.ofNullable(method);
    this.tag = tag;
  }

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

  /**
   * Creates a new lambda and set a pattern prefix.
   *
   * @param prefix Prefix.
   * @return A new lambda.
   */
  public Lambda prefix(String prefix) {
    return new Lambda(declaringClass, owner, desc, implementationName, name,
        Route.normalize(prefix + "/" + pattern), method.orElse(null), tag);
  }

  public Lambda tag(String tag) {
    if (this.tag != null || tag.startsWith("/:") || tag.startsWith("/{")) {
      // NOOP, keep the most specific
      return this;
    }
    return new Lambda(declaringClass, owner, desc, implementationName, name, pattern,
        method.orElse(null), tag);
  }

  /**
   * Creates a new lambda and set a pattern prefix.
   *
   * @param method Method node.
   * @return A new lambda.
   */
  public Lambda method(MethodNode method) {
    return new Lambda(declaringClass, owner, desc, implementationName, name, pattern, method, tag);
  }

  public static Stream<Lambda> create(String owner, Optional<String> prefix, MethodInsnNode method,
      MethodNode implementation) {
    List<String> patterns = Insn.ldcFor(method).stream().map(it -> it.cst.toString())
        .collect(Collectors.toList());
    if (patterns.size() == 0) {
      if (method.owner.equals("org/jooby/Kooby")) {
        if (method.getPrevious() instanceof TypeInsnNode) {
          // get(path) { req -> ...}
          // seek and find first MethodInsnNode, which contains the path pattern.
          AbstractInsnNode ldcpath = new Insn<>(implementation, method)
              .prev()
              .filter(Filters.is(TypeInsnNode.class))
              .flatMap(n -> {
                if (n.getPrevious() instanceof LdcInsnNode) {
                  return Stream.of(n.getPrevious());
                }
                return Stream.empty();
              })
              .findFirst()
              .orElse(null);
          if (ldcpath instanceof LdcInsnNode) {
            patterns.add(((LdcInsnNode) ldcpath).cst.toString());
          }
        }
      }
      // Still empty? use default
      if (patterns.size() == 0) {
        patterns.add("/");
      }
    }
    prefix.ifPresent(p -> {
      IntStream.range(0, patterns.size())
          .forEach(i -> patterns.set(i, Route.normalize(p + "/" + patterns.get(i))));
    });
    return patterns.stream()
        .map(pattern -> {
          return new Lambda(owner, owner, implementation.desc, implementation.name,
              method.name.replace("$default", ""), pattern, implementation);
        });
  }

  public static Stream<Lambda> create(ClassLoader loader,
      Predicate<MethodInsnNode> scriptRoute, String declaringClass,
      InvokeDynamicInsnNode node, MethodNode implementation) {
    return new Insn<>(null, node)
        .next()
        .filter(Filters.is(MethodInsnNode.class))
        .findFirst()
        .map(MethodInsnNode.class::cast)
        .filter(scriptRoute)
        .map(method -> {
          return Arrays.asList(node.bsmArgs).stream()
              .filter(Handle.class::isInstance)
              .map(Handle.class::cast)
              .findFirst()
              .map(handle -> {

                Lambda lambda = new Lambda(declaringClass, method, handle);
                // Collect pattern
                List<Type> args = Arrays.asList(Type.getArgumentTypes(method.desc));
                AtomicInteger count = new AtomicInteger(IntStream.range(0, args.size())
                    .filter(i -> !args.get(i).getClassName().equals(String.class.getName()))
                    .findFirst()
                    .orElse(0));
                int from = 0;
                // use(verb, pattern)
                if ("use".equals(method.name) && count.get() == 2) {
                  from += 1;
                }

                MethodInsnNode start = method;

                // Instance method reference but not Static method reference:
                if ((!method.owner.equals(lambda.owner)
                    && handle.getTag() != Opcodes.H_INVOKESTATIC)) {
                  // get("pattern", instance::methodReference)
                  start = new Insn<>(null, method.getPrevious())
                      .prev()
                      .filter(Filters.is(MethodInsnNode.class))
                      .findFirst()
                      .map(MethodInsnNode.class::cast)
                      .orElse(method);
                }

                List<String> ldc = ldcFor(start).stream()
                    .map(it -> Route.normalize(it.cst.toString()))
                    .collect(Collectors.toList());

                if (ldc.size() == 0) {
                  // default path:
                  ldc.add("/");
                  count.incrementAndGet();
                }
                return ldc.stream()
                    .skip(from)
                    .limit(Math.min(count.get(), ldc.size()))
                    .flatMap(pattern ->
                        Stream.of(new Lambda(lambda.declaringClass, lambda.owner, lambda.desc,
                            lambda.implementationName, lambda.name, pattern, implementation))
                    );
              })
              .orElse(Stream.of());
        })
        .orElse(Stream.of());
  }
}


/**
 * Apache License
 * Version 2.0, January 2004
 * http://www.apache.org/licenses/
 *

 * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 *

 * 1. Definitions.
 *

 * "License" shall mean the terms and conditions for use, reproduction,
 * and distribution as defined by Sections 1 through 9 of this document.
 *

 * "Licensor" shall mean the copyright owner or entity authorized by
 * the copyright owner that is granting the License.
 *

 * "Legal Entity" shall mean the union of the acting entity and all
 * other entities that control, are controlled by, or are under common
 * control with that entity. For the purposes of this definition,
 * "control" means (i) the power, direct or indirect, to cause the
 * direction or management of such entity, whether by contract or
 * otherwise, or (ii) ownership of fifty percent (50%) or more of the
 * outstanding shares, or (iii) beneficial ownership of such entity.
 *

 * "You" (or "Your") shall mean an individual or Legal Entity
 * exercising permissions granted by this License.
 *

 * "Source" form shall mean the preferred form for making modifications,
 * including but not limited to software source code, documentation
 * source, and configuration files.
 *

 * "Object" form shall mean any form resulting from mechanical
 * transformation or translation of a Source form, including but
 * not limited to compiled object code, generated documentation,
 * and conversions to other media types.
 *

 * "Work" shall mean the work of authorship, whether in Source or
 * Object form, made available under the License, as indicated by a
 * copyright notice that is included in or attached to the work
 * (an example is provided in the Appendix below).
 *

 * "Derivative Works" shall mean any work, whether in Source or Object
 * form, that is based on (or derived from) the Work and for which the
 * editorial revisions, annotations, elaborations, or other modifications
 * represent, as a whole, an original work of authorship. For the purposes
 * of this License, Derivative Works shall not include works that remain
 * separable from, or merely link (or bind by name) to the interfaces of,
 * the Work and Derivative Works thereof.
 *

 * "Contribution" shall mean any work of authorship, including
 * the original version of the Work and any modifications or additions
 * to that Work or Derivative Works thereof, that is intentionally
 * submitted to Licensor for inclusion in the Work by the copyright owner
 * or by an individual or Legal Entity authorized to submit on behalf of
 * the copyright owner. For the purposes of this definition, "submitted"
 * means any form of electronic, verbal, or written communication sent
 * to the Licensor or its representatives, including but not limited to
 * communication on electronic mailing lists, source code control systems,
 * and issue tracking systems that are managed by, or on behalf of, the
 * Licensor for the purpose of discussing and improving the Work, but
 * excluding communication that is conspicuously marked or otherwise
 * designated in writing by the copyright owner as "Not a Contribution."
 *

 * "Contributor" shall mean Licensor and any individual or Legal Entity
 * on behalf of whom a Contribution has been received by Licensor and
 * subsequently incorporated within the Work.
 *

 * 2. Grant of Copyright License. Subject to the terms and conditions of
 * this License, each Contributor hereby grants to You a perpetual,
 * worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 * copyright license to reproduce, prepare Derivative Works of,
 * publicly display, publicly perform, sublicense, and distribute the
 * Work and such Derivative Works in Source or Object form.
 *

 * 3. Grant of Patent License. Subject to the terms and conditions of
 * this License, each Contributor hereby grants to You a perpetual,
 * worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 * (except as stated in this section) patent license to make, have made,
 * use, offer to sell, sell, import, and otherwise transfer the Work,
 * where such license applies only to those patent claims licensable
 * by such Contributor that are necessarily infringed by their
 * Contribution(s) alone or by combination of their Contribution(s)
 * with the Work to which such Contribution(s) was submitted. If You
 * institute patent litigation against any entity (including a
 * cross-claim or counterclaim in a lawsuit) alleging that the Work
 * or a Contribution incorporated within the Work constitutes direct
 * or contributory patent infringement, then any patent licenses
 * granted to You under this License for that Work shall terminate
 * as of the date such litigation is filed.
 *

 * 4. Redistribution. You may reproduce and distribute copies of the
 * Work or Derivative Works thereof in any medium, with or without
 * modifications, and in Source or Object form, provided that You
 * meet the following conditions:
 *

 * (a) You must give any other recipients of the Work or
 * Derivative Works a copy of this License; and
 *

 * (b) You must cause any modified files to carry prominent notices
 * stating that You changed the files; and
 *

 * (c) You must retain, in the Source form of any Derivative Works
 * that You distribute, all copyright, patent, trademark, and
 * attribution notices from the Source form of the Work,
 * excluding those notices that do not pertain to any part of
 * the Derivative Works; and
 *

 * (d) If the Work includes a "NOTICE" text file as part of its
 * distribution, then any Derivative Works that You distribute must
 * include a readable copy of the attribution notices contained
 * within such NOTICE file, excluding those notices that do not
 * pertain to any part of the Derivative Works, in at least one
 * of the following places: within a NOTICE text file distributed
 * as part of the Derivative Works; within the Source form or
 * documentation, if provided along with the Derivative Works; or,
 * within a display generated by the Derivative Works, if and
 * wherever such third-party notices normally appear. The contents
 * of the NOTICE file are for informational purposes only and
 * do not modify the License. You may add Your own attribution
 * notices within Derivative Works that You distribute, alongside
 * or as an addendum to the NOTICE text from the Work, provided
 * that such additional attribution notices cannot be construed
 * as modifying the License.
 *

 * You may add Your own copyright statement to Your modifications and
 * may provide additional or different license terms and conditions
 * for use, reproduction, or distribution of Your modifications, or
 * for any such Derivative Works as a whole, provided Your use,
 * reproduction, and distribution of the Work otherwise complies with
 * the conditions stated in this License.
 *

 * 5. Submission of Contributions. Unless You explicitly state otherwise,
 * any Contribution intentionally submitted for inclusion in the Work
 * by You to the Licensor shall be under the terms and conditions of
 * this License, without any additional terms or conditions.
 * Notwithstanding the above, nothing herein shall supersede or modify
 * the terms of any separate license agreement you may have executed
 * with Licensor regarding such Contributions.
 *

 * 6. Trademarks. This License does not grant permission to use the trade
 * names, trademarks, service marks, or product names of the Licensor,
 * except as required for reasonable and customary use in describing the
 * origin of the Work and reproducing the content of the NOTICE file.
 *

 * 7. Disclaimer of Warranty. Unless required by applicable law or
 * agreed to in writing, Licensor provides the Work (and each
 * Contributor provides its Contributions) on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied, including, without limitation, any warranties or conditions
 * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 * PARTICULAR PURPOSE. You are solely responsible for determining the
 * appropriateness of using or redistributing the Work and assume any
 * risks associated with Your exercise of permissions under this License.
 *

 * 8. Limitation of Liability. In no event and under no legal theory,
 * whether in tort (including negligence), contract, or otherwise,
 * unless required by applicable law (such as deliberate and grossly
 * negligent acts) or agreed to in writing, shall any Contributor be
 * liable to You for damages, including any direct, indirect, special,
 * incidental, or consequential damages of any character arising as a
 * result of this License or out of the use or inability to use the
 * Work (including but not limited to damages for loss of goodwill,
 * work stoppage, computer failure or malfunction, or any and all
 * other commercial damages or losses), even if such Contributor
 * has been advised of the possibility of such damages.
 *

 * 9. Accepting Warranty or Additional Liability. While redistributing
 * the Work or Derivative Works thereof, You may choose to offer,
 * and charge a fee for, acceptance of support, warranty, indemnity,
 * or other liability obligations and/or rights consistent with this
 * License. However, in accepting such obligations, You may act only
 * on Your own behalf and on Your sole responsibility, not on behalf
 * of any other Contributor, and only if You agree to indemnify,
 * defend, and hold each Contributor harmless for any liability
 * incurred by, or claims asserted against, such Contributor by reason
 * of your accepting any such warranty or additional liability.
 *

 * END OF TERMS AND CONDITIONS
 *

 * APPENDIX: How to apply the Apache License to your work.
 *

 * To apply the Apache License to your work, attach the following
 * boilerplate notice, with the fields enclosed by brackets "{}"
 * replaced with your own identifying information. (Don't include
 * the brackets!)  The text should be enclosed in the appropriate
 * comment syntax for the file format. We also recommend that a
 * file or class name and description of purpose be included on the
 * same "printed page" as the copyright notice for easier
 * identification within third-party archives.
 *

 * Copyright 2014 Edgar Espina
 *

 * 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.
 */
package org.jooby.assets;

import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.typesafe.config.Config;
import org.jooby.funzy.Throwing;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

/**
 *
svg-symbol

 *

 * SVG symbol for icons: merge svg files from a folder and generates a
 * sprite.svg and sprite.css files.
 *

 *
 *
usage

 *
 *
{@code
 * assets {
 *   fileset {
 *     sprite: svg-symbol
 *   }
 *
 *   svg-symbol {
 *     input: "images/svg"
 *   }
 * }
 * }

 *
 *

 * Previous example looks for *.svg files inside the images/svg folder and
 * generate a sprite.svg and sprite.css files.
 *

 *
 *

 * You can display the svg icons using id reference:
 *

 *
 *
{@code
 * <svg>
 *   <use xlink:href="#approved" />
 * </svg>
 * }

 *
 *

 * This technique is described here:
 * SVG symbol a Good Choice for
 * Icons
 *

 *
 *
options

 *
 *
output

 *

 * Defines where to write the svg and css files. Default value is:
 * sprite.
 *

 *
 *
{@code
 * svg-symbol {
 *   output: "folder/symbols"
 * }
 * }

 *
 *

 * There are two more specific output options: svg.output and css.output
 * if any of these options are present the output option is ignored:
 *

 *
 *
{@code
 * svg-symbol {
 *   css {
 *     output: "css/sprite.css"
 *   },
 *   svg {
 *     output: "img/sprite.svg"
 *   }
 * }
 * }

 *
 *
id prefix and suffix

 *

 * ID is generated from svg file names. These options prepend or append something to
 * the generated id.
 *

 *
 *
{@code
 * svg-symbol {
 *   output: "sprite"
 *   id {
 *     prefix: "icon-"
 *   }
 * }
 * }

 *
 *

 * Generates IDs like: icon-approved, while:
 *

 *
 *
{@code
 * svg-symbol {
 *   output: "sprite"
 *   id {
 *     suffix: "-icon"
 *   }
 * }
 * }

 *
 *

 * Generates IDs like: approved-icon
 *

 *
 *
css prefix

 *

 * Prepend a string to a generated css class. Here is the css class for approved.svg:
 *

 *
 *
{@code
 * .approved {
 *   width: 18px;
 *   height: 18px;
 * }
 * }

 *
 *

 * If we set a svg css prefix:
 *
 *
{@code
 * {
 *   svg-symbol: {
 *     css {
 *       prefix: "svg"
 *     }
 *   }
 * }
 * }

 *
 *

 * The generated css class will be:
 *
{@code
 * svg.approved {
 *   width: 18px;
 *   height: 18px;
 * }
 * }

 *
 *

 * This option is useful for generating more specific css class selectors.
 *

 *
 * @author edgar
 */
public class SvgSymbol extends AssetAggregator {

  public static class Tuple<T1, T2> {
    public final T1 _1;
    public final T2 _2;

    public Tuple(final T1 _1, final T2 _2) {
      this._1 = _1;
      this._2 = _2;
    }
  }

  /**
   * #3.
   */
  private static final int _3 = 3;

  /**
   * Handle svg dimension: 0 0 20 20.
   */
  static final Pattern SIZE = Pattern.compile("(\\d+(\\.\\d+)?)(\\w+)");

  /**
   * The logging system.
   */
  private final Logger log = LoggerFactory.getLogger(getClass());

  /**
   * Creates a new {@link SvgSymbol}.
   */
  public SvgSymbol() {
    set("output", "sprite");

    set("id.prefix", "");
    set("id.suffix", "");

    set("css.prefix", "");
    set("css.round", true);

    set("svg.xmlns", "http://www.w3.org/2000/svg");
  }

  @Override
  public List<String> fileset() {
    return ImmutableList.of(cssPath());
  }

  @Override
  public void run(final Config conf) throws Exception {
    String input = get("input");
    if (input == null) {
      throw new IllegalArgumentException("Required option 'svg-symbol.input' not present");
    }
    Path basedir = Paths.get(get("basedir").toString());
    Path dir = basedir.resolve(input);

    List<CharSequence> cssout = new ArrayList<>();

    Element svg = new Element(Tag.valueOf("svg"), "");
    attrs("svg", "output").forEach((n, v) -> svg.attr(n, v.toString()));

    files(dir, file -> {
      log.debug("{}", file);
      String id = get("id.prefix") + file.getFileName().toString().replace(".svg", "")
          + get("id.suffix");

      Tuple<Element, Element> rewrite = symbol(file, id);
      svg.appendChild(rewrite._2);

      cssout.add(css(id, rewrite._1));
    });

    write(basedir.resolve(svgPath()), ImmutableList.of(svg.outerHtml()));
    write(basedir.resolve(cssPath()), cssout);
  }

  private void files(Path dir, Throwing.Consumer<Path> consumer) throws Exception {
    try (Stream<Path> stream = Files.walk(dir)) {
      Iterator<Path> files = stream.filter(Files::isRegularFile)
          .filter(it -> it.toString().endsWith(".svg"))
          .sorted()
          .iterator();
      while (files.hasNext()) {
        consumer.accept(files.next());
      }
    }
  }

  /**
   * Read an object path and optionally filter some child paths.
   *
   * @param path    Path to read.
   * @param without Properties to filter.
   * @return Properties.
   */
  private Map<String, Object> attrs(final String path, final String... without) {
    Map<String, Object> attrs = new LinkedHashMap<>(get(path));
    Arrays.asList(without).forEach(attrs::remove);
    return attrs;
  }

  /**
   * Read a svg file and return the svg element (original) and a new symbol element (created from
   * original).
   *
   * @param file Svg file.
   * @param id   ID to use.
   * @return Svg element (original) and a symbol element (converted).
   * @throws IOException If something goes wrong.
   */
  private Tuple<Element, Element> symbol(final Path file, final String id) throws IOException {
    Element svg = Jsoup.parse(file.toFile(), "UTF-8").select("svg").first();
    Element symbol = new Element(Tag.valueOf("symbol"), "")
        .attr("id", id)
        .attr("viewBox", svg.attr("viewBox"));
    new ArrayList<>(svg.childNodes()).forEach(symbol::appendChild);
    return new Tuple(svg, symbol);
  }

  /**
   * Generate a CSS rule, it reads the width and height attributes of the svg element or fallback to
   * viewBox attribute.
   *
   * @param id  ID to use.
   * @param svg Svg element to convert.
   * @return A css rule.
   */
  private CharSequence css(final String id, final Element svg) {
    Throwing.Function<String, Tuple<Tuple<Number, String>, Tuple<Number, String>>> viewBox = Throwing
        .<String, Tuple<Tuple<Number, String>, Tuple<Number, String>>>throwingFunction(name -> {
          String vbox = svg.attr(name);
          String[] dimension = vbox.split("\\s+");
          return new Tuple(parse(dimension[2]), parse(dimension[_3]));
        }).memoized();
    Tuple<Number, String> w = Optional.ofNullable(Strings.emptyToNull(svg.attr("width")))
        .map(this::parse)
        .orElseGet(() -> viewBox.apply("viewBox")._1);
    Tuple<Number, String> h = Optional.ofNullable(Strings.emptyToNull(svg.attr("height")))
        .map(this::parse)
        .orElseGet(() -> viewBox.apply("viewBox")._2);
    StringBuilder css = new StringBuilder();
    css.append(get("css.prefix").toString()).append(".").append(id)
        .append(" {\n  width: ").append(w._1).append(w._2).append(";\n")
        .append("  height: ").append(h._1).append(h._2).append(";\n}");
    return css;
  }

  /**
   * Parse a css size unit value, like 10px or 18.919px and optionally round the value to the
   * closest integer.
   *
   * @param value Value to parse.
   * @return A tuple with a number and unit(px, em, etc...)
   */
  private Tuple<Number, String> parse(final String value) {
    Matcher matcher = SIZE.matcher(value);
    if (matcher.find()) {
      String number = matcher.group(1);
      String unit = matcher.group(_3);
      boolean round = get("css.round");
      Number num = Double.parseDouble(number);
      return new Tuple(round ? Math.round(num.doubleValue()) : num, unit);
    }
    return null;
  }

  /**
   * Write content to file.
   *
   * @param path     Target file.
   * @param sequence File content.
   * @throws IOException If something goes wrong.
   */
  private void write(final Path path, final List<CharSequence> sequence) throws IOException {
    log.debug("writing: {}", path.normalize().toAbsolutePath());
    path.toFile().getParentFile().mkdirs();
    Files.write(path, sequence);
  }

  /**
   * @return Generate a css path from css.output or fallback to output
   */
  private String cssPath() {
    String css = Optional.<String>ofNullable(get("css.output")).orElse(get("output"));
    return css.endsWith(".css") ? css : css + ".css";
  }

  /**
   * @return Generate a css path from svg.output or fallback to output
   */
  private String svgPath() {
    String svg = Optional.<String>ofNullable(get("svg.output")).orElse(get("output"));
    return svg.endsWith(".svg") ? svg : svg + ".svg";
  }

}


/**
 *                                  Apache License
 *                            Version 2.0, January 2004
 *                         http://www.apache.org/licenses/
 *
 *    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 *
 *    1. Definitions.
 *
 *       "License" shall mean the terms and conditions for use, reproduction,
 *       and distribution as defined by Sections 1 through 9 of this document.
 *
 *       "Licensor" shall mean the copyright owner or entity authorized by
 *       the copyright owner that is granting the License.
 *
 *       "Legal Entity" shall mean the union of the acting entity and all
 *       other entities that control, are controlled by, or are under common
 *       control with that entity. For the purposes of this definition,
 *       "control" means (i) the power, direct or indirect, to cause the
 *       direction or management of such entity, whether by contract or
 *       otherwise, or (ii) ownership of fifty percent (50%) or more of the
 *       outstanding shares, or (iii) beneficial ownership of such entity.
 *
 *       "You" (or "Your") shall mean an individual or Legal Entity
 *       exercising permissions granted by this License.
 *
 *       "Source" form shall mean the preferred form for making modifications,
 *       including but not limited to software source code, documentation
 *       source, and configuration files.
 *
 *       "Object" form shall mean any form resulting from mechanical
 *       transformation or translation of a Source form, including but
 *       not limited to compiled object code, generated documentation,
 *       and conversions to other media types.
 *
 *       "Work" shall mean the work of authorship, whether in Source or
 *       Object form, made available under the License, as indicated by a
 *       copyright notice that is included in or attached to the work
 *       (an example is provided in the Appendix below).
 *
 *       "Derivative Works" shall mean any work, whether in Source or Object
 *       form, that is based on (or derived from) the Work and for which the
 *       editorial revisions, annotations, elaborations, or other modifications
 *       represent, as a whole, an original work of authorship. For the purposes
 *       of this License, Derivative Works shall not include works that remain
 *       separable from, or merely link (or bind by name) to the interfaces of,
 *       the Work and Derivative Works thereof.
 *
 *       "Contribution" shall mean any work of authorship, including
 *       the original version of the Work and any modifications or additions
 *       to that Work or Derivative Works thereof, that is intentionally
 *       submitted to Licensor for inclusion in the Work by the copyright owner
 *       or by an individual or Legal Entity authorized to submit on behalf of
 *       the copyright owner. For the purposes of this definition, "submitted"
 *       means any form of electronic, verbal, or written communication sent
 *       to the Licensor or its representatives, including but not limited to
 *       communication on electronic mailing lists, source code control systems,
 *       and issue tracking systems that are managed by, or on behalf of, the
 *       Licensor for the purpose of discussing and improving the Work, but
 *       excluding communication that is conspicuously marked or otherwise
 *       designated in writing by the copyright owner as "Not a Contribution."
 *
 *       "Contributor" shall mean Licensor and any individual or Legal Entity
 *       on behalf of whom a Contribution has been received by Licensor and
 *       subsequently incorporated within the Work.
 *
 *    2. Grant of Copyright License. Subject to the terms and conditions of
 *       this License, each Contributor hereby grants to You a perpetual,
 *       worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 *       copyright license to reproduce, prepare Derivative Works of,
 *       publicly display, publicly perform, sublicense, and distribute the
 *       Work and such Derivative Works in Source or Object form.
 *
 *    3. Grant of Patent License. Subject to the terms and conditions of
 *       this License, each Contributor hereby grants to You a perpetual,
 *       worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 *       (except as stated in this section) patent license to make, have made,
 *       use, offer to sell, sell, import, and otherwise transfer the Work,
 *       where such license applies only to those patent claims licensable
 *       by such Contributor that are necessarily infringed by their
 *       Contribution(s) alone or by combination of their Contribution(s)
 *       with the Work to which such Contribution(s) was submitted. If You
 *       institute patent litigation against any entity (including a
 *       cross-claim or counterclaim in a lawsuit) alleging that the Work
 *       or a Contribution incorporated within the Work constitutes direct
 *       or contributory patent infringement, then any patent licenses
 *       granted to You under this License for that Work shall terminate
 *       as of the date such litigation is filed.
 *
 *    4. Redistribution. You may reproduce and distribute copies of the
 *       Work or Derivative Works thereof in any medium, with or without
 *       modifications, and in Source or Object form, provided that You
 *       meet the following conditions:
 *
 *       (a) You must give any other recipients of the Work or
 *           Derivative Works a copy of this License; and
 *
 *       (b) You must cause any modified files to carry prominent notices
 *           stating that You changed the files; and
 *
 *       (c) You must retain, in the Source form of any Derivative Works
 *           that You distribute, all copyright, patent, trademark, and
 *           attribution notices from the Source form of the Work,
 *           excluding those notices that do not pertain to any part of
 *           the Derivative Works; and
 *
 *       (d) If the Work includes a "NOTICE" text file as part of its
 *           distribution, then any Derivative Works that You distribute must
 *           include a readable copy of the attribution notices contained
 *           within such NOTICE file, excluding those notices that do not
 *           pertain to any part of the Derivative Works, in at least one
 *           of the following places: within a NOTICE text file distributed
 *           as part of the Derivative Works; within the Source form or
 *           documentation, if provided along with the Derivative Works; or,
 *           within a display generated by the Derivative Works, if and
 *           wherever such third-party notices normally appear. The contents
 *           of the NOTICE file are for informational purposes only and
 *           do not modify the License. You may add Your own attribution
 *           notices within Derivative Works that You distribute, alongside
 *           or as an addendum to the NOTICE text from the Work, provided
 *           that such additional attribution notices cannot be construed
 *           as modifying the License.
 *
 *       You may add Your own copyright statement to Your modifications and
 *       may provide additional or different license terms and conditions
 *       for use, reproduction, or distribution of Your modifications, or
 *       for any such Derivative Works as a whole, provided Your use,
 *       reproduction, and distribution of the Work otherwise complies with
 *       the conditions stated in this License.
 *
 *    5. Submission of Contributions. Unless You explicitly state otherwise,
 *       any Contribution intentionally submitted for inclusion in the Work
 *       by You to the Licensor shall be under the terms and conditions of
 *       this License, without any additional terms or conditions.
 *       Notwithstanding the above, nothing herein shall supersede or modify
 *       the terms of any separate license agreement you may have executed
 *       with Licensor regarding such Contributions.
 *
 *    6. Trademarks. This License does not grant permission to use the trade
 *       names, trademarks, service marks, or product names of the Licensor,
 *       except as required for reasonable and customary use in describing the
 *       origin of the Work and reproducing the content of the NOTICE file.
 *
 *    7. Disclaimer of Warranty. Unless required by applicable law or
 *       agreed to in writing, Licensor provides the Work (and each
 *       Contributor provides its Contributions) on an "AS IS" BASIS,
 *       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 *       implied, including, without limitation, any warranties or conditions
 *       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 *       PARTICULAR PURPOSE. You are solely responsible for determining the
 *       appropriateness of using or redistributing the Work and assume any
 *       risks associated with Your exercise of permissions under this License.
 *
 *    8. Limitation of Liability. In no event and under no legal theory,
 *       whether in tort (including negligence), contract, or otherwise,
 *       unless required by applicable law (such as deliberate and grossly
 *       negligent acts) or agreed to in writing, shall any Contributor be
 *       liable to You for damages, including any direct, indirect, special,
 *       incidental, or consequential damages of any character arising as a
 *       result of this License or out of the use or inability to use the
 *       Work (including but not limited to damages for loss of goodwill,
 *       work stoppage, computer failure or malfunction, or any and all
 *       other commercial damages or losses), even if such Contributor
 *       has been advised of the possibility of such damages.
 *
 *    9. Accepting Warranty or Additional Liability. While redistributing
 *       the Work or Derivative Works thereof, You may choose to offer,
 *       and charge a fee for, acceptance of support, warranty, indemnity,
 *       or other liability obligations and/or rights consistent with this
 *       License. However, in accepting such obligations, You may act only
 *       on Your own behalf and on Your sole responsibility, not on behalf
 *       of any other Contributor, and only if You agree to indemnify,
 *       defend, and hold each Contributor harmless for any liability
 *       incurred by, or claims asserted against, such Contributor by reason
 *       of your accepting any such warranty or additional liability.
 *
 *    END OF TERMS AND CONDITIONS
 *
 *    APPENDIX: How to apply the Apache License to your work.
 *
 *       To apply the Apache License to your work, attach the following
 *       boilerplate notice, with the fields enclosed by brackets "{}"
 *       replaced with your own identifying information. (Don't include
 *       the brackets!)  The text should be enclosed in the appropriate
 *       comment syntax for the file format. We also recommend that a
 *       file or class name and description of purpose be included on the
 *       same "printed page" as the copyright notice for easier
 *       identification within third-party archives.
 *
 *    Copyright 2014 Edgar Espina
 *
 *    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.
 */
package org.jooby.jdbi;

import com.google.inject.Key;
import com.google.inject.name.Names;
import static java.util.Objects.requireNonNull;
import org.jdbi.v3.core.Handle;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;

/**
 * Configure a transaction per request filter by setting the method and pattern to intercept and
 * advanced Handle configuration.
 *
 * @author edgar
 */
public class TransactionalRequest {
  private String method = "*";

  private String pattern = "*";

  private Key<Handle> handleKey = Key.get(Handle.class);

  private List<Class> sqlObjects = new ArrayList<>();

  Consumer<Handle> configurer;

  /**
   * Configure a Handle before beginning a new transaction.
   *
   * @param configurer Configurer callback.
   * @return This object.
   */
  public TransactionalRequest doWith(Consumer<Handle> configurer) {
    this.configurer = requireNonNull(configurer, "Handle configurer required.");
    return this;
  }

  /**
   * HTTP method to intercept, defauts is: `*`.
   *
   * @return HTTP method to intercept, defauts is: `*`.
   */
  public String method() {
    return method;
  }

  /**
   * Set a HTTP method to intercept.
   *
   * @param method HTTP method.
   * @return This object.
   */
  public TransactionalRequest method(final String method) {
    this.method = method;
    return this;
  }

  /**
   * HTTP pattern to interncept, defaults is: `*`.
   *
   * @return HTTP pattern to interncept, defaults is: `*`.
   */
  public String pattern() {
    return pattern;
  }

  /**
   * Set the HTTP pattern to intercept.
   *
   * @param pattern HTTP pattern to intercept.
   * @return This object.
   */
  public TransactionalRequest pattern(final String pattern) {
    this.pattern = pattern;
    return this;
  }

  /**
   * Key for accessing to the request scoped Handle.
   *
   * @return Key for accessing to the request scoped Handle.
   */
  public Key<Handle> handle() {
    return handleKey;
  }

  /**
   * Use a named key for accessing to the request scoped Handle.
   *
   * @param name Name of request scoped Handle.
   * @return This object.
   */
  public TransactionalRequest handle(String name) {
    this.handleKey = Key.get(Handle.class, Names.named(name));
    return this;
  }

  /**
   * List of SqlObjects attached to the request scoped Handle.
   *
   * @return List of SqlObjects attached to the request scoped Handle.
   */
  public List<Class> sqlObjects() {
    return sqlObjects;
  }

  /**
   * Attach/append a SqlObject (a.k.a DAO, Repository) to the request scoped Handle.
   *
   * @param sqlObject SqlObject (a.k.a DAO, Repository) to attach to the request scoped Handle.
   * @return This object.
   */
  public TransactionalRequest attach(final Class sqlObject) {
    this.sqlObjects.add(sqlObject);
    return this;
  }
}