/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.uima.ruta.ide.ui.console;

import java.text.MessageFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.dltk.debug.ui.DLTKDebugUIPlugin;
import org.eclipse.dltk.internal.ui.editor.EditorUtility;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.console.IHyperlink;
import org.eclipse.ui.console.TextConsole;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;

/**
 * A hyperlink from a stack trace line of the form "(file "*.*")"
 */
public class RutaFileHyperlink implements IHyperlink {

  private TextConsole fConsole;

  public RutaFileHyperlink(TextConsole console) {
    fConsole = console;
  }

  @Override
  public void linkEntered() {
  }

  @Override
  public void linkExited() {
  }

  @Override
  public void linkActivated() {
    try {
      String fileName;
      int lineNumber;
      try {
        String linkText = getLinkText();
        fileName = getFileName(linkText);
        lineNumber = getLineNumber(linkText);
      } catch (CoreException e1) {
        ErrorDialog.openError(DLTKDebugUIPlugin.getActiveWorkbenchShell(),
                ConsoleMessages.RutaFileHyperlink_Error, ConsoleMessages.RutaFileHyperlink_Error,
                e1.getStatus());
        return;
      }

      // documents start at 0
      if (lineNumber > 0) {
        lineNumber--;
      }
      Object sourceElement = getSourceModule(fileName);
      if (sourceElement != null) {
        IEditorPart part = EditorUtility.openInEditor(sourceElement);
        IEditorPart editorPart = EditorUtility.openInEditor(sourceElement);
        if (editorPart instanceof ITextEditor && lineNumber >= 0) {
          ITextEditor textEditor = (ITextEditor) editorPart;
          IDocumentProvider provider = textEditor.getDocumentProvider();
          IEditorInput input = part.getEditorInput();
          provider.connect(input);
          IDocument document = provider.getDocument(input);
          try {
            IRegion line = document.getLineInformation(lineNumber);
            textEditor.selectAndReveal(line.getOffset(), line.getLength());
          } catch (BadLocationException e) {

          }
          provider.disconnect(input);
        }
        return;
      }
      // did not find source
      MessageDialog.openInformation(DLTKDebugUIPlugin.getActiveWorkbenchShell(),
              ConsoleMessages.RutaFileHyperlink_Information_1,
              MessageFormat.format(ConsoleMessages.RutaFileHyperlink_Source_not_found_for__0__2,
                      new String[] { fileName }));
    } catch (CoreException e) {
      DLTKDebugUIPlugin.errorDialog(
              ConsoleMessages.RutaFileHyperlink_An_exception_occurred_while_following_link__3, e);
      return;
    }
  }

  public String getEditorId(IEditorInput input, Object inputObject) {
    try {
      IEditorDescriptor descriptor = IDE.getEditorDescriptor(input.getName());
      return descriptor.getId();
    } catch (PartInitException e) {
      return null;
    }
  }

  public IEditorInput getEditorInput(Object item) {
    return EditorUtility.getEditorInput(item);
  }

  protected Object getSourceModule(String fileName) {
    IFile f = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(new Path(fileName));
    return f;
  }

  /**
   * Returns the fully qualified name of the type to open
   * 
   * @param linkText
   *          contains the type name
   * @return fully qualified type name
   * @exception CoreException
   *              if unable to parse the type name
   */
  protected String getFileName(String linkText) throws CoreException {
    Pattern p = Pattern.compile("\\(file \"(.*)\"");
    Matcher m = p.matcher(linkText);
    if (m.find()) {
      String name = m.group(1);
      return name;
    }
    IStatus status = new Status(IStatus.ERROR, DLTKDebugUIPlugin.getUniqueIdentifier(), 0,
            ConsoleMessages.RutaFileHyperlink_Unable_to_parse_type_name_from_hyperlink__5, null);
    throw new CoreException(status);
  }

  protected int getLineNumber(String linkText) throws CoreException {
    Pattern p = Pattern.compile("line (\\d*)");
    Matcher m = p.matcher(linkText);
    if (m.find()) {
      String lineText = m.group(1);
      try {
        return Integer.parseInt(lineText);
      } catch (NumberFormatException e) {
        IStatus status = new Status(IStatus.ERROR, DLTKDebugUIPlugin.getUniqueIdentifier(), 0,
                ConsoleMessages.RutaFileHyperlink_Unable_to_parse_line_number_from_hyperlink__6, e);
        throw new CoreException(status);
      }
    }
    IStatus status = new Status(IStatus.ERROR, DLTKDebugUIPlugin.getUniqueIdentifier(), 0,
            ConsoleMessages.RutaFileHyperlink_Unable_to_parse_line_number_from_hyperlink__7, null);
    throw new CoreException(status);
  }

  protected TextConsole getConsole() {
    return fConsole;
  }

  protected String getLinkText() throws CoreException {
    try {
      IDocument document = getConsole().getDocument();
      IRegion region = getConsole().getRegion(this);
      int regionOffset = region.getOffset();

      int lineNumber = document.getLineOfOffset(regionOffset);
      IRegion lineInformation = document.getLineInformation(lineNumber);
      int lineOffset = lineInformation.getOffset();
      String line = document.get(lineOffset, lineInformation.getLength());

      int regionOffsetInLine = regionOffset - lineOffset;

      int linkEnd = line.indexOf(')', regionOffsetInLine);
      int linkStart = line.lastIndexOf(' ', regionOffsetInLine);

      return line.substring(linkStart == -1 ? 0 : linkStart + 1, linkEnd + 1);
    } catch (BadLocationException e) {
      IStatus status = new Status(IStatus.ERROR, DLTKDebugUIPlugin.getUniqueIdentifier(), 0,
              ConsoleMessages.RutaFileHyperlink_Unable_to_retrieve_hyperlink_text__8, e);
      throw new CoreException(status);
    }
  }

}
