// 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;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;

import jp.hasc.hasctool.core.blockdiagram.model.AbstractBlock;
import jp.hasc.hasctool.core.blockdiagram.model.BeanBlock;
import jp.hasc.hasctool.core.blockdiagram.model.BlockDiagram;
import jp.hasc.hasctool.core.blockdiagram.model.Comment;
import jp.hasc.hasctool.core.blockdiagram.model.Connection;
import jp.hasc.hasctool.core.blockdiagram.model.Point;
import jp.hasc.hasctool.core.blockdiagram.model.Shape;
import jp.hasc.hasctool.ui.bdeditor2.BlockDiagramEditorUtil;

/**
 * @author iwasaki
 */
public class BlockDiagramElement extends ModelElement {

	/** Property ID to use when a child is added to this diagram. */
	public static final String SHAPE_ADDED_PROP = "ShapesDiagram.ShapeAdded";
	/** Property ID to use when a child is removed from this diagram. */
	public static final String SHAPE_REMOVED_PROP = "ShapesDiagram.ShapeRemoved";
	
	public static final String CONNECTION_ADDED_PROP = "ShapesDiagram.ConnectionAdded";
	public static final String CONNECTION_REMOVED_PROP = "ShapesDiagram.ConnectionRemoved";
	
	private static final long serialVersionUID = 1;
	
	public static final int GRID_SIZE = 8;
	
	private BlockDiagram blockDiagram_;
	private HashMap<String, BlockElement> blockObjMap_=new HashMap<String, BlockElement>();
	private HashSet<ConnectionElement> connections_=new HashSet<ConnectionElement>();
	private HashSet<ShapeElement> shapes_=new HashSet<ShapeElement>();
	
	public BlockDiagramElement() {
		this(new BlockDiagram());
	}
	
	// 1以上なら、BlockDiagramを更新しないモード
	private int surpressUpdatingBlockDiagram_=0;
	private boolean isSupressUpdatingBlockDiagram() { return surpressUpdatingBlockDiagram_!=0; }
	
	public BlockDiagramElement(BlockDiagram diagram) {
		blockDiagram_=diagram;
		//
		
		// BlockDdiagramを読み込む
		++surpressUpdatingBlockDiagram_;
		try{
			// blocks
			int i=0;
			final int gw=128, gh=64, gn=4;
			for(AbstractBlock b: blockDiagram_.getBlocks()) {
				if (b instanceof BeanBlock) {
					BeanBlock bb = (BeanBlock) b;
					if (bb.getViewPosition()==null) {
						bb.setViewPosition(new Point(gw/2+gw*(i%gn), gw/2+gh*(i/gn)));
					}
					
					BlockElement be = new BlockElement();
					be.init(this, bb);
					blockObjMap_.put(be.getBeanBlock().getName(), be);
				}
				++i;
			}
			
			// connections
			for(Connection c: blockDiagram_.getConnections()) {
				ConnectionElement ce=new ConnectionElement();
				if (ce.init(this, c)) connections_.add(ce);
			}
			
			// shapes
			for(Shape s: blockDiagram_.getShapes()) {
				ShapeElement ce=ShapeElement.newInstance(this,s);
				shapes_.add(ce);
			}
			
			// BDEditor1の図のCommentを、BDEditor2用のCommentに変換
			if (blockDiagram_.getComment()!=null && !blockDiagram_.getComment().isEmpty()) {
				Comment c=new Comment(blockDiagram_.getComment());
				c.setViewPosition(new Point(gw*(gn+1)+16, 16));
				c.setViewSize(new Point(200,200));
				blockDiagram_.addShape(c);
				ShapeElement ce=ShapeElement.newInstance(this,c);
				shapes_.add(ce);
				blockDiagram_.setComment(null);
			}
		}finally{
			--surpressUpdatingBlockDiagram_;
		}
	}
	
	/**
	 * Add a shape to this diagram.
	 * 
	 * @param s
	 *            a non-null shape instance
	 * @return true, if the shape was added, false otherwise
	 */
	public boolean addBlock(BlockElement be) {
		if (!isSupressUpdatingBlockDiagram()) blockDiagram_.addBlock(be.getBeanBlock());
		blockObjMap_.put(be.getBeanBlock().getName(), be);
		firePropertyChange(SHAPE_ADDED_PROP, null, be);
		return true;
	}

	/**
	 * Return a List of Shapes in this diagram. The returned List should not be
	 * modified.
	 */
	public Collection<BlockElement> getBlocks() {
		return blockObjMap_.values();
	}

	/**
	 * Remove a shape from this diagram.
	 * 
	 * @param s
	 *            a non-null shape instance;
	 * @return true, if the shape was removed, false otherwise
	 */
	public boolean removeBlock(BlockElement be) {
		BeanBlock bb = be.getBeanBlock();
		BlockElement old = blockObjMap_.remove(bb.getName());
		if (old==null) return false;
		if (!isSupressUpdatingBlockDiagram()) {
			BlockDiagramEditorUtil.removeElement(blockDiagram_.getBlocks(), bb, true);
		}
		firePropertyChange(SHAPE_REMOVED_PROP, null, be);
		return true;
	}

	public Collection<ConnectionElement> getConnections() {
		return connections_;
	}
	
	public boolean addConnection(ConnectionElement ce) {
		Connection c = ce.getConnection();
		if (!isSupressUpdatingBlockDiagram()) blockDiagram_.addConnection(c);
		connections_.add(ce);
		firePropertyChange(CONNECTION_ADDED_PROP, null, ce);
		return true;
	}
	
	public boolean removeConnection(ConnectionElement ce) {
		boolean removed = connections_.remove(ce);
		if (!removed) return false;
		if (!isSupressUpdatingBlockDiagram()) {
			BlockDiagramEditorUtil.removeElement(blockDiagram_.getConnections(), ce.getConnection(), true);
		}
		firePropertyChange(CONNECTION_REMOVED_PROP, null, ce);
		return true;
	}

	public BlockDiagram getBlockDiagram() {
		return blockDiagram_;
	}
	
	public HashMap<String, BlockElement> getBlockObjMap() {
		return blockObjMap_;
	}

	public String getUniqueNameForClass(Class<?> cls) {
		String name=""; //$NON-NLS-1$
		for(int i=1;i<1000;++i) {
			name=cls.getSimpleName()+i;
			if (!getBlockObjMap().containsKey(name)) break;
		}
		return name;
	}

	public static jp.hasc.hasctool.core.blockdiagram.model.Point toModelPosition(
			org.eclipse.draw2d.geometry.Point newLocation, boolean snapToGrid) {
		int x = newLocation.x, y = newLocation.y;
		if (snapToGrid) {
			x=x/GRID_SIZE*GRID_SIZE;
			y=y/GRID_SIZE*GRID_SIZE;
		}
		
		return new jp.hasc.hasctool.core.blockdiagram.model.Point(x,y);
	}
	
	public static jp.hasc.hasctool.core.blockdiagram.model.Point toModelPosition(
			org.eclipse.draw2d.geometry.Dimension newSize, boolean snapToGrid) {
		return toModelPosition(new org.eclipse.draw2d.geometry.Point(newSize.width,newSize.height), snapToGrid);
	}

	public boolean addShape(ShapeElement elem) {
		if (!isSupressUpdatingBlockDiagram()) blockDiagram_.addShape(elem.getShape());
		shapes_.add(elem);
		firePropertyChange(SHAPE_ADDED_PROP, null, elem);
		return true;
	}

	public boolean removeShape(ShapeElement elem) {
		if (!shapes_.remove(elem)) return false;
		if (!isSupressUpdatingBlockDiagram()) {
			BlockDiagramEditorUtil.removeElement(blockDiagram_.getShapes(), elem.getShape(), true);
		}
		firePropertyChange(SHAPE_REMOVED_PROP, null, elem);
		return true;
	}

	public Collection<? extends ShapeElement> getShapes() {
		return shapes_;
	}
}
