/***************************************************************************************************
 * Copyright (c) 2005, 2006 IBM Corporation and others. 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
 * 
 * Contributors: IBM Corporation - initial API and implementation
 **************************************************************************************************/
package org.eclipse.ui.internal.intro.impl.presentations;

import java.util.Map;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.util.Geometry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.RectangleAnimation;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.WorkbenchWindow;
import org.eclipse.ui.internal.dnd.DragUtil;
import org.eclipse.ui.internal.intro.impl.IntroPlugin;
import org.eclipse.ui.internal.intro.impl.Messages;
import org.eclipse.ui.internal.intro.impl.model.IntroLaunchBarElement;
import org.eclipse.ui.internal.intro.impl.model.IntroLaunchBarShortcut;
import org.eclipse.ui.internal.intro.impl.model.IntroTheme;
import org.eclipse.ui.internal.intro.impl.swt.SharedStyleManager;
import org.eclipse.ui.internal.intro.impl.util.ImageUtil;
import org.eclipse.ui.internal.layout.ITrimManager;
import org.eclipse.ui.internal.layout.IWindowTrim;
import org.eclipse.ui.intro.IIntroPart;
import org.eclipse.ui.intro.config.CustomizableIntroPart;
import org.eclipse.ui.intro.config.IIntroURL;
import org.eclipse.ui.intro.config.IntroURLFactory;

/**
 * This class is responsible for creating the intro launch bar in the provided parent. It creates
 * 'restore' and 'close' actions, as well as actions for each shortcut element contributed in the
 * extension point.
 * 
 * @since 3.1
 */
public class IntroLaunchBar implements IWindowTrim {

	private Composite container;

	protected ToolBarManager toolBarManager;

	protected int orientation;

	protected int location;

	protected String lastPageId;

	protected Action closeAction = null;

	private IntroLaunchBarElement element;

	protected boolean simple;

	private String presentationId;
	
	private IntroTheme theme;

	static final int[] TOP_LEFT_CORNER = new int[] { 0, 6, 1, 5, 1, 4, 4, 1, 5, 1, 6, 0 };

	static final int[] TOP_RIGHT_CORNER = new int[] { -6, 0, -5, 1, -4, 1, -1, 4, -1, 5, 0, 6 };

	static final int[] BOTTOM_LEFT_CORNER = new int[] { 0, -6, 1, -5, 1, -4, 4, -1, 5, -1, 6, 0 };

	static final int[] BOTTOM_RIGHT_CORNER = new int[] { -6, 0, -5, -1, -4, -1, -1, -4, -1, -5, 0, -6 };

	static final int[] SIMPLE_TOP_LEFT_CORNER = new int[] { 0, 2, 1, 1, 2, 0 };

	static final int[] SIMPLE_TOP_RIGHT_CORNER = new int[] { -2, 0, -1, 1, 0, 2 };

	static final int[] SIMPLE_BOTTOM_LEFT_CORNER = new int[] { 0, -2, 1, -1, 2, 0 };

	static final int[] SIMPLE_BOTTOM_RIGHT_CORNER = new int[] { -2, 0, -1, -1, 0, -2 };

	static final String S_STORED_LOCATION = "introLaunchBar.location"; //$NON-NLS-1$

	private final static String LAUNCH_COMMAND_BASE = "http://org.eclipse.ui.intro/showPage?id="; //$NON-NLS-1$

	private Color fg;

	private Color bg;

	class BarLayout extends Layout {

		protected Point computeSize(Composite composite, int wHint, int hHint, boolean changed) {
			boolean vertical = (orientation & SWT.VERTICAL) != 0;
			int marginWidth = vertical | isPlain() ? 1 : simple ? 3 : 7;
			int marginHeight = !vertical | isPlain() ? 1 : simple ? 3 : 7;
			int width = 0;
			int height = 0;

			Point tsize = toolBarManager.getControl().computeSize(SWT.DEFAULT, SWT.DEFAULT, changed);

			if (vertical) {
				width = tsize.x;
				height = tsize.y;
			} else {
				height = tsize.y;
				width = tsize.x;
			}
			if (vertical) {
				width += marginWidth;
				height += marginHeight + marginHeight;
			} else {
				width += marginWidth + marginWidth;
				height += marginHeight;
			}
			return new Point(width, height);
		}

		protected void layout(Composite composite, boolean changed) {
			boolean vertical = (orientation & SWT.VERTICAL) != 0;
			int marginWidth = vertical | isPlain() ? 1 : simple ? 4 : 7;
			int marginHeight = !vertical | isPlain() ? 1 : simple ? 4 : 7;

			Point tsize = toolBarManager.getControl().computeSize(SWT.DEFAULT, SWT.DEFAULT, changed);
			Rectangle carea = composite.getClientArea();
			int x = carea.x + (location == SWT.LEFT ? 0 : marginWidth);
			int y = carea.y + marginHeight;

			if (vertical) {
				toolBarManager.getControl().setBounds(x, y, carea.width - marginWidth, tsize.y);
			} else {
				toolBarManager.getControl().setBounds(x, y, tsize.x, carea.height - marginHeight);
			}
		}
	}

	public IntroLaunchBar(int orientation, String lastPageId, IntroLaunchBarElement element, IntroTheme theme) {
		this.orientation = orientation;
		this.location = element.getLocation();
		this.lastPageId = lastPageId;
		this.element = element;
		this.theme = theme;

		simple = true;
		presentationId = PlatformUI.getPreferenceStore().getString(
				IWorkbenchPreferenceConstants.PRESENTATION_FACTORY_ID);
		loadStoredLocation();
	}

	private void loadStoredLocation() {
		IDialogSettings settings = IntroPlugin.getDefault().getDialogSettings();
		try {
			int storedLocation = settings.getInt(S_STORED_LOCATION);
			if (storedLocation > 0)
				setLocation(storedLocation);
		} catch (NumberFormatException e) {
			// The stored value either does not exist or
			// is corrupted - just pick the default silently.
		}
	}

	private void storeLocation() {
		IDialogSettings settings = IntroPlugin.getDefault().getDialogSettings();
		settings.put(S_STORED_LOCATION, this.location);
	}

	/**
	 * This method now calls dock(location) and then adds itself to the window trim. This is to
	 * support the re-ordering of IWindowTrim lifecycle related to dock().
	 */
	public void createInActiveWindow() {
		IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();

		dock(location);

		ITrimManager trimManager = getTrimManager();
		trimManager.addTrim(location, this);
		window.getShell().layout();
	}

	/**
	 * Get the trim manager from the default workbench window. If the current
	 * workbench window is -not- the <code>WorkbenchWindow</code> then return null.
	 *  
	 * @return The trim manager for the current workbench window
	 */
	private ITrimManager getTrimManager() {
		IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
		if (window instanceof WorkbenchWindow)
			return ((WorkbenchWindow)window).getTrimManager();
		
		return null; // not using the default workbench window
	}
	
	protected boolean isPlain() {
		return !"org.eclipse.ui.presentations.default".equals(presentationId); //$NON-NLS-1$
	}

	public void createControl(Composite parent) {
		container = new Composite(parent, SWT.NULL);
		computeColors(parent.getDisplay());
		container.setLayout(new BarLayout());
		// boolean vertical = (orientation & SWT.VERTICAL) != 0;
		toolBarManager = new ToolBarManager(SWT.FLAT | orientation);


		fillToolBar();
		// coolBar = new CoolBar(container, SWT.NULL);
		// CoolItem coolItem = new CoolItem(coolBar, SWT.NULL);
		// toolBarManager.createControl(coolBar);
		toolBarManager.createControl(container);
		ToolBar toolBar = toolBarManager.getControl();

		// coolItem.setControl(toolBar);
		// Point toolBarSize = toolBar.computeSize(SWT.DEFAULT,
		// SWT.DEFAULT);
		// Set the preffered size to the size of the toolbar plus trim
		// Point preferredSize = coolItem.computeSize(toolBarSize.x,
		// toolBarSize.y);
		// coolItem.setPreferredSize(preferredSize);

		if (bg != null) {
			toolBar.setBackground(bg);
			// coolBar.setBackground(bg);
		}
		container.addPaintListener(new PaintListener() {

			public void paintControl(PaintEvent e) {
				onPaint(e);
			}
		});
		MenuManager manager = new MenuManager();
		IMenuListener listener = new IMenuListener() {

			public void menuAboutToShow(IMenuManager manager) {
				contextMenuAboutToShow(manager);
			}
		};
		manager.setRemoveAllWhenShown(true);
		manager.addMenuListener(listener);
		Menu contextMenu = manager.createContextMenu(toolBarManager.getControl());
		toolBarManager.getControl().setMenu(contextMenu);
		IntroPlugin.getDefault().setLaunchBar(this);
	}

	protected void startDragging(Point position, boolean usingKeyboard) {
		Rectangle dragRect = DragUtil.getDisplayBounds(getControl());
		startDrag(this, dragRect, position, usingKeyboard);
	}

	private void startDrag(Object toDrag, Rectangle dragRect, Point position, boolean usingKeyboard) {

		DragUtil.performDrag(toDrag, dragRect, position, !usingKeyboard);
	}

	protected void onPaint(PaintEvent e) {
		GC gc = e.gc;
		Color color = fg;
		if (color == null) {
			color = e.display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
		}
		gc.setForeground(color);
		if (bg != null)
			gc.setBackground(bg);
		if (isPlain()) {
			Point size = container.getSize();
			gc.fillRectangle(0, 0, size.x, size.y);
			gc.drawRectangle(0, 0, size.x - 1, size.y - 1);
		} else {
			switch (location) {
			case SWT.LEFT:
				paintLeft(gc);
				break;
			case SWT.RIGHT:
				paintRight(gc);
				break;
			case SWT.BOTTOM:
				paintBottom(gc);
				break;
			}
		}
	}

	private void paintLeft(GC gc) {
		int[] top = simple ? SIMPLE_TOP_RIGHT_CORNER : TOP_RIGHT_CORNER;
		int[] bot = simple ? SIMPLE_BOTTOM_RIGHT_CORNER : BOTTOM_RIGHT_CORNER;
		int[] shape = new int[top.length + bot.length + 4];
		int index = 0;
		Point size = container.getSize();
		int x = size.x - 1;
		int y = 0;
		index = fillShape(shape, top, index, x, y, false);
		y = size.y - 1;
		index = fillShape(shape, bot, index, x, y, true);
		shape[index++] = -1;
		shape[index++] = size.y - 1;
		shape[index++] = -1;
		shape[index++] = 0;
		gc.fillPolygon(shape);
		gc.drawPolygon(shape);
	}

	private void paintBottom(GC gc) {
		int[] left = simple ? SIMPLE_TOP_LEFT_CORNER : TOP_LEFT_CORNER;
		int[] right = simple ? SIMPLE_TOP_RIGHT_CORNER : TOP_RIGHT_CORNER;
		int[] shape = new int[left.length + right.length + 4];
		int index = 0;
		Point size = container.getSize();
		int x = 0;
		int y = 0;
		index = fillShape(shape, left, index, x, y, false);
		x = size.x - 1;
		index = fillShape(shape, right, index, x, y, false);
		shape[index++] = size.x - 1;
		shape[index++] = size.y;
		shape[index++] = 0;
		shape[index++] = size.y;
		gc.fillPolygon(shape);
		gc.drawPolygon(shape);
	}

	private void paintRight(GC gc) {
		int[] top = simple ? SIMPLE_TOP_LEFT_CORNER : TOP_LEFT_CORNER;
		int[] bot = simple ? SIMPLE_BOTTOM_LEFT_CORNER : BOTTOM_LEFT_CORNER;
		int[] shape = new int[top.length + bot.length + 4];
		int index = 0;
		Point size = container.getSize();
		int x = 0;
		int y = 0;
		index = fillShape(shape, top, index, x, y, false);
		shape[index++] = size.x;
		shape[index++] = 0;
		shape[index++] = size.x;
		shape[index++] = size.y - 1;
		x = 0;
		y = size.y - 1;
		fillShape(shape, bot, index, x, y, true);
		gc.fillPolygon(shape);
		gc.drawPolygon(shape);
	}


	private int fillShape(int[] shape, int[] points, int index, int x, int y, boolean reverse) {
		int fill = points.length;
		for (int i = 0; i < points.length / 2; i++) {
			if (!reverse) {
				shape[index++] = x + points[2 * i];
				shape[index++] = y + points[2 * i + 1];
			} else {
				shape[index + fill - 2 - 2 * i] = x + points[2 * i];
				shape[index + fill - 1 - 2 * i] = y + points[2 * i + 1];
			}
		}
		if (reverse) {
			index += fill;
		}
		return index;
	}

	private void computeColors(Display display) {
		if (element.getBackground() != null) {
			String value = resolveColor(element.getBackground());
			if (value!=null) {
				RGB r = SharedStyleManager.parseRGB(value);
				if (r != null)
					bg = new Color(display, r);
			}
		}
		if (element.getForeground() != null) {
			String value = resolveColor(element.getForeground());
			if (value!=null) {
				RGB r = SharedStyleManager.parseRGB(value);
				if (r != null)
					fg = new Color(display, r);
			}
		}
	}
	
	private String resolveColor(String value) {
		if (value.indexOf('$')== -1)
			return value;
		if (value.charAt(0)=='$' && value.charAt(value.length()-1)=='$' && theme!=null) {
			Map properties = theme.getProperties();
			if (properties!=null) {
				String key = value.substring(1, value.length()-1);
				return (String)properties.get(key);
			}
		}
		return value;
	}

	public Control getControl() {
		return container;
	}

	public void dispose() {
		if (container != null) {
			container.dispose();
		}
		if (toolBarManager != null) {
			toolBarManager.dispose();
			toolBarManager.removeAll();
		}

		toolBarManager = null;
		container = null;

		if (bg != null)
			bg.dispose();
		if (fg != null)
			fg.dispose();
	}

	private void fillToolBar() {
		Action action;

		closeAction = new Action("close") { //$NON-NLS-1$

			public void run() {
				closeLaunchBar(false);
			}
		};
		closeAction.setText(Messages.IntroLaunchBar_close_label);
		closeAction.setToolTipText(Messages.IntroLaunchBar_close_tooltip);
		/*
		 * closeAction.setImageDescriptor(ImageUtil
		 * .createImageDescriptor("full/elcl16/close_view.gif")); //$NON-NLS-1$
		 */

		action = new Action("restore") { //$NON-NLS-1$

			public void run() {
				openPage(lastPageId);
			}
		};
		action.setToolTipText(Messages.IntroLaunchBar_restore_tooltip);
		action.setImageDescriptor(ImageUtil.createImageDescriptor("full/etool16/restore_welcome.gif")); //$NON-NLS-1$
		// toolBarManager.add(closeAction);
		toolBarManager.add(action);
		toolBarManager.add(new Separator());
		if (element == null)
			return;
		IntroLaunchBarShortcut[] shortcuts = element.getShortcuts();
		for (int i = 0; i < shortcuts.length; i++) {
			IntroLaunchBarShortcut shortcut = shortcuts[i];
			addShortcut(shortcut, toolBarManager);
		}
	}

	private void addShortcut(final IntroLaunchBarShortcut shortcut, IToolBarManager toolBarManager) {
		Action action = new Action(shortcut.getToolTip()) {

			public void run() {
				executeShortcut(shortcut.getURL());
			}
		};
		action.setImageDescriptor(shortcut.getImageDescriptor());
		action.setToolTipText(shortcut.getToolTip());
		toolBarManager.add(action);
	}

	public void close() {
		closeLaunchBar(false);
	}

	protected IIntroPart closeLaunchBar(boolean restore) {

		IntroPlugin.getDefault().setLaunchBar(null);
		IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();

		// if we've already been removed, this won't hurt us
		getTrimManager().removeTrim(this);

		IIntroPart intro = null;
		if (restore) {
			intro = PlatformUI.getWorkbench().getIntroManager().showIntro(window, false);
			CustomizableIntroPart cpart = (CustomizableIntroPart) intro;
			Rectangle startBounds = Geometry.toDisplay(getControl().getParent(), getControl().getBounds());
			Rectangle endBounds = Geometry.toDisplay(cpart.getControl().getParent(), cpart.getControl()
					.getBounds());

			RectangleAnimation animation = new RectangleAnimation(window.getShell(), startBounds, endBounds);
			animation.schedule();
		}
		dispose();
		window.getShell().layout();
		return intro;
	}

	protected void executeShortcut(String url) {
		IIntroURL introURL = IntroURLFactory.createIntroURL(url);
		if (introURL != null) {
			IIntroPart intro = closeLaunchBar(true);
			if (intro == null)
				return;
			introURL.execute();
		}
	}

	protected void openPage(String id) {
		IIntroPart intro = closeLaunchBar(true);
		if (intro == null)
			return;
		StringBuffer url = new StringBuffer();
		url.append(LAUNCH_COMMAND_BASE);
		url.append(id);
		IIntroURL introURL = IntroURLFactory.createIntroURL(url.toString());
		if (introURL != null)
			introURL.execute();
	}

	protected void contextMenuAboutToShow(IMenuManager manager) {
		manager.add(closeAction);
	}

	public void dock(int side) {
		dispose();
		setLocation(side);
		storeLocation();
		IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
		createControl(window.getShell());
	}

	private void setLocation(int location) {
		this.orientation = (location == SWT.LEFT || location == SWT.RIGHT) ? SWT.VERTICAL : SWT.HORIZONTAL;
		this.location = location;
	}

	public int getValidSides() {
		return SWT.LEFT | SWT.RIGHT | SWT.BOTTOM;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.internal.IWindowTrim#getId()
	 */
	public String getId() {
		return "org.eclipse.ui.internal.intro.impl.presentations.IntroLaunchBar"; //$NON-NLS-1$
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.internal.IWindowTrim#getDisplayName()
	 */
	public String getDisplayName() {
		return WorkbenchMessages.TrimCommon_IntroBar_TrimName;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.internal.IWindowTrim#isCloseable()
	 */
	public boolean isCloseable() {
		return element.getClose();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.internal.IWindowTrim#handleClose()
	 */
	public void handleClose() {
		closeLaunchBar(false);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.IWindowTrim#getWidthHint()
	 */
	public int getWidthHint() {
		return SWT.DEFAULT;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.IWindowTrim#getHeightHint()
	 */
	public int getHeightHint() {
		return SWT.DEFAULT;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.IWindowTrim#isResizeable()
	 */
	public boolean isResizeable() {
		return false;
	}

}
