/*
 * $Id$
 *
 * Copyright (c) 2004-2005 by the TeXlapse Team.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package net.sourceforge.texlipse.viewer;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import net.sourceforge.texlipse.TexlipsePlugin;
import net.sourceforge.texlipse.properties.TexlipseProperties;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchPage;
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.MarkerUtilities;


/**
 * Monitor the output of an external viewer program and open the
 * file from the correct location when viewer outputs
 * a "file:lineNumber" -string.
 *  
 * @author Kimmo Karlsson
 */
public class ViewerOutputScanner implements Runnable {

    // the stream reader
    private BufferedReader br;
    
    // the project this viewer is viewing
    private IProject project;

    /**
     * Create a new scanner.
     * @param project
     * @param in
     */
    public ViewerOutputScanner(IProject project, InputStream in) {
        this.project = project;
        this.br = new BufferedReader(new InputStreamReader(in));
    }

    /**
     * Empty constructor for the static version of the file open method.
     */
    protected ViewerOutputScanner(IProject project) {
        this.project = project;
    }
    
    /**
     * Convenience method for outsiders.
     * @param proj the current project
     * @param file file name, relative or absolute
     * @param lineNumber line number
     */
    public static void openInEditor(IProject proj, String file, int lineNumber) {
        new ViewerOutputScanner(proj).openFileFromLineNumber(file, lineNumber);
    }
    
    /**
     * Parse filename and line number from the given line of text.
     * 
     * @param line line of text from viewer, possibly a line number event
     */
    private void checkLine(String line) {
        
        int index = line.indexOf(':');
        if (index < 0) {
            return;
        }
        
        String file = line.substring(0, index);
        String number = line.substring(index+1);
        
        int lineNumber = -1;
        try {
            lineNumber = Integer.parseInt(number);
        } catch (NumberFormatException e) {
        }
        
        if (lineNumber >= 0) {
            openFileFromLineNumber(file, lineNumber);
        }
    }
    
    /**
     * Opens the given file from the given location if the file belongs
     * to the currently open project.
     * 
     * There is basically two possibilities for the filename:
     * 1) Path is relative: the filename represents a relative path
     *    from the current directory (project source dir) to the output dir.
     * 2) Path is absolute: the filename is an absolute
     *    path from the filesystem root to the project dir.
     * 
     * @param file the file name, relative or absolute
     * @param lineNumber line number
     */
    protected void openFileFromLineNumber(String file, int lineNumber) {

        if (project == null) {
            return;
        }
        
        IResource resource = null;
        
        // path may contain project path
        String projDir = project.getLocation().addTrailingSeparator().toOSString();
        int index = file.indexOf(projDir);
        if (index == 0) {
            
            // remove the project path (external or inside workspace)
            file = file.substring(projDir.length());
            resource = project.findMember(file);
            
        } else {
            
            // path is relative
            if (file.startsWith("..")) {
                // remove dots and 'File.separator'
                file = file.substring(3);
            }
            
            String outDir = TexlipseProperties.getProjectProperty(project, TexlipseProperties.OUTPUT_DIR_PROPERTY);
            if (outDir != null && outDir.length() > 0) {
                
                if (outDir.endsWith("/") || outDir.endsWith("\\")) {
                    outDir = outDir.substring(0, outDir.length()-1);
                }
                outDir = outDir.trim();
                if (file.indexOf(outDir) == 0) {
                    // remove output dir from path
                    file = file.substring(outDir.length()+1);
                }
            }
            
            IContainer srcDir = TexlipseProperties.getProjectSourceDir(project);
            resource = srcDir.findMember(file);
        }
        
        if (resource == null) {
            return;
        }
        
        IMarker mark = null;
        try {
            mark = resource.createMarker(IMarker.BOOKMARK);
            MarkerUtilities.setLineNumber(mark, lineNumber);
        } catch (CoreException e) {
        }
        
        if (mark != null) {
            
            Display display = TexlipsePlugin.getDefault().getWorkbench().getDisplay();
            display.syncExec(new EditorOpener(mark));
            
            try {
                mark.delete();
            } catch (CoreException e) {
            }
            
            new Thread(new Runnable() {
                public void run() {
                      ViewerManager.returnFocusToEclipse(true);
                }
             }).start();
        }
    }

    /**
     * Editor opener task. Opens a file containing the given marker
     * to a new window in the editor and scrolls the editor window
     * to the right position.
     */
    class EditorOpener implements Runnable {

        private IMarker marker;

        public EditorOpener(IMarker marker) {
            this.marker = marker;
        }
        
        public void run() {
            
            IWorkbenchPage page = TexlipsePlugin.getCurrentWorkbenchPage();
            if (page == null) {
                return;
            }
            
            try {
                IDE.openEditor(page, marker, false);
            } catch (PartInitException e) {
            }
        }
    }

    /**
     * Monitor the stream line by line.
     */
    public void run() {
        String line = null;
        
        try {
            while ((line = br.readLine()) != null) {
                
                checkLine(line);
            }
        } catch (IOException e) {
        }
    }
}

@@ -375,24 +375,45 @@
 eredReader;%0A
+import java.io.File;%0A
 import java.
@@ -4104,44 +4104,761 @@
     
-resource = project.findMember(file);
+// yap also adds the output dir%0A            IFolder outdir = TexlipseProperties.getProjectOutputDir(project);%0A            index = file.indexOf(outdir.getProjectRelativePath().toString());%0A            if (index == 0) %7B%0A                // remove output path%0A                file = file.substring(outdir.getProjectRelativePath().toString().length());%0A                while (file.startsWith(File.separator))%0A                    file = file.substring(1);%0A            %7D%0A            resource = project.findMember(file);%0A            if (resource == null) %7B%0A                //maybe in src folder%0A                IContainer srcDir = TexlipseProperties.getProjectSourceDir(project);%0A                resource = srcDir.findMember(file);                %0A            %7D
 %0A   

Fix Bug where eclipse cannot detect the correct inverse search file (sent by yap) if using an output folder