// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.debug.ui.actions;

import org.chromium.debug.core.model.ConnectedTargetData;
import org.chromium.debug.core.model.Value;
import org.chromium.debug.core.model.VmResourceId;
import org.chromium.debug.core.sourcemap.SourcePosition;
import org.chromium.debug.core.sourcemap.SourcePositionMap;
import org.chromium.debug.core.sourcemap.SourcePositionMap.TranslateDirection;
import org.chromium.sdk.JsFunction;
import org.chromium.sdk.JsObject;
import org.chromium.sdk.JsValue;
import org.chromium.sdk.Script;
import org.chromium.sdk.TextStreamPosition;
import org.eclipse.core.resources.IFile;
import org.eclipse.debug.core.model.ISourceLocator;
import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;

/**
 * The action for context menu in Variable/Expression views that opens selected
 * function source text in editor.
 */
public abstract class OpenFunctionAction extends VariableBasedAction {
  public static class ForVariable extends OpenFunctionAction {
    public ForVariable() {
      super(VARIABLE_VIEW_ELEMENT_HANDLER);
    }
  }
  public static class ForExpression extends OpenFunctionAction {
    public ForExpression() {
      super(EXPRESSION_VIEW_ELEMENT_HANDLER);
    }
  }

  protected OpenFunctionAction(ElementHandler elementHandler) {
    super(elementHandler);
  }

  protected Runnable createRunnable(VariableWrapper wrapper) {
    if (wrapper == null) {
      return null;
    }
    final ConnectedTargetData connectedTargetData = wrapper.getConnectedTargetData();
    if (connectedTargetData == null) {
      return null;
    }
    final JsFunction jsFunction = getJsFunctionFromElement(wrapper);
    if (jsFunction == null) {
      return null;
    }
    return new Runnable() {

      public void run() {
        // This works in UI thread.
        IWorkbench workbench = PlatformUI.getWorkbench();
        final IWorkbenchWindow activeWorkbenchWindow = workbench.getActiveWorkbenchWindow();

        ISourceLocator sourceLocator =
            connectedTargetData.getDebugTarget().getLaunch().getSourceLocator();
        if (sourceLocator instanceof ISourceLookupDirector == false) {
          return;
        }
        ISourceLookupDirector director = (ISourceLookupDirector) sourceLocator;


        SourcePositionMap positionMap = connectedTargetData.getSourcePositionMap();
        SourcePosition userPosition;
        {
          // First get VM positions.
          Script script = jsFunction.getScript();
          if (script == null) {
            return;
          }
          TextStreamPosition functionOpenParenPosition = jsFunction.getOpenParenPosition();
          if (functionOpenParenPosition == null) {
            return;
          }

          // Convert them to user positions.
          userPosition = positionMap.translatePosition(
              VmResourceId.forScript(script), functionOpenParenPosition.getLine(),
              functionOpenParenPosition.getColumn(), TranslateDirection.VM_TO_USER);
        }

        Object sourceObject = director.getSourceElement(userPosition.getId());
        if (sourceObject instanceof IFile == false) {
          return;
        }
        IFile resource = (IFile) sourceObject;
        IEditorPart editor;
        try {
          editor= IDE.openEditor(activeWorkbenchWindow.getActivePage(), resource, true);
        } catch (PartInitException e) {
          throw new RuntimeException(e);
        }
        if (editor instanceof ITextEditor == false) {
          return;
        }
        ITextEditor textEditor = (ITextEditor) editor;

        int offset = calculateOffset(textEditor, userPosition);

        textEditor.selectAndReveal(offset, 0);
      }

      private int calculateOffset(ITextEditor editor, SourcePosition userPosition) {
        IDocumentProvider provider = editor.getDocumentProvider();
        IDocument document = provider.getDocument(editor.getEditorInput());
        int lineStartOffset;
        try {
          lineStartOffset = document.getLineOffset(userPosition.getLine());
        } catch (BadLocationException e) {
          throw new RuntimeException(e);
        }
        return lineStartOffset + userPosition.getColumn();
      }
    };
  }

  private JsFunction getJsFunctionFromElement(VariableWrapper wrapper) {
    if (wrapper == null) {
      return null;
    }
    final Value uiValue = wrapper.getValue();
    if (uiValue == null) {
      // Probably hasn't got result yet.
      return null;
    }
    JsValue jsValue = uiValue.getJsValue();
    if (jsValue == null) {
      return null;
    }
    JsObject jsObject = jsValue.asObject();
    if (jsObject == null) {
      return null;
    }
    return jsObject.asFunction();
  }
}
