/*******************************************************************************
 * Copyright (c) 2000, 2016 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.help.ui.internal;

import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.ACC;
import org.eclipse.swt.accessibility.Accessible;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleControlAdapter;
import org.eclipse.swt.accessibility.AccessibleControlEvent;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.TypedListener;

/**
 *
 * A canvas holding a hyperlink label. Need this to deal with focus selection.
 */
public class HyperlinkLabel extends Canvas {
	Label label;
	boolean hasFocus;

	/**
	 * Constructor for Hyperlink.
	 *
	 * @param parent
	 * @param style
	 */
	public HyperlinkLabel(Composite parent, int style) {
		super(parent, style);

		GridLayout layout = new GridLayout();
		layout.marginHeight = 3;
		layout.marginWidth = 2;
		layout.numColumns = 1;
		this.setLayout(layout);

		this.label = new Label(this, style);

		addPaintListener(e -> paint(e));

		addKeyListener(new KeyAdapter() {

			@Override
			public void keyPressed(KeyEvent e) {
				if (e.character == '\r') {
					// Activation
					notifyListeners(SWT.DefaultSelection);
				}
			}
		});

		addListener(SWT.Traverse, e -> {
			switch (e.detail) {
			// let arrows move focus
			case SWT.TRAVERSE_ARROW_NEXT:
				e.detail = SWT.TRAVERSE_TAB_NEXT;
				break;
			case SWT.TRAVERSE_ARROW_PREVIOUS:
				e.detail = SWT.TRAVERSE_TAB_PREVIOUS;
				break;

			case SWT.TRAVERSE_PAGE_NEXT:
			case SWT.TRAVERSE_PAGE_PREVIOUS:
			case SWT.TRAVERSE_RETURN:
				e.doit = false;
				return;
			}
			e.doit = true;
		});

		addFocusListener(new FocusListener() {

			@Override
			public void focusGained(FocusEvent e) {
				if (!hasFocus) {
					hasFocus = true;
					notifyListeners(SWT.Selection);
					redraw();
				}
			}

			@Override
			public void focusLost(FocusEvent e) {
				if (hasFocus) {
					hasFocus = false;
					notifyListeners(SWT.Selection);
					redraw();
				}
			}
		});

		GridData data = new GridData();
		data.horizontalAlignment = GridData.HORIZONTAL_ALIGN_BEGINNING;
		data.verticalAlignment = GridData.VERTICAL_ALIGN_BEGINNING;
		label.setLayoutData(data);

		initAccessibleLink();
		initAccessibleLabel();
	}

	public void setText(String text) {
		label.setText(text);
	}

	public boolean getSelection() {
		return hasFocus;
	}

	public Label getLabel() {
		return label;
	}

	void notifyListeners(int eventType) {
		Event event = new Event();
		event.type = eventType;
		event.widget = this;
		notifyListeners(eventType, event);
	}

	protected void paint(PaintEvent e) {
		if (hasFocus) {
			GC gc = e.gc;
			Point size = getSize();
			gc.setForeground(getForeground());
			gc.drawFocus(0, 0, size.x, size.y);
		}
	}

	public void addSelectionListener(SelectionListener listener) {
		checkWidget();
		if (listener == null)
			return;
		TypedListener typedListener = new TypedListener(listener);
		addListener(SWT.Selection, typedListener);
		addListener(SWT.DefaultSelection, typedListener);
	}

	public void removeSelectionListener(SelectionListener listener) {
		checkWidget();
		if (listener == null)
			return;
		removeListener(SWT.Selection, listener);
		removeListener(SWT.DefaultSelection, listener);
	}

	@Override
	public Point computeSize(int wHint, int hHint, boolean changed) {
		int innerWidth = wHint;
		if (innerWidth != SWT.DEFAULT)
			innerWidth -= 4;
		Point textSize = label.computeSize(wHint, hHint, changed);//computeTextSize(innerWidth,
		// hHint);
		int textWidth = textSize.x + 4;
		int textHeight = textSize.y + 6;
		return new Point(textWidth, textHeight);
	}

	@Override
	public void addMouseListener(MouseListener l) {
		//super.addMouseListener(l);
		label.addMouseListener(l);
	}

	@Override
	public void addMouseTrackListener(MouseTrackListener l) {
		//super.addMouseTrackListener(l);
		label.addMouseTrackListener(l);
	}

	@Override
	public void addPaintListener(PaintListener l) {
		super.addPaintListener(l);
		label.addPaintListener(l);
	}

	@Override
	public void addListener(int e, Listener l) {
		super.addListener(e, l);
		//label.addListener(e, l);
	}

	@Override
	public void setBackground(Color c) {
		super.setBackground(c);
		label.setBackground(c);
	}

	@Override
	public void setForeground(Color c) {
		super.setForeground(c);
		label.setForeground(c);
	}

	@Override
	public void setCursor(Cursor c) {
		super.setCursor(c);
		label.setCursor(c);
	}

	private void initAccessibleLink() {
		Accessible accessible = this.getAccessible();
		accessible.addAccessibleListener(new AccessibleAdapter() {

			@Override
			public void getName(AccessibleEvent e) {
				e.result = label.getText();
			}

			@Override
			public void getHelp(AccessibleEvent e) {
				e.result = label.getToolTipText();
			}
		});

		accessible.addAccessibleControlListener(new AccessibleControlAdapter() {

			@Override
			public void getRole(AccessibleControlEvent e) {
				e.detail = ACC.ROLE_LINK;
			}

			@Override
			public void getState(AccessibleControlEvent e) {
				if (hasFocus)
					e.detail = ACC.STATE_FOCUSABLE | ACC.STATE_LINKED
							| ACC.STATE_FOCUSED;
				else
					e.detail = ACC.STATE_FOCUSABLE | ACC.STATE_LINKED;

			}
		});
	}
	private void initAccessibleLabel() {
		Accessible accessible = label.getAccessible();
		accessible.addAccessibleControlListener(new AccessibleControlAdapter() {

			@Override
			public void getState(AccessibleControlEvent e) {
				if (hasFocus)
					e.detail = ACC.STATE_READONLY | ACC.STATE_FOCUSABLE
							| ACC.STATE_SELECTABLE | ACC.STATE_LINKED
							| ACC.STATE_FOCUSED;
				else
					e.detail = ACC.STATE_READONLY | ACC.STATE_FOCUSABLE
							| ACC.STATE_SELECTABLE | ACC.STATE_LINKED;

			}
		});
	}
}
