/*
 * Copyright 2000-2010 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 com.intellij.openapi.vcs.changes;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.FileStatusManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiFilter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Konstantin Bulenkov
 */
public class PsiChangeTracker {
  private PsiChangeTracker() {
  }

  public static <T extends PsiElement> Map<T, FileStatus> getElementsChanged(PsiElement file,
                                                                             PsiElement oldFile,
                                                                             final PsiFilter<T> filter) {
    final HashMap<T, FileStatus> result = new HashMap<>();
    final List<T> oldElements = new ArrayList<>();
    final List<T> elements = new ArrayList<>();

    if (file == null) {
      oldFile.accept(filter.createVisitor(oldElements));
      calculateStatuses(elements, oldElements, result, filter);
      return result;
    }

    final Project project = file.getProject();

    file.accept(filter.createVisitor(elements));
    final VirtualFile vf = file.getContainingFile().getVirtualFile();
    FileStatus status = vf == null ? null : FileStatusManager.getInstance(project).getStatus(vf);
    if (status == null && oldFile == null) {
      status = FileStatus.ADDED;
    }
    if (status == FileStatus.ADDED ||
        status == FileStatus.DELETED ||
        status == FileStatus.DELETED_FROM_FS ||
        status == FileStatus.UNKNOWN) {
      for (T element : elements) {
        result.put(element, status);
      }
      return result;
    }

    if (oldFile == null) return result;
    oldFile.accept(filter.createVisitor(oldElements));
    calculateStatuses(elements, oldElements, result, filter);

    return result;
  }

  private static <T extends PsiElement> Map<T, FileStatus> calculateStatuses(List<T> elements,
                                                                             List<T> oldElements,
                                                                             Map<T, FileStatus> result, PsiFilter<T> filter) {
    for (T element : elements) {
      T e = null;
      for (T oldElement : oldElements) {
        if (filter.areEquivalent(element, oldElement)) {
          e = oldElement;
          break;
        }
      }
      if (e != null) {
        oldElements.remove(e);
        if (!element.getText().equals(e.getText())) {
          result.put(element, FileStatus.MODIFIED);
        }
      }
      else {
        result.put(element, FileStatus.ADDED);
      }
    }

    for (T oldElement : oldElements) {
      result.put(oldElement, FileStatus.DELETED);
    }

    return result;
  }
}
