/*
 * Copyright 2016 The Bazel Authors. All rights reserved.
 *
 * 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.google.idea.blaze.base.lang.buildfile.psi;

import com.google.idea.blaze.base.lang.buildfile.search.BlazePackage;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.navigation.ItemPresentation;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import java.lang.reflect.Array;
import java.util.Arrays;
import javax.annotation.Nullable;
import javax.swing.Icon;

/** Base PSI class for the BUILD language */
public abstract class BuildElementImpl extends ASTWrapperPsiElement implements BuildElement {

  public BuildElementImpl(ASTNode astNode) {
    super(astNode);
  }

  public <P extends PsiElement> P getPsiChild(IElementType type, Class<P> psiClass) {
    ASTNode childNode = getNode().findChildByType(type);
    return childNode != null ? (P) childNode.getPsi() : null;
  }

  @Override
  public <P extends PsiElement> P[] childrenOfClass(Class<P> psiClass) {
    return findChildrenByClass(psiClass);
  }

  @Nullable
  @Override
  public <P extends PsiElement> P firstChildOfClass(Class<P> psiClass) {
    return findChildByClass(psiClass);
  }

  /**
   * Returns the BuildElement child at the specified index, where index is calculated after
   * filtering out non-BuildElement children.
   *
   * @return null if index >= number of BuildElement children
   */
  @Nullable
  protected BuildElement getBuildElementChild(int index) {
    BuildElement[] children = buildElementChildren();
    return children.length <= index ? null : children[index];
  }

  public BuildElement[] buildElementChildren() {
    return Arrays.stream(getNode().getChildren(null))
        .map(ASTNode::getPsi)
        .filter(psiElement -> psiElement instanceof BuildElement)
        .toArray(BuildElement[]::new);
  }

  protected <T extends BuildElement> T[] childrenToPsi(TokenSet filterSet, T[] array) {
    final ASTNode[] nodes = getNode().getChildren(filterSet);
    T[] psiElements = (T[]) Array.newInstance(array.getClass().getComponentType(), nodes.length);
    for (int i = 0; i < nodes.length; i++) {
      psiElements[i] = (T) nodes[i].getPsi();
    }
    return psiElements;
  }

  /** Finds the n'th child of the specified type */
  @Nullable
  protected PsiElement childToPsi(TokenSet filterSet, int index) {
    final ASTNode[] nodes = getNode().getChildren(filterSet);
    if (nodes.length <= index) {
      return null;
    }
    return nodes[index].getPsi();
  }

  @Nullable
  protected IElementType getParentType() {
    ASTNode node = getNode().getTreeParent();
    return node != null ? node.getElementType() : null;
  }

  public String nonNullName() {
    String name = getName();
    return name != null ? name : "<unnamed>";
  }

  @Override
  public String getPresentableText() {
    return nonNullName();
  }

  @Override
  public String toString() {
    return super.toString() + ": " + getPresentableText();
  }

  @Override
  public void accept(PsiElementVisitor visitor) {
    if (visitor instanceof BuildElementVisitor) {
      acceptVisitor(((BuildElementVisitor) visitor));
    } else {
      super.accept(visitor);
    }
  }

  protected abstract void acceptVisitor(BuildElementVisitor visitor);

  @Nullable
  @Override
  public PsiElement getReferencedElement() {
    PsiReference[] refs = getReferences();
    for (PsiReference ref : refs) {
      PsiElement element = ref.resolve();
      if (element != null) {
        return element;
      }
    }
    return null;
  }

  @Override
  public ItemPresentation getPresentation() {
    final BuildElement element = this;
    return new ItemPresentation() {
      @Override
      public String getPresentableText() {
        return element.getPresentableText();
      }

      @Override
      public String getLocationString() {
        return element.getLocationString();
      }

      @Override
      public Icon getIcon(boolean unused) {
        return element.getIcon(0);
      }
    };
  }

  @Nullable
  @Override
  public BlazePackage getBlazePackage() {
    PsiFile file = getContainingFile();
    return file != null ? BlazePackage.getContainingPackage(file) : null;
  }

  @Nullable
  @Override
  public BuildFile getContainingFile() {
    return (BuildFile) super.getContainingFile();
  }
}

@@ -1113,65 +1113,8 @@
 et;%0A
-import java.lang.reflect.Array;%0Aimport java.util.Arrays;%0A
 impo
@@ -1866,1034 +1866,8 @@
  %7D%0A%0A
-  /**%0A   * Returns the BuildElement child at the specified index, where index is calculated after%0A   * filtering out non-BuildElement children.%0A   *%0A   * @return null if index %3E= number of BuildElement children%0A   */%0A  @Nullable%0A  protected BuildElement getBuildElementChild(int index) %7B%0A    BuildElement%5B%5D children = buildElementChildren();%0A    return children.length %3C= index ? null : children%5Bindex%5D;%0A  %7D%0A%0A  public BuildElement%5B%5D buildElementChildren() %7B%0A    return Arrays.stream(getNode().getChildren(null))%0A        .map(ASTNode::getPsi)%0A        .filter(psiElement -%3E psiElement instanceof BuildElement)%0A        .toArray(BuildElement%5B%5D::new);%0A  %7D%0A%0A  protected %3CT extends BuildElement%3E T%5B%5D childrenToPsi(TokenSet filterSet, T%5B%5D array) %7B%0A    final ASTNode%5B%5D nodes = getNode().getChildren(filterSet);%0A    T%5B%5D psiElements = (T%5B%5D) Array.newInstance(array.getClass().getComponentType(), nodes.length);%0A    for (int i = 0; i %3C nodes.length; i++) %7B%0A      psiElements%5Bi%5D = (T) nodes%5Bi%5D.getPsi();%0A    %7D%0A    return psiElements;%0A  %7D%0A%0A
   /*

Remove unused methods