/*
 * Copyright (c) 2006-2008 Maskat Project.
 *
 * 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:
 *     Maskat Project - initial API and implementation
 */
package org.maskat.ui.views.properties.tabbed.masterdetails;

import java.util.Iterator;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.IFormPart;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.forms.widgets.TableWrapData;
import org.eclipse.ui.forms.widgets.TableWrapLayout;
import org.eclipse.ui.part.FileEditorInput;

import org.maskat.core.event.Event;
import org.maskat.core.event.Header;
import org.maskat.core.event.Param;
import org.maskat.core.event.Result;
import org.maskat.core.event.Target;
import org.maskat.ui.MaskatNature;
import org.maskat.ui.Messages;
import org.maskat.ui.views.properties.tabbed.beanwrapper.EventTreeNode;
import org.maskat.ui.views.properties.tabbed.treenodewrapper.ITreeNode;

public class EventHandlerDetailsPage extends AbstractMaskatDetailsPage {

	private static final String SECTION_TEXT = Messages
			.getString("eventHandlerDetails.sectionText"); //$NON-NLS-1$

	private static final String DISABLED_DESCRIPTION = Messages
			.getString("eventHandlerDetails.disabledDescription"); //$NON-NLS-1$

	private static final String REMOTE_DESCRIPTION = Messages
			.getString("eventHandlerDetails.remoteDescription"); //$NON-NLS-1$

	private static final String LOCAL_DESCRIPTION = Messages
			.getString("eventHandlerDetails.localDescription"); //$NON-NLS-1$

	private static final String COMMUNICATION_TEXT = Messages
			.getString("eventHandlerDetails.communicationText"); //$NON-NLS-1$

	private static final String COMMUNICATION_DESCRIPTION = Messages
			.getString("eventHandlerDetails.communicationDescription"); //$NON-NLS-1$

	private static final String DISABLE_WARNING = Messages
			.getString("eventHandlerDetails.disableWarning"); //$NON-NLS-1$

	private static final String LOCAL_WARNING = Messages
			.getString("eventHandlerDetails.localWarning"); //$NON-NLS-1$

	private static final String WARNING_TITLE = Messages
			.getString("eventHandlerDetails.warningTitle"); //$NON-NLS-1$

	private Section eventSection;

	private Combo eventTypeCombo;

	private Event event;
	
	private Section communicationSection;

	private Text remoteURLText;

	private Button asyncButton;

	private Button syncButton;

	private String lastStateEventType = null;
	
	private Hyperlink openEventLink;

	private boolean lastStateEventIsEnabled = false;

	public EventHandlerDetailsPage(TreeViewer masterBlockTreeViewer) {
		this.masterBlockTreeViewer = masterBlockTreeViewer;
	}

	public void createContents(Composite parent) {

		TableWrapLayout layout = new TableWrapLayout();
		layout.topMargin = 0;
		layout.leftMargin = 5;
		layout.rightMargin = 5;
		layout.bottomMargin = 5;
		parent.setLayout(layout);

		FormToolkit toolkit = managedForm.getToolkit();

		eventSection = toolkit.createSection(parent,
				ExpandableComposite.TITLE_BAR | Section.DESCRIPTION);
		eventSection.setText(SECTION_TEXT);
		eventSection.setDescription(DISABLED_DESCRIPTION);
		TableWrapData tableWrapData = new TableWrapData(TableWrapData.FILL,
				TableWrapData.TOP);
		tableWrapData.grabHorizontal = true;
		eventSection.setLayoutData(tableWrapData);

		communicationSection = toolkit.createSection(parent,
				ExpandableComposite.TITLE_BAR | Section.DESCRIPTION);
		communicationSection.setText(COMMUNICATION_TEXT);
		communicationSection.setDescription(COMMUNICATION_DESCRIPTION);
		tableWrapData = new TableWrapData(TableWrapData.FILL, TableWrapData.TOP);
		tableWrapData.grabHorizontal = true;
		eventSection.setLayoutData(tableWrapData);

		Composite eventComposite = toolkit.createComposite(eventSection);
		eventComposite.setLayout(new GridLayout(4, false));

		toolkit.createLabel(eventComposite, Messages
				.getString("eventHandlerDetails.eventLabel")); //$NON-NLS-1$
		toolkit.createLabel(eventComposite, ":");
		eventTypeCombo = new Combo(eventComposite, SWT.DROP_DOWN
				| SWT.READ_ONLY);
		eventTypeCombo.add(Messages
				.getString("eventHandlerDetails.eventType.disabled")); //$NON-NLS-1$;
		eventTypeCombo.add(Messages
				.getString("eventHandlerDetails.eventType.remote")); //$NON-NLS-1$;
		eventTypeCombo.add(Messages
				.getString("eventHandlerDetails.eventType.local")); //$NON-NLS-1$;

		openEventLink = toolkit.createHyperlink(eventComposite,
				Messages.getString("eventHandlerDetails.openHandlerImpl"), SWT.NONE); //$NON-NLS-1$;
		
		openEventLink.addHyperlinkListener(new HyperlinkAdapter() {
			public void linkActivated(HyperlinkEvent ev) {
				// TODO: もっと良いNatureの取得方法を検証する。
				IEditorPart editor = PlatformUI.getWorkbench().
				    getActiveWorkbenchWindow().getActivePage().getActiveEditor();
			
				IFile file = ((FileEditorInput) editor.getEditorInput()).getFile();
				MaskatNature nature = MaskatNature.getNature(file.getProject());

				try {
					nature.openEventHandlerImpl(event);
				} catch (CoreException e) {
					e.printStackTrace();
				}
			}
		});		
		
		Composite communicationComposite = toolkit
				.createComposite(communicationSection);

		communicationComposite.setLayout(new GridLayout(4, false));

		toolkit.createLabel(communicationComposite, Messages
				.getString("eventHandlerDetails.remoteUrlLabel")); //$NON-NLS-1$;
		toolkit.createLabel(communicationComposite, ":");
		remoteURLText = toolkit.createText(communicationComposite, "",
				SWT.SINGLE | SWT.BORDER);
		GridData gridData = new GridData(SWT.FILL, 0, true, false);
		gridData.horizontalSpan = 2;
		remoteURLText.setLayoutData(gridData);

		toolkit.createLabel(communicationComposite, Messages
				.getString("eventHandlerDetails.communicationTypeLabel")); //$NON-NLS-1$;
		toolkit.createLabel(communicationComposite, ":");
		asyncButton = toolkit.createButton(communicationComposite, Messages
				.getString("eventHandlerDetails.asyncButtonLabel"), SWT.RADIO); //$NON-NLS-1$;
		syncButton = toolkit.createButton(communicationComposite, Messages
				.getString("eventHandlerDetails.syncButtonLabel"), SWT.RADIO); //$NON-NLS-1$;

		eventTypeCombo.addSelectionListener(new SelectionAdapter() {

			public void widgetSelected(SelectionEvent e) {
				updateEventType(eventTypeCombo.getSelectionIndex());

				super.widgetSelected(e);
			}
		});

		remoteURLText.addKeyListener(new KeyAdapter() {

			public void keyReleased(KeyEvent e) {
				if (event.getRemoteUrl() == null
						|| !event.getRemoteUrl()
								.equals(remoteURLText.getText())) {
					EventPropertiesMasterDetailsBlock.setEventProperty(masterBlockTreeViewer,
							EventTreeNode.class, event, "remoteUrl",
							remoteURLText.getText());
				}
			}
		});

		remoteURLText.addFocusListener(new FocusAdapter() {

			public void focusLost(FocusEvent e) {
				if (event.getRemoteUrl() == null
						|| !event.getRemoteUrl()
								.equals(remoteURLText.getText())) {
					EventPropertiesMasterDetailsBlock.setEventProperty(masterBlockTreeViewer,
							EventTreeNode.class, event, "remoteUrl",
							remoteURLText.getText());
				}
			}
		});

		asyncButton.addSelectionListener(new SelectionAdapter() {

			public void widgetSelected(SelectionEvent e) {
				updateCommunicationType(asyncButton.getSelection());
			}
		});

		eventSection.setClient(eventComposite);

		communicationSection.setClient(communicationComposite);
	}

	protected void initializeInterface() {
		lastStateEventIsEnabled = event.isEnable();

		lastStateEventType = event.getType();

		if (!event.isEnable()) {
			eventTypeCombo.select(0);
			eventSection.setDescription(DISABLED_DESCRIPTION);

			lastStateEventType = null;

			communicationSection.setVisible(false);
			openEventLink.setVisible(false);
			
		} else if (event.getType().equals("remote")) {

			eventTypeCombo.select(1);
			eventSection.setDescription(REMOTE_DESCRIPTION);

			if (event.getRemoteUrl() != null) {

				remoteURLText.setText(event.getRemoteUrl());
			} else {
				remoteURLText.setText("");
			}
			asyncButton.setSelection(event.isAsync());

			syncButton.setSelection(!event.isAsync());

			communicationSection.setVisible(true);
			openEventLink.setVisible(true);
		} else { // event type == local

			eventTypeCombo.select(2);
			eventSection.setDescription(LOCAL_DESCRIPTION);

			communicationSection.setVisible(false);
			openEventLink.setVisible(false);
		}

		setStaleFlag(false);
	}

	public void selectionChanged(IFormPart part, ISelection selection) {
		if (isDirty()) {
			update();
		}
	}

	public void update() {
		if (lastStateEventIsEnabled != event.isEnable()) {
			lastStateEventIsEnabled = event.isEnable();
		}

		// needed for the warning message. should be changed (deleted)
		if (event.getType() != null) {
			if (!event.getType().equals(lastStateEventType)) {
				lastStateEventType = event.getType();
			}
		}

		if (!event.isEnable()) {
			eventTypeCombo.select(0);
			eventSection.setDescription(DISABLED_DESCRIPTION);

			communicationSection.setVisible(false);

			lastStateEventType = null;

		} else if (event.getType().equals("remote")) {

			eventTypeCombo.select(1);
			eventSection.setDescription(REMOTE_DESCRIPTION);

			if (event.getRemoteUrl() != null) {
				remoteURLText.setText(event.getRemoteUrl());
			} else {
				remoteURLText.setText("");
			}

			if (event.isAsync()) {
				asyncButton.setSelection(true);
			} else {
				syncButton.setSelection(true);
			}

			communicationSection.setVisible(true);
		} else {

			eventTypeCombo.select(2);
			eventSection.setDescription(LOCAL_DESCRIPTION);

			communicationSection.setVisible(false);
		}

		setDirtyFlag(false);
	}

	public void setEvent(Event event) {
		this.event = event;
	}
	
	protected void updateEventType(int selectionIndex) {
		boolean changeEventType = false;

		if (selectionIndex == 0) {
			if (lastStateEventIsEnabled) {
				changeEventType = showWarningDialog(EventHandlerDetailsPage.DISABLE_WARNING);

				if (changeEventType) {
					EventPropertiesMasterDetailsBlock.setEventProperty(masterBlockTreeViewer,
							EventTreeNode.class, event, "enable", "false");
					cleanEvent();
				}
				setDirtyFlag(true);
			} else {
				EventPropertiesMasterDetailsBlock.setEventProperty(masterBlockTreeViewer,
						EventTreeNode.class, event, "enable", "false");
				setDirtyFlag(true);
				openEventLink.setVisible(false);
			}
		} else if (selectionIndex == 1) {
			if (lastStateEventType != null) {
				if (lastStateEventType.equals("local")) {
					makeEventRemote(); // does nothing
				}
			}
			EventPropertiesMasterDetailsBlock.setEventProperty(masterBlockTreeViewer,
					EventTreeNode.class, event, "type", "remote");
			EventPropertiesMasterDetailsBlock.setEventProperty(masterBlockTreeViewer,
					EventTreeNode.class, event, "enable", "true");

			setDirtyFlag(true);
			openEventLink.setVisible(true);
			
		} else if (selectionIndex == 2) {
			if (lastStateEventType != null) {
				if (lastStateEventType.equals("remote")) {
					changeEventType = showWarningDialog(EventHandlerDetailsPage.LOCAL_WARNING);

					if (changeEventType == true) {
						EventPropertiesMasterDetailsBlock.setEventProperty(masterBlockTreeViewer,
								EventTreeNode.class, event, "type", "local");
						makeEventLocal();
					} else {
					}
					setDirtyFlag(true);
				}
			} else {
				EventPropertiesMasterDetailsBlock.setEventProperty(masterBlockTreeViewer,
						EventTreeNode.class, event, "type", "local");
				EventPropertiesMasterDetailsBlock.setEventProperty(masterBlockTreeViewer,
						EventTreeNode.class, event, "enable", "true");
				setDirtyFlag(true);
			}
			openEventLink.setVisible(false);
		}

		if (isDirty()) {
			// update tree
			updateTreeViewer();

			// update details part
			update();
		}
	}

	private boolean showWarningDialog(String message) {

		return MessageDialog.openConfirm(Display.getCurrent().getActiveShell(),
				EventHandlerDetailsPage.WARNING_TITLE, message);
	}

	private void updateTreeViewer() {
		masterBlockTreeViewer.refresh();
		masterBlockTreeViewer.expandToLevel(2);
		ITreeNode treeNode = EventPropertiesMasterDetailsBlock.getTreeNode(
				event, EventTreeNode.class);
		if (treeNode != null) {
			masterBlockTreeViewer.setSelection(
					new StructuredSelection(treeNode), true);
		}
	}

	private void updateCommunicationType(boolean selection) {
		String select = selection ? "true" : "false";
		EventPropertiesMasterDetailsBlock.setEventProperty(masterBlockTreeViewer,EventTreeNode.class,
				event, "async", select);

		setDirtyFlag(true);

		update();
	}

	private void makeEventRemote() {
		Iterator it = event.getTypedChildren(Result.class);
		if (it != null && it.hasNext()) {
			Result result = (Result)it.next();
			Iterator it2 = result.getTypedChildren(Target.class);
			while (it2 != null && it2.hasNext()) {
				Target target = (Target)it2.next();
				//if the event's type is local , the target's type will be processed 
				//as local by MaskatFW event it's set to 'remote'. 
				//so if the event type is changed from local to remote,
				//set the Target's type to local explicitly.
				target.setTeleType(target.getWorkType());
				target.setWorkType(null);
				target.setType("local");
			}
		}
	}

	private void makeEventLocal() {
		event.setEnable(true);
		event.setType("local");

		// delete remote event only information
		event.setBefore(null);
		event.setAfter(null);

		event.setRemoteUrl(null);

		//set to default value so that it will not be saved
		event.setTimeout(Event.TIMEOUT_DEFAULT);
		event.setAsync(Event.ASYNC_DEFAULT);
		event.setOnTimeoutError(null);

		if (event.getTypedChildren(Param.class) != null) {
			// delete param
			Param param = event.getParam();
	
			param.setEnable(false);
	
			param.removeAllChildren();
	
			param.setNs(null);
			param.setRootNode(null);
		}

		if (event.getTypedChildren(Result.class) != null) {
			// delete remote data binding
			Result result = event.getResult();
			result.setEnable(false);
	
			Iterator iterator = result.getTargetsIt();
			while (iterator != null && iterator.hasNext()) {
				Target target = (Target) iterator.next();
	
				if (target.getType().equals("remote")) {
					result.removeChild(target);
					iterator = result.getTargetsIt();
				} else {
					target.setWorkType(target.getTeleType());
					target.setTeleType(null);
				}
			}
	
			result.setRootNode(null);
			result.setOnErrorTele(null);
		}

		event.removeAllByType(Header.class);
	}

	private void cleanEvent() {
		event.removeAllChildren();

		event.setRemoteUrl(null);

		event.setStart(null);
		event.setBefore(null);
		event.setAfter(null);
		event.setFinish(null);

		event.setTimeout(Event.TIMEOUT_DEFAULT);
		event.setOnTimeoutError(null);

		event.setAfter(null);

		event.setEnable(false);
	}
}
