/*
 * Copyright 2006 Sascha Weinreuter
 *
 * 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.intellij.lang.regexp.surroundWith;

import com.intellij.lang.surroundWith.SurroundDescriptor;
import com.intellij.lang.surroundWith.Surrounder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import org.intellij.lang.regexp.psi.RegExpAtom;
import org.intellij.lang.regexp.psi.RegExpBranch;
import org.intellij.lang.regexp.psi.RegExpElement;
import org.intellij.lang.regexp.psi.RegExpPattern;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

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

public class SimpleSurroundDescriptor implements SurroundDescriptor {
  private static final Surrounder[] SURROUNDERS = {
    new GroupSurrounder("Capturing Group (pattern)", "("),
    new GroupSurrounder("Non-Capturing Group (?:pattern)", "(?:"),
    new GroupSurrounder("Atomic Group (?:pattern)", "(?:"),
    new GroupSurrounder("Positive Lookbehind (?<=pattern)", "(?<="),
    new GroupSurrounder("Negative Lookbehind (?<!pattern)", "(?<!"),
    new GroupSurrounder("Positive Lookahead (?=pattern)", "(?="),
    new GroupSurrounder("Negative Lookahead (?!pattern)", "(?!"),
  };

  @Override
  @NotNull
  public PsiElement[] getElementsToSurround(PsiFile file, int startOffset, int endOffset) {
    // adjust start/end
    PsiElement element1 = file.findElementAt(startOffset);
    PsiElement element2 = file.findElementAt(endOffset - 1);
    if (element1 instanceof PsiWhiteSpace) {
      startOffset = element1.getTextRange().getEndOffset();
    }
    if (element2 instanceof PsiWhiteSpace) {
      endOffset = element2.getTextRange().getStartOffset();
    }

    RegExpElement pattern = findElementAtStrict(file, startOffset, endOffset, RegExpPattern.class);
    if (pattern != null) return new RegExpElement[]{pattern};

    RegExpElement branch = findElementAtStrict(file, startOffset, endOffset, RegExpBranch.class);
    if (branch != null) return new RegExpElement[]{branch};

    List<PsiElement> atoms = new ArrayList<>();
    RegExpAtom atom = PsiTreeUtil.findElementOfClassAtRange(file, startOffset, endOffset, RegExpAtom.class);
    for (; atom != null; atom = PsiTreeUtil.findElementOfClassAtRange(file, startOffset, endOffset, RegExpAtom.class)) {
      atoms.add(atom);
      startOffset = atom.getTextRange().getEndOffset();

      // handle embedded whitespace
      if ((element1 = file.findElementAt(startOffset)) instanceof PsiWhiteSpace) {
        startOffset = element1.getTextRange().getEndOffset();
        atoms.add(element1);
      }
    }

    if (startOffset == endOffset && atoms.size() > 0) {
      PsiElement[] elements = PsiUtilCore.toPsiElementArray(atoms);
      if ((atoms.size() == 1 || PsiTreeUtil.findCommonParent(elements) == elements[0].getParent())) {
        return elements;
      }
    }
    return PsiElement.EMPTY_ARRAY;
  }

  @Override
  @NotNull
  public Surrounder[] getSurrounders() {
    return SURROUNDERS;
  }

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

  @Nullable
  private static <T extends RegExpElement> T findElementAtStrict(PsiFile file, int startOffset, int endOffset, Class<T> clazz) {
    T element = PsiTreeUtil.findElementOfClassAtRange(file, startOffset, endOffset, clazz);
    if (element == null || element.getTextRange().getEndOffset() < endOffset) return null;
    return element;
  }
}
