/*******************************************************************************
 * Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is 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:
 *     Exadel, Inc. and Red Hat, Inc. - initial API and implementation
 ******************************************************************************/ 
package org.jboss.tools.common.model.ui.widgets;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;

/**
 * @author AU
 */
public class ScrolledComposite extends Composite {
	
	private Composite viewport;
	private boolean fixedWidth;
	private boolean fixedHeight;
	private boolean inResize;
	private boolean grabHSpace = Boolean.TRUE.booleanValue();
	private boolean grabVSpace = Boolean.TRUE.booleanValue();

	public ScrolledComposite(Composite parent, int style) {
		super(parent, checkStyle(style));
		setBackground(parent.getBackground());
	
		ScrollBar hBar = getHorizontalBar ();
		if (hBar != null) {
			hBar.addListener (SWT.Selection, new Listener () {
				public void handleEvent (Event e) {
					hScroll();
				}
			});
		} else {
			this.fixedWidth = Boolean.TRUE.booleanValue();
		}
	
		ScrollBar vBar = getVerticalBar ();
		vBar.setIncrement(10);
		if (vBar != null) {
			vBar.addListener (SWT.Selection, new Listener () {
				public void handleEvent (Event e) {
					vScroll();
				}
			});
		} else {
			this.fixedHeight = Boolean.TRUE.booleanValue();
		}
	
		addListener (SWT.Resize,  new Listener () {
			public void handleEvent (Event e) {
				resize();
			}
		});
		
		Label label = new Label(this, SWT.NONE);
		label.setText("Test");
		
		viewport = new Viewport(this, SWT.NONE);
	}

	private static int checkStyle (int style) {
		int mask = SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
		return style & mask;
	}
	
	private void vScroll() {
		if (viewport == null) return;
		Point location = viewport.getLocation ();
		ScrollBar vBar = getVerticalBar ();
		int vSelection = vBar.getSelection ();
		viewport.setLocation (location.x, -vSelection);
	}

	private void hScroll() {
		if (viewport == null) return;
		Point location = viewport.getLocation ();
		ScrollBar hBar = getHorizontalBar ();
		int hSelection = hBar.getSelection ();
		viewport.setLocation (-hSelection, location.y);
	}

	private void resize() {
		if (inResize) return;
		inResize = Boolean.TRUE.booleanValue();
		layout();
		inResize = Boolean.FALSE.booleanValue();
	}
	
	public Composite getViewport() {
		return viewport;
	}

	public void setLayout(Layout layout) {
		if (viewport!=null) viewport.setLayout(layout);
	}

	private int lock = 0;
	public void layout(boolean changed) {
		checkWidget();
		if (viewport == null) return;
		if(lock > 0) return; //EFWPE-617
		++lock;
		try {
			doLayout();
		} finally {
			--lock;
		}
	}
	
	private void doLayout() {
		Point viewportSize = viewport.computeSize(SWT.DEFAULT, SWT.DEFAULT, Boolean.TRUE.booleanValue());
		Point viewportLocation = viewport.getLocation();
		Rectangle cr = new Rectangle(viewportLocation.x, viewportLocation.y, viewportSize.x, viewportSize.y);
		Rectangle hr = getClientArea();
		if (fixedWidth) {
			cr.width = hr.width;	
		} else if (grabHSpace) {
			cr.width = Math.max(cr.width, hr.width);	
		}
		if (fixedHeight) {
			cr.height = hr.height;	
		} else if (grabVSpace) {
			cr.height = Math.max(cr.height, hr.height);
		}
		layoutHBar(cr, hr);
		layoutVBar(cr, hr);
		viewport.setBounds(cr);
	}
	
	private void layoutHBar(Rectangle cr, Rectangle hr) {
		ScrollBar hBar = getHorizontalBar ();
		if(hBar == null) return;
		hBar.setMaximum (cr.width);
		hBar.setThumb(Math.min (cr.width, hr.width));
		int hs = hBar.getSelection ();
		if (hs >= cr.width - hr.width) {
			if (cr.width <= hr.width) {
				hs = 0;
				hBar.setSelection(0);
			}
			cr.x = -hs;
		}
		if(hBar.getThumb() >= hBar.getMaximum())
			hBar.setVisible(false);
		else 
			hBar.setVisible(true);
	}
	private void layoutVBar(Rectangle cr, Rectangle hr) {
		ScrollBar vBar = getVerticalBar();
		if(vBar == null) return;
		vBar.setMaximum(cr.height);
		vBar.setThumb(Math.min (cr.height, hr.height));
		int vs = vBar.getSelection ();
		if (vs >= cr.height - hr.height) {
			if (cr.height <= hr.height) {
				vs = 0;
				vBar.setSelection(0);
			}
			cr.y = -vs;
		}
		if(vBar.getThumb() >= vBar.getMaximum())
			vBar.setVisible(false);
		else 
			vBar.setVisible(true);
	}

	public Point computeSize (int wHint, int hHint, boolean changed) {
		checkWidget ();
		if (viewport == null) {
			return super.computeSize (wHint, hHint, changed);
		}
		if (wHint>16) wHint = wHint-16;
		Point size = viewport.computeSize (wHint, hHint, changed);
		Rectangle trim = computeTrim (0, 0, size.x, size.y);
		//ModelUIPlugin.log("+++ computeSize="+trim);
		return new Point (trim.width, trim.height);
	}
	
	public static void scrollToVisible(Control control, Rectangle r) {
		int y = r == null ? 0 : r.y;
		Rectangle pr = control.getBounds();
		y += pr.y;
		Composite parent = control.getParent();
		while(parent != null && !(parent instanceof Viewport)) {
			pr = parent.getBounds();
			y += pr.y;
			parent = parent.getParent();
		}
		if(parent == null) return;
		Viewport v = (Viewport)parent;
		pr = v.getBounds();
		ScrolledComposite sc = (ScrolledComposite)v.getParent();
		int availableH = sc.getBounds().height;
		int viewportH = pr.height;
		if(viewportH <= availableH) return;
		ScrollBar bar = sc.getVerticalBar();
		int dh = r == null ? 10 : r.height;
		int up = dh + 2 - y;
		int down = availableH - dh - 2 - dh - y;
		if(up > down) {
			up = (up + down) / 2 - 5;
			down = up + 10;
		}
		int delta = 0;
		if(pr.y > down) {
			delta = down - pr.y;
		} else if(pr.y < up) {
			delta = up - pr.y;
		}
		int sDelta = (bar.getMaximum() - bar.getMinimum() - bar.getThumb()) * (-delta) / (viewportH - availableH);
		bar.setSelection(bar.getSelection() +  sDelta);
		pr.y += delta;
		v.setBounds(pr);
		v.layout(true);
	}
}
