/*
 * Copyright 2003-2011 JetBrains s.r.o.
 *
 * 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 jetbrains.mps.util;

import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

/**
 * evgeny, 11/18/11
 */
public class PatternUtil {

  private static enum State {
    NO_QUOTING,
    QUOTING,
    SEQUENCE_LETTERS
  }

  public static boolean matchesPattern(String pattern, String matchingText) {
    if (pattern == null || pattern.length() == 0) {
      return true;
    }
    if (matchingText == null || matchingText.length() == 0) {
      return false;
    }
    return matchingText.charAt(0) == pattern.charAt(0) &&
        (matchingText.startsWith(pattern) || matchingText.matches(PatternUtil.getExactItemPatternBuilder(pattern, false, false).toString() + ".*"));
  }

  public static StringBuilder getExactItemPatternBuilder(String text, boolean useDots, boolean useStarAndQuestionMark) {
    StringBuilder b = new StringBuilder();
    State state = State.NO_QUOTING;
    for (int i = 0; i < text.length(); i++) {
      char c = text.charAt(i);
      state = appendNextChar(c, state, b, useDots, useStarAndQuestionMark);
    }
    if (state == State.QUOTING) {
      b.append("\\E");
    }
    return b;
  }

  private static State appendNextChar(char c, State state, StringBuilder b, boolean useDots, boolean useStarAndQuestionMark) {
    if (state == State.SEQUENCE_LETTERS && Character.isUpperCase(c)) {
      b.append("[a-z0-9_]*");
      b.append("((").append(c).append(")|((\\s)+[").append(Character.toLowerCase(c)).append(c).append("]))");
      return State.SEQUENCE_LETTERS;
    }
    if (c == '*' || c == '?' || c == '.' || c == '@' || Character.isLetterOrDigit(c) || c == '_') {
      if (state == State.QUOTING) {
        b.append("\\E");
      }
    } else {
      if (state != State.QUOTING) {
        b.append("\\Q");
      }
    }

    if (c == '*') {
      if (useStarAndQuestionMark) {
        b.append(".*");
      } else {
        b.append("\\*");
      }
      return State.NO_QUOTING;
    } else if (c == '?') {
      if (useStarAndQuestionMark) {
        b.append(".");
      } else {
        b.append("\\?");
      }
      return State.NO_QUOTING;
    } else if (c == '.') {
      if (useDots) {
        b.append("[^\\.]*\\.");
      } else {
        b.append("\\.");
      }
      return State.NO_QUOTING;
    } else if (c == '@') {
      b.append("[^\\@\\.]*\\@");
      return State.NO_QUOTING;
    } else if (Character.isLetterOrDigit(c) || c == '_') {
      b.append(c);
      return State.SEQUENCE_LETTERS;
    } else {
      b.append(c);
      return State.QUOTING;
    }
  }

  public static List<Integer> getIndexes(@NotNull String pattern, boolean useDots, @NotNull String matchingText) {
    List<Integer> indexList = new ArrayList<Integer>();
    if (addIndexes(matchingText, indexList, pattern)) {
      return indexList;
    } else {
      indexList = new ArrayList<Integer>();
    }
    StringBuilder nextSubstring = new StringBuilder();
    for (int i = 0; i < pattern.length(); i++) {
      char c = pattern.charAt(i);
      if (c == '*' || c == '?') {
        if (!addIndexes(matchingText, indexList, nextSubstring.toString())) return new ArrayList<Integer>();
        nextSubstring = new StringBuilder();
      } else if (useDots && c == '.' || c == '@' || Character.isUpperCase(c)) {
        if (!addIndexes(matchingText, indexList, nextSubstring.toString())) return new ArrayList<Integer>();
        String upperCase = new String(new char[]{c});
        String spaceAndLowerCase = " " + Character.toLowerCase(c);
        if (!addIndexes(matchingText, indexList, upperCase) && !addIndexes(matchingText, indexList, spaceAndLowerCase)) return new ArrayList<Integer>();
        nextSubstring = new StringBuilder();
      } else {
        nextSubstring.append(c);
      }
    }
    if (!addIndexes(matchingText, indexList, nextSubstring.toString())) return new ArrayList<Integer>();
    return indexList;
  }

  private static boolean addIndexes(String matchingText, List<Integer> indexList, String nextSubstring) {
    int curIndex = indexList.isEmpty() ? 0 : indexList.get(indexList.size() - 1) + 1;
    int indexOf = matchingText.indexOf(nextSubstring, curIndex);
    if (indexOf == -1) {
      return false;
    }
    for (int j = 0; j < nextSubstring.length(); ++j) {
      indexList.add(indexOf + j);
    }
    return true;
  }
}
