package git.path.matcher.path;

import git.path.NameMatcher;
import git.path.PathMatcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.Objects;

/**
 * Complex full-feature pattern matcher.
 *
 * @author Artem V. Navrotskiy <bozaro@users.noreply.github.com>
 */
public final class RecursivePathMatcher implements PathMatcher {
  @NotNull
  private final static int[] START_ARRAY = {0};
  @NotNull
  private final int[] indexes;
  @NotNull
  private final NameMatcher[] nameMatchers;
  private final boolean exact;

  public RecursivePathMatcher(@NotNull NameMatcher[] nameMatchers, boolean exact) {
    this(nameMatchers, exact, START_ARRAY);
  }

  private RecursivePathMatcher(@NotNull NameMatcher[] nameMatchers, boolean exact, @NotNull int[] indexes) {
    this.nameMatchers = nameMatchers;
    this.exact = exact;
    this.indexes = indexes;
  }

  @Nullable
  @Override
  public PathMatcher createChild(@NotNull String name, boolean isDir) {
    final int[] childs = new int[indexes.length * 2];
    boolean changed = false;
    int count = 0;
    for (int index : indexes) {
      if (nameMatchers[index].isMatch(name, isDir)) {
        if (nameMatchers[index].isRecursive()) {
          childs[count++] = index;
          if (nameMatchers[index + 1].isMatch(name, isDir)) {
            if (index + 2 == nameMatchers.length) {
              return exact && isDir ? null : AlwaysMatcher.INSTANCE;
            }
            childs[count++] = index + 2;
            changed = true;
          }
        } else {
          if (index + 1 == nameMatchers.length) {
            return exact && isDir ? null : AlwaysMatcher.INSTANCE;
          }
          childs[count++] = index + 1;
          changed = true;
        }
      } else {
        changed = true;
      }
    }
    if (!isDir) {
      return null;
    }
    if (!changed) {
      return this;
    }
    return count == 0 ? null : new RecursivePathMatcher(nameMatchers, exact, Arrays.copyOf(childs, count));
  }

  @Override
  public boolean isMatch() {
    return false;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    RecursivePathMatcher that = (RecursivePathMatcher) o;

    if (indexes.length != that.indexes.length) return false;
    final int offset = indexes[0];
    final int thatOffset = that.indexes[0];
    if (nameMatchers.length - offset != that.nameMatchers.length - thatOffset) return false;

    final int shift = thatOffset - offset;
    for (int i = offset; i < indexes.length; ++i) {
      if (indexes[i] != that.indexes[i + shift]) {
        return false;
      }
    }
    for (int i = offset; i < nameMatchers.length; ++i) {
      if (!Objects.equals(nameMatchers[i], that.nameMatchers[i + shift])) {
        return false;
      }
    }
    return true;
  }

  @Override
  public int hashCode() {
    int offset = indexes[0];
    int result = 0;
    for (int index : indexes) {
      result = 31 * (index - offset);
      assert (offset <= index);
    }
    for (int i = offset; i < nameMatchers.length; ++i) {
      result = 31 * result + nameMatchers[i].hashCode();
    }
    return result;
  }
}

@@ -1160,32 +1160,63 @@
 es) %7B%0A      if (
+index %3C nameMatchers.length && 
 nameMatchers%5Bind
@@ -1235,32 +1235,32 @@
 name, isDir)) %7B%0A
-
         if (name
@@ -1459,38 +1459,54 @@
     
-return 
+if (!
 exact 
-&& 
+%7C%7C !
 isDir
- ? null :
+) %7B%0A                return
  Alw
@@ -1518,32 +1518,48 @@
 tcher.INSTANCE;%0A
+              %7D%0A
             %7D%0A  
@@ -1720,38 +1720,52 @@
     
-return 
+if (!
 exact 
-&& 
+%7C%7C !
 isDir
- ? null :
+) %7B%0A              return
  Alw
@@ -1777,32 +1777,46 @@
 tcher.INSTANCE;%0A
+            %7D%0A
           %7D%0A    
@@ -2144,32 +2144,32 @@
  %7D%0A%0A  @Override%0A
-
   public boolean
@@ -2177,24 +2177,133 @@
 isMatch() %7B%0A
+    for (int index : indexes) %7B%0A      if (index == nameMatchers.length) %7B%0A        return true;%0A      %7D%0A    %7D%0A
     return f

Fix directory pattern matching