/*
 * Copyright 2000-2016 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.editor.ex.util;

import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.ScrollingModel;
import org.jetbrains.annotations.Nullable;

import java.awt.*;
import java.util.HashMap;
import java.util.Map;

/**
 * Allows to keep caret's position in editor window at the same vertical position for operations, that can potentially move caret.
 * An instance of this class is to be created before the operation, method {@link #restoreOriginalLocation()} should be called after it.
 */
public class CaretVisualPositionKeeper {
  private final Map<Editor, Integer> myCaretRelativeVerticalPositions = new HashMap<>();

  public CaretVisualPositionKeeper(@Nullable Editor editor) {
    this(editor == null ? Editor.EMPTY_ARRAY : new Editor[]{editor});
  }

  public CaretVisualPositionKeeper(@Nullable Document document) {
    this(document == null ? Editor.EMPTY_ARRAY : EditorFactory.getInstance().getEditors(document));
  }

  private CaretVisualPositionKeeper(Editor[] editors) {
    for (Editor editor : editors) {
      Rectangle visibleArea = editor.getScrollingModel().getVisibleAreaOnScrollingFinished();
      Point pos = editor.visualPositionToXY(editor.getCaretModel().getVisualPosition());
      int relativePosition = pos.y - visibleArea.y;
      myCaretRelativeVerticalPositions.put(editor, relativePosition);
    }
  }

  public void restoreOriginalLocation() {
    for (Map.Entry<Editor, Integer> e : myCaretRelativeVerticalPositions.entrySet()) {
      Editor editor = e.getKey();
      int relativePosition = e.getValue();
      Point caretLocation = editor.visualPositionToXY(editor.getCaretModel().getVisualPosition());
      int scrollOffset = caretLocation.y - relativePosition;
      ScrollingModel scrollingModel = editor.getScrollingModel();
      Rectangle targetArea = scrollingModel.getVisibleAreaOnScrollingFinished();
      // when animated scrolling is in progress, we'll not stop it immediately
      boolean useAnimation = !targetArea.equals(scrollingModel.getVisibleArea());
      if (!useAnimation) scrollingModel.disableAnimation();
      scrollingModel.scroll(targetArea.x, scrollOffset);
      if (!useAnimation) scrollingModel.enableAnimation();
    }
  }
}
