// based on GEF ShapesEditor example
/*******************************************************************************
 * Copyright (c) 2004, 2005 Elias Volanakis 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:
 *    Elias Volanakis - initial API and implementation
 *******************************************************************************/
package jp.hasc.hasctool.ui.bdeditor2.model.commands;

import java.beans.PropertyDescriptor;

import jp.hasc.hasctool.core.blockdiagram.model.BlockDiagram;
import jp.hasc.hasctool.core.blockdiagram.model.Connection;
import jp.hasc.hasctool.core.blockdiagram.model.PortReference;
import jp.hasc.hasctool.core.messaging.MessageConnector;
import jp.hasc.hasctool.core.messaging.MessageProcessor;
import jp.hasc.hasctool.ui.bdeditor2.Messages;
import jp.hasc.hasctool.ui.bdeditor2.model.BlockDiagramElement;
import jp.hasc.hasctool.ui.bdeditor2.model.BlockElement;
import jp.hasc.hasctool.ui.bdeditor2.model.ConnectionElement;
import jp.hasc.hasctool.ui.util.UIUtil;

import org.apache.commons.beanutils.PropertyUtils;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.commands.Command;
import org.eclipse.swt.widgets.Shell;

/**
 * @author iwasaki
 */
public class ConnectionCreateCommand extends Command {
	/** logger for this class */
	private final static org.apache.commons.logging.Log LOG = org.apache.commons.logging.LogFactory
			.getLog(ConnectionCreateCommand.class);
	
	/** The connection instance. */
	private ConnectionElement connection;

	/** Start endpoint for the connection. */
	private final BlockElement source;
	/** Target endpoint for the connection. */
	private BlockElement target;
	
	private BlockDiagramElement diagram_;
	private EditPolicy editPolicy_;

	/**
	 * Instantiate a command that can create a connection between two shapes.
	 * 
	 * @param source
	 *            the source endpoint (a non-null Shape instance)
	 * @param lineStyle
	 *            the desired line style. See Connection#setLineStyle(int) for
	 *            details
	 * @throws IllegalArgumentException
	 *             if source is null
	 * @see ConnectionElement#setLineStyle(int)
	 */
	public ConnectionCreateCommand(EditPolicy policy, BlockDiagramElement diagram, BlockElement source) {
		editPolicy_=policy;
		diagram_=diagram;
		if (source == null) {
			throw new IllegalArgumentException();
		}
		setLabel("connection creation");
		this.source = source;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#canExecute()
	 */
	public boolean canExecute() {
		// disallow source -> source connections
		if (source.equals(target)) {
			return false;
		}
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#execute()
	 */
	public void execute() {
		// create a new connection between source and target
		connection = new ConnectionElement();
		connection.init(diagram_, source, target);
		checkConection();
	}
	
	public void checkConection() {
		
		BlockElement blk0 = connection.getSource();
		BlockElement blk1 = connection.getTarget();
		LOG.debug(blk0.getBeanBlock().getName()+" to "+blk1.getBeanBlock().getName()); //$NON-NLS-1$
		PortReference pr0 = connection.getConnection().getOutputPortReference();
		PortReference pr1 = connection.getConnection().getInputPortReference();
		String pname0=pr0.getPortName();
		String pname1=pr1.getPortName();
		StringBuilder msgs=new StringBuilder();
		try{
			Class<?> cls0=Class.forName(blk0.getBeanBlock().getRuntimeClassName());
			PropertyDescriptor desc0 = PropertyUtils.getPropertyDescriptor(cls0.newInstance(), pname0);
			if (desc0==null || desc0.getPropertyType()==null || !MessageConnector.class.isAssignableFrom(desc0.getPropertyType())) {
				pname0=""; //$NON-NLS-1$
				msgs.append(String.format(Messages.ConnectionCreateCommand_NoDefaultOutputPort,
						blk0.getBeanBlock().getName()));
				msgs.append("\n"); //$NON-NLS-1$
			}
			
			
			Class<?> cls1=Class.forName(blk1.getBeanBlock().getRuntimeClassName());
			PropertyDescriptor desc1 = PropertyUtils.getPropertyDescriptor(cls1.newInstance(), pname1);
			if (desc1==null || desc1.getPropertyType()==null || !MessageProcessor.class.isAssignableFrom(desc1.getPropertyType())) {
				pname1=""; //$NON-NLS-1$
				msgs.append(String.format(Messages.ConnectionCreateCommand_NoDefaultInputPort,
						blk1.getBeanBlock().getName()));
				msgs.append("\n"); //$NON-NLS-1$
			}
			
		}catch(Exception ex) {
			msgs.append(ex.toString());
			msgs.append("\n"); //$NON-NLS-1$
		}
		
		BlockDiagram bd = diagram_.getBlockDiagram();
		
		// 同一ポートへの複数入力時に警告
		for(Connection c :bd.getConnections()) {
			if (c==connection.getConnection()) continue;
			if (c.getInputPortReference().equals(pr1)) {
				msgs.append(String.format(Messages.ConnectionCreateCommand_DuplicatedInputPortConnections,
						pr1.getBlockName(), pr1.getPortName()));
				msgs.append("\n"); //$NON-NLS-1$
				break;
			}
		}
		
		// show message
		if (msgs.length()!=0) {
			LOG.info(msgs);
			EditPartViewer viewer = editPolicy_.getHost().getRoot().getViewer();
			Shell shell = viewer.getControl().getShell();
			UIUtil.showMessageDialog(shell, msgs.toString());
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#redo()
	 */
	public void redo() {
		connection.reconnect();
	}

	/**
	 * Set the target endpoint for the connection.
	 * 
	 * @param target
	 *            that target endpoint (a non-null Shape instance)
	 * @throws IllegalArgumentException
	 *             if target is null
	 */
	public void setTarget(BlockElement target) {
		if (target == null) {
			throw new IllegalArgumentException();
		}
		this.target = target;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#undo()
	 */
	public void undo() {
		connection.disconnect();
	}
}
