/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * Portions of this software are based upon public domain software
 * originally written at the National Center for Supercomputing Applications,
 * University of Illinois, Urbana-Champaign.
 */
package com.bonevich.eclipse.cvsgrapher.editors;

import com.bonevich.eclipse.cvsgrapher.CvsGraphPluginMessages;
import com.bonevich.eclipse.cvsgrapher.model.CvsGraphModel;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.draw2d.FigureCanvas;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.ui.parts.GraphicalEditor;
import org.eclipse.gef.ui.parts.GraphicalViewerKeyHandler;
import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
import org.eclipse.team.internal.ccvs.core.ICVSRemoteFile;
import org.eclipse.team.internal.ccvs.core.ILogEntry;
import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;

import java.lang.reflect.InvocationTargetException;

/**
 * The CVS Graph GEF editor.
 * @author jbonevic
 * @todo add tooltip to editor tab (class level todo) 
 */
public final class CvsGraphEditor extends GraphicalEditor
{
	private static final Log _logger = LogFactory.getLog(CvsGraphEditor.class);
	
	public static final String EDITOR_ID = "com.bonevich.eclipse.cvsgrapher.editor";
	
	private ICVSRemoteFile  _remoteFile = null;
	private ILogEntry[]     _entries = null;
	
	public CvsGraphEditor()
	{
		setEditDomain(new DefaultEditDomain(this));
	}

	/**
	 * @see org.eclipse.ui.IEditorPart#init(IEditorSite, IEditorInput)
	 * @todo add tooltip to editor tab 
	 */
	public void init(IEditorSite iSite, IEditorInput iInput) throws PartInitException
	{
		setSite(iSite);
		setInput(iInput);
		initializeCvsRemoteFile();
	}

	/**
	 * @see org.eclipse.gef.ui.parts.GraphicalEditor#initializeGraphicalViewer()
	 */
	protected void initializeGraphicalViewer()
	{
		if (_logger.isDebugEnabled())
		{
			_logger.debug("Opening editor for " + _remoteFile.getRepositoryRelativePath());
			_logger.debug("Viewer = " + getGraphicalViewer());
		}
		EditPartViewer viewer = getGraphicalViewer();
		viewer.setContents(new CvsGraphModel(_entries));
	}

	/**
	  * @see org.eclipse.ui.IEditorPart#isSaveAsAllowed()
	  */
	public boolean isSaveAsAllowed()
	{
		return false;
	}

	/**
	 * @see org.eclipse.ui.IEditorPart#doSave(IProgressMonitor)
	 */
	public void doSave(IProgressMonitor iMonitor)
	{
	}

	/**
	 * @see org.eclipse.ui.IEditorPart#doSaveAs()
	 */
	public void doSaveAs()
	{
	}

	/**
	 * @see org.eclipse.ui.IEditorPart#gotoMarker(IMarker)
	 */
	public void gotoMarker(IMarker iMarker)
	{
	}

	/**
	 * @see org.eclipse.ui.IEditorPart#isDirty()
	 */
	public boolean isDirty()
	{
		return false;
	}

	/**
	 * @see org.eclipse.gef.ui.parts.GraphicalEditor#configureGraphicalViewer()
	 */
	protected void configureGraphicalViewer()
	{
		super.configureGraphicalViewer();
		GraphicalViewer viewer = getGraphicalViewer();

		ScalableFreeformRootEditPart rootPart = new ScalableFreeformRootEditPart();
		viewer.setRootEditPart(rootPart);
		viewer.setEditPartFactory(new CvsGraphEditPartFactory());

		((FigureCanvas) viewer.getControl()).setScrollBarVisibility(FigureCanvas.ALWAYS);
		viewer.setContextMenu(new CvsGraphContextMenu(viewer, rootPart.getZoomManager()));
		viewer.setKeyHandler(new CvsGraphViewerKeyHandler(viewer));
	}

	private void initializeCvsRemoteFile()
	{
		IResource resource = (IResource) getEditorInput().getAdapter(IResource.class);
		if (resource instanceof IFile)
		{
			IFile file = (IFile) resource;

			if (_logger.isDebugEnabled())
			{
				_logger.debug("resource = " + file);
			}

			RepositoryProvider teamProvider =
				RepositoryProvider.getProvider(
					file.getProject(), 
					CVSProviderPlugin.getTypeId()
				);
			if (teamProvider != null)
			{
				try
				{
					_remoteFile = (ICVSRemoteFile) CVSWorkspaceRoot.getRemoteResourceFor(file);
					setTitle(CvsGraphPluginMessages.bind("CvsGraphEditor.titleWithArgument", _remoteFile.getName())); //$NON-NLS-1$
					initializeLogEntries();
				}
				catch (TeamException e)
				{
					CVSUIPlugin.openError(getSite().getShell(), null, null, e);
				}
			}
			return;
		}
		_remoteFile = null;
		setTitle(CvsGraphPluginMessages.bind("CvsGraphEditor.title")); //$NON-NLS-1$
	}

	private void initializeLogEntries()
	{
		try
		{
			new ProgressMonitorDialog(
				getSite().getShell()
			).run(
				true,
				true,
				new IRunnableWithProgress()
				{
					public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException
					{
						try
						{
							_entries = _remoteFile.getLogEntries(monitor);
						}
						catch (TeamException e)
						{
							throw new InvocationTargetException(e);
						}
					}
				}
			);
		}
		catch (InterruptedException ignore)
		{
			// ignore cancellation
		}
		catch (InvocationTargetException e)
		{
			CVSUIPlugin.openError(getSite().getShell(), null, null, e);
		}
	}
	
	///////////////////////////////////////////
	// Inner classes
	
	/**
	 * Extends the basic key handler to add appropriate scrolling behavior for PageDown,
	 * PageUp, Home, and End key events.
	 * 
	 * @author jbonevic
	 */
	final class CvsGraphViewerKeyHandler extends GraphicalViewerKeyHandler
	{
		public CvsGraphViewerKeyHandler(GraphicalViewer viewer)
		{
			super(viewer);
		}
		
		public boolean keyPressed(KeyEvent event)
		{
			if (event.keyCode == SWT.PAGE_DOWN || event.keyCode == SWT.PAGE_UP || 
				event.keyCode == SWT.HOME || event.keyCode == SWT.END )
			{
				ScrollingGraphicalViewer viewer = (ScrollingGraphicalViewer) getGraphicalViewer();
				FigureCanvas canvas = (FigureCanvas) viewer.getControl();

				int x = canvas.getViewport().getViewLocation().x;
				int y = canvas.getViewport().getViewLocation().y;
				int dx = 0;
				int dy = 0;

				Dimension viewingArea = canvas.getViewport().getClientArea().getSize();
				Rectangle totalArea = canvas.getBounds();
				
				switch (event.keyCode)
				{
					case SWT.PAGE_DOWN :	dx = x; dy = y + viewingArea.height; break;
					case SWT.PAGE_UP :		dx = x; dy = y - viewingArea.height; break;
					case SWT.HOME :			dx = 0; dy = y; break;
					case SWT.END :			dx = totalArea.width; dy = y; break;
					default:                dx = x; dy = y; break;
				}
				
				canvas.scrollSmoothTo(dx, dy);
				return true;
			}
			return super.keyPressed(event);
		}
	}
}
