/*
 * Copyright 2017-present Facebook, Inc.
 *
 * 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 com.facebook.buck.ide.intellij.projectview;

import com.google.common.base.Joiner;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Utility code for working with {@link java.util.regex.Pattern regex patterns.} Only
 * package-visible, for now at least, because the APIs are not very general.
 */
class Patterns {
  private final Pattern[] patterns;

  private Patterns(Pattern... patterns) {
    this.patterns = patterns;
  }

  private Patterns(String... patterns) {
    Pattern[] compiled = new Pattern[patterns.length];
    for (int index = 0, length = patterns.length; index < length; ++index) {
      compiled[index] = Pattern.compile(patterns[index]);
    }
    this.patterns = compiled;
  }

  /**
   * Parameter to {@link #onAnyMatch(String, MatchHandler)}. Normally will be implemented as a
   * lambda.
   */
  interface MatchHandler {
    /**
     * Called when {@link #onAnyMatch(String, MatchHandler)} finds a match
     *
     * @param matcher The {@link Matcher} with match location, captures, &c
     * @param text The text that was passed to {@link #onMatch(Matcher, String)}
     */
    void onMatch(Matcher matcher, String text);
  }

  /**
   * Passes {@code target} to each regex passed to the constructor. Calls {@code handler} on first
   * match.
   *
   * @param target Text to match against each regex
   * @param handler Code to call on first match
   * @return {@code true} if any regex matched; {@code false} if no regex matched
   */
  boolean onAnyMatch(String target, MatchHandler handler) {
    for (Pattern pattern : patterns) {
      Matcher match = pattern.matcher(target);
      if (match.find()) {
        handler.onMatch(match, target);
        return true;
      }
    }
    return false;
  }

  /**
   * The {@link Patterns} constructor takes either a {@code String...} parameter or a {@code
   * Pattern...} parameter. This {@code Builder} is an alternative to {@code new Patterns(compile(a,
   * b, c), compile(d, e, f))}
   */
  static Builder builder() {
    return new Builder();
  }

  static class Builder {
    private final List<String> patterns = new ArrayList<>();

    /** Add a regex to the Builder */
    Builder add(String pattern) {
      patterns.add(pattern);
      return this;
    }

    /** Add a regex to the Builder */
    Builder add(String... elements) {
      return add(join(elements));
    }

    /** Create a new {@#link Patterns} */
    Patterns build() {
      String[] patternArray = new String[patterns.size()];
      return new Patterns(patterns.toArray(patternArray));
    }
  }

  /**
   * Factory method to create a single-regex {@link Patterns}: equivalent to {@code
   * Patterns.builder().add(pattern).build()} but smaller and more efficient
   */
  static Patterns build(String pattern) {
    return new Patterns(pattern);
  }

  /**
   * Factory method to create a single-regex {@link Patterns}: equivalent to {@code
   * Patterns.builder().add(elements).build()} but smaller and more efficient
   */
  static Patterns build(String... elements) {
    return new Patterns(join(elements));
  }

  /**
   * Convenience function to compile a complex regex from a comma-delimited stream of elements. This
   * can be easier to read than a single long string or {@code "..." + "..."}.
   *
   * @param elements A stream of regex elements
   * @return A compiled regex
   */
  static Pattern compile(String... elements) {
    return compile(0, elements);
  }

  /**
   * Convenience function to compile a complex regex from a comma-delimited stream of elements. This
   * can be easier to read than a single long string or {@code "..." + "..."}.
   *
   * @param flags {@link Pattern#compile(String, int) Pattern.compile() flags}
   * @param elements A stream of regex elements
   * @return A compiled regex
   */
  static Pattern compile(int flags, String... elements) {
    return Pattern.compile(join(elements), flags);
  }

  /**
   * Convenience function to generate a capture group. {@code "capture("foo"} is bigger (and more
   * expensive) than {@code "(foo)"} but it's very explicit, and contrasts naturally with {@link
   * #noncapture(String)}.
   */
  static String capture(String element) {
    return "(" + element + ")";
  }

  /**
   * Convenience function to generate a capture group. {@code "capture("foo"} is bigger (and more
   * expensive) than {@code "(foo)"} but it's very explicit, and contrasts naturally with {@link
   * #noncapture(String)}.
   */
  static String capture(String... elements) {
    return capture(join(elements));
  }

  /**
   * Convenience function to generate a non-capture group. {@code "noncapture("foo"} is easier to
   * read than {@code "(?:foo)"}.
   */
  static String noncapture(String element) {
    return "(?:" + element + ")";
  }

  /**
   * Convenience function to generate a non-capture group. {@code "noncapture("foo"} is easier to
   * read than {@code "(?:foo)"}.
   */
  static String noncapture(String... elements) {
    return noncapture(join(elements));
  }

  /**
   * Convenience function to generate an optional regex element. {@code optional(noncapture("foo"))}
   * is a lot easier to read/verify than {@code "(?:foo)?"}.
   */
  static String optional(String element) {
    return element + "?";
  }

  /**
   * Convenience function to generate an optional regex element. {@code optional(noncapture("foo"))}
   * is a lot easier to read/verify than {@code "(?:foo)?"}.
   */
  static String optional(String... elements) {
    return optional(join(elements));
  }

  private static String join(String... elements) {
    return Joiner.on("").join(elements);
  }
}

@@ -4660,32 +4660,33 @@
 e %22capture(%22foo%22
+)
 %7D is bigger (and
@@ -4978,24 +4978,25 @@
 apture(%22foo%22
+)
 %7D is bigger 

Add a couple of right parens to JavaDoc ...