package com.intellij.vcs.log.data;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.VcsLogBranchFilter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

public class VcsLogBranchFilterImpl implements VcsLogBranchFilter {
  private static final Logger LOG = Logger.getInstance(VcsLogBranchFilterImpl.class);

  @NotNull private final List<String> myBranches;
  @NotNull private final List<Pattern> myPatterns;

  @NotNull private final List<String> myExcludedBranches;
  @NotNull private final List<Pattern> myExcludedPatterns;

  private VcsLogBranchFilterImpl(@NotNull List<String> branches,
                                 @NotNull List<Pattern> patterns,
                                 @NotNull List<String> excludedBranches,
                                 @NotNull List<Pattern> excludedPatterns) {
    myBranches = branches;
    myPatterns = patterns;
    myExcludedBranches = excludedBranches;
    myExcludedPatterns = excludedPatterns;
  }

  @Deprecated
  public VcsLogBranchFilterImpl(@NotNull Collection<String> branches, @NotNull Collection<String> excludedBranches) {
    myBranches = new ArrayList<>(branches);
    myPatterns = new ArrayList<>();
    myExcludedBranches = new ArrayList<>(excludedBranches);
    myExcludedPatterns = new ArrayList<>();
  }

  @Nullable
  public static VcsLogBranchFilterImpl fromBranch(@NotNull final String branchName) {
    return new VcsLogBranchFilterImpl(Collections.singletonList(branchName), Collections.<Pattern>emptyList(),
                                      Collections.<String>emptyList(), Collections.<Pattern>emptyList());
  }

  @NotNull
  public static VcsLogBranchFilterImpl fromTextPresentation(@NotNull Collection<String> strings, @NotNull Set<String> existingBranches) {
    List<String> branchNames = new ArrayList<>();
    List<String> excludedBranches = new ArrayList<>();
    List<Pattern> patterns = new ArrayList<>();
    List<Pattern> excludedPatterns = new ArrayList<>();

    for (String string : strings) {
      boolean isExcluded = string.startsWith("-");
      string = isExcluded ? string.substring(1) : string;
      boolean isRegexp = !existingBranches.contains(string);

      if (isRegexp) {
        try {
          Pattern pattern = Pattern.compile(string);
          if (isExcluded) {
            excludedPatterns.add(pattern);
          }
          else {
            patterns.add(pattern);
          }
        }
        catch (PatternSyntaxException e) {
          LOG.warn("Pattern " + string + " is not a proper regular expression and no branch can be found with that name.", e);
          if (isExcluded) {
            excludedBranches.add(string);
          }
          else {
            branchNames.add(string);
          }
        }
      }
      else {
        if (isExcluded) {
          excludedBranches.add(string);
        }
        else {
          branchNames.add(string);
        }
      }
    }

    return new VcsLogBranchFilterImpl(branchNames, patterns, excludedBranches, excludedPatterns);
  }

  @NotNull
  @Override
  public Collection<String> getTextPresentation() {
    List<String> result = new ArrayList<>();

    result.addAll(myBranches);
    result.addAll(ContainerUtil.map(myPatterns, pattern -> pattern.pattern()));

    result.addAll(ContainerUtil.map(myExcludedBranches, branchName -> "-" + branchName));
    result.addAll(ContainerUtil.map(myExcludedPatterns, pattern -> "-" + pattern.pattern()));

    return result;
  }

  @Override
  public String toString() {
    String result = "";
    if (!myPatterns.isEmpty()) {
      result += "on patterns: " + StringUtil.join(myPatterns, ", ");
    }
    if (!myBranches.isEmpty()) {
      if (!result.isEmpty()) result += "; ";
      result += "on branches: " + StringUtil.join(myBranches, ", ");
    }
    if (!myExcludedPatterns.isEmpty()) {
      if (result.isEmpty()) result += "; ";
      result += "not on patterns: " + StringUtil.join(myExcludedPatterns, ", ");
    }
    if (!myExcludedBranches.isEmpty()) {
      if (result.isEmpty()) result += "; ";
      result += "not on branches: " + StringUtil.join(myExcludedBranches, ", ");
    }
    return result;
  }

  @Override
  public boolean matches(@NotNull String name) {
    return isIncluded(name) && !isExcluded(name);
  }

  private boolean isIncluded(@NotNull String name) {
    if (myPatterns.isEmpty() && myBranches.isEmpty()) return true;
    if (myBranches.contains(name)) return true;
    for (Pattern regexp : myPatterns) {
      if (regexp.matcher(name).matches()) return true;
    }
    return false;
  }

  private boolean isExcluded(@NotNull String name) {
    if (myExcludedBranches.contains(name)) return true;
    for (Pattern regexp : myExcludedPatterns) {
      if (regexp.matcher(name).matches()) return true;
    }
    return false;
  }
}

@@ -270,51 +270,8 @@
 ull;
-%0Aimport org.jetbrains.annotations.Nullable;
 %0A%0Aim
@@ -1497,15 +1497,14 @@
   @N
+otN
 ull
-able
 %0A  p
@@ -1559,22 +1559,16 @@
 NotNull 
-final 
 String b
@@ -1665,33 +1665,24 @@
 Collections.
-%3CPattern%3E
 emptyList(),
@@ -1732,24 +1732,16 @@
 ections.
-%3CString%3E
 emptyLis
@@ -1757,25 +1757,16 @@
 ections.
-%3CPattern%3E
 emptyLis

fix annotation, remove final, use diamonds