/*
 * Copyright 2000-2013 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.xdebugger.impl.ui;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.AppUIUtil;
import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.impl.XDebuggerUtilImpl;
import com.intellij.xdebugger.impl.XSourcePositionImpl;
import com.intellij.xdebugger.ui.DebuggerColors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author nik
 */
public class ExecutionPointHighlighter {
  private final Project myProject;
  private RangeHighlighter myRangeHighlighter;
  private Editor myEditor;
  private XSourcePosition mySourcePosition;
  private OpenFileDescriptor myOpenFileDescriptor;
  private boolean myUseSelection;
  private GutterIconRenderer myGutterIconRenderer;
  private static final Key<Boolean> EXECUTION_POINT_HIGHLIGHTER_KEY = Key.create("EXECUTION_POINT_HIGHLIGHTER_KEY");

  private final AtomicBoolean updateRequested = new AtomicBoolean();

  public ExecutionPointHighlighter(final Project project) {
    myProject = project;
  }

  public void show(final @NotNull XSourcePosition position, final boolean useSelection,
                   @Nullable final GutterIconRenderer gutterIconRenderer) {
    updateRequested.set(false);
    AppUIUtil.invokeLaterIfProjectAlive(myProject, new Runnable() {
      @Override
      public void run() {
        mySourcePosition = position;

        myOpenFileDescriptor = XSourcePositionImpl.createOpenFileDescriptor(myProject, position);
        myOpenFileDescriptor.setUseCurrentWindow(true);

        myGutterIconRenderer = gutterIconRenderer;
        myUseSelection = useSelection;

        doShow();
      }
    });
  }

  public void hide() {
    AppUIUtil.invokeOnEdt(new Runnable() {
      @Override
      public void run() {
        doHide();
      }
    });
  }

  public void navigateTo() {
    if (myOpenFileDescriptor != null) {
      myOpenFileDescriptor.navigateInEditor(myProject, true);
    }
  }

  @Nullable
  public VirtualFile getCurrentFile() {
    return myOpenFileDescriptor != null ? myOpenFileDescriptor.getFile() : null;
  }

  public void update() {
    if (updateRequested.compareAndSet(false, true)) {
      ApplicationManager.getApplication().invokeLater(new Runnable() {
        @Override
        public void run() {
          if (updateRequested.compareAndSet(true, false)) {
            doShow();
          }
        }
      }, myProject.getDisposed());
    }
  }

  public void updateGutterIcon(@NotNull final GutterIconRenderer renderer) {
    AppUIUtil.invokeOnEdt(new Runnable() {
      @Override
      public void run() {
        if (myRangeHighlighter != null && myGutterIconRenderer != null) {
          myRangeHighlighter.setGutterIconRenderer(renderer);
        }
      }
    });
  }

  private void doShow() {
    ApplicationManager.getApplication().assertIsDispatchThread();
    removeHighlighter();

    myEditor = XDebuggerUtilImpl.createEditor(myOpenFileDescriptor);
    if (myEditor != null) {
      addHighlighter();
    }
  }

  private void doHide() {
    updateRequested.set(false);

    ApplicationManager.getApplication().assertIsDispatchThread();
    removeHighlighter();
    myOpenFileDescriptor = null;
    myEditor = null;
    myGutterIconRenderer = null;
  }

  private void removeHighlighter() {
    if (myRangeHighlighter == null || myEditor == null) {
      return;
    }

    if (myUseSelection) {
      myEditor.getSelectionModel().removeSelection();
    }

    myRangeHighlighter.dispose();
    myRangeHighlighter = null;
  }

  private void addHighlighter() {
    int line = mySourcePosition.getLine();
    Document document = myEditor.getDocument();
    if (line >= document.getLineCount()) return;

    if (myUseSelection) {
      myEditor.getSelectionModel().setSelection(document.getLineStartOffset(line), document.getLineEndOffset(line) + document.getLineSeparatorLength(line));
      return;
    }

    if (myRangeHighlighter != null) return;

    EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
    myRangeHighlighter = myEditor.getMarkupModel().addLineHighlighter(line, DebuggerColors.EXECUTION_LINE_HIGHLIGHTERLAYER,
                                                                      scheme.getAttributes(DebuggerColors.EXECUTIONPOINT_ATTRIBUTES));
    myRangeHighlighter.putUserData(EXECUTION_POINT_HIGHLIGHTER_KEY, true);
    myRangeHighlighter.setGutterIconRenderer(myGutterIconRenderer);
  }
}

@@ -2523,32 +2523,69 @@
 ic void run() %7B%0A
+        updateRequested.set(false);%0A%0A
         mySource
@@ -3007,24 +3007,171 @@
         
-doHide()
+updateRequested.set(false);%0A%0A        removeHighlighter();%0A        myOpenFileDescriptor = null;%0A        myEditor = null;%0A        myGutterIconRenderer = null
 ;%0A      
@@ -4272,16 +4272,54 @@
 Editor =
+ myOpenFileDescriptor == null ? null :
  XDebugg
@@ -4432,250 +4432,8 @@
  %7D%0A%0A
-  private void doHide() %7B%0A    updateRequested.set(false);%0A%0A    ApplicationManager.getApplication().assertIsDispatchThread();%0A    removeHighlighter();%0A    myOpenFileDescriptor = null;%0A    myEditor = null;%0A    myGutterIconRenderer = null;%0A  %7D%0A%0A
   pr

fix NPE