/* $Id: ConcurrentLayer.java,v 1.2 2007/12/11 09:54:58 nito Exp $
 * 
 * Copyright (c)ARGO 21, Corporation. 2005, 2006.  All rights reserved.
 * 
 * This file is part of Nautica Workflow.
 * 
 *  Nautica Workflow is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 * 
 *  Nautica Workflow is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with Nautica Workflow; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *  
 */
package jp.co.argo21.nautica.tool.wfd;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

import jp.co.argo21.nautica.tool.wfd.element.ActivityElement;

/**
 * 並行処理アクティビティで構成される同期階層を管理する。
 * 
 * @author  Norihiro Itoh(ARGO 21 Corp.)
 * @version $Revision: 1.2 $
 * @since   Nautica Workflow 1.0
 */
public class ConcurrentLayer
{
	//同期階層の開始アクティビティ
	private ActivityElement start;
	//同期階層の終了アクティビティ
	private ActivityElement end;
	//親の同期階層のリスト
	private ConcurrentLayer parentLayer;
	//内包される同期階層のリスト
	private List<ConcurrentLayer> subLayers = new ArrayList<ConcurrentLayer>();
	//このレイヤーに所属するアクティビティのリスト
	private List<ActivityElement> activities = new ArrayList<ActivityElement>();

	/**
	 * 同期階層を生成する。
	 * 
	 * @param p	親階層
	 * @param s	並行処理開始アクティビティ
	 * @param e	並行処理終了アクティビティ
	 */
	public ConcurrentLayer(ConcurrentLayer p, ActivityElement s, ActivityElement e)
	{
		this.parentLayer = p;
		this.start = s;
		this.end = e;
	}

	/**
	 * 同期階層を生成する。
	 */
	public ConcurrentLayer()
	{
	}

	/**
	 * 階層IDを返す。
	 * 
	 * @return	階層ID
	 */
	public String getLayerID()
	{
		return start.getBlockID();
	}

	/**
	 * 階層にアクティビティを追加する。
	 * 
	 * @param elem	アクティビティ
	 */	
	public void addLayerElement(ActivityElement elem)
	{
		if (elem == null) return;
		elem.setAffiliateBID(getStartElement().getBlockID());
		activities.add(elem);		
	}

	/**
	 * 階層からアクティビティを削除する。
	 * 
	 * @param elem	アクティビティ
	 */	
	public void removeLayerElement(ActivityElement elem)
	{
		if (elem == null) return;
		if (activities.contains(elem)) {
			activities.remove(elem);
		} else {
			for (ConcurrentLayer layer : subLayers) {
				layer.removeLayerElement(elem);
			}
		}
		elem.setAffiliateBID(null);
		elem.setParentBID(null);
	}

	/**
	 * 同期階層を追加する。
	 * 
	 * @param layer	同期階層
	 */	
	public void addLayer(ConcurrentLayer layer)
	{
		if (layer == null) return;
		
		layer.getStartElement().setParentBID(getStartElement().getBlockID());
		layer.getEndElement().setParentBID(getStartElement().getBlockID());
		addLayerElement(layer.getStartElement());
		addLayerElement(layer.getEndElement());
		subLayers.add(layer);
	}

	/**
	 * 同期階層を削除する。
	 * 
	 * @param l	同期階層
	 */	
	public void removeLayer(ConcurrentLayer l)
	{
		if (l == null) return;

		if (subLayers.contains(l)) {
			removeLayerElement(l.getStartElement());
			removeLayerElement(l.getEndElement());
			l.clearParentLayer();
			subLayers.remove(l);
		} else {
			for (ConcurrentLayer layer : subLayers) {
				layer.removeLayer(l);
			}
		}
	}

	/**
	 * 前回層のアクティビティを収集して返す。
	 * 
	 * @param list	収集用リスト
	 */	
	public void collectAllLayerElement(List<ActivityElement> list)
	{
		for (ConcurrentLayer layer : subLayers) {
			layer.collectAllLayerElement(list);
		}

		for (ActivityElement act : activities) {
			if (list.contains(act) == false) list.add(act);
		}
		if (list.contains(start) == false) list.add(start);
		if (list.contains(end) == false) list.add(end);
	}

	/**
	 * 受け入れ可能な同期階層を返す。
	 * 
	 * @param lx 論理X座標
	 * @param ly 論理Y座標
	 * @return 同期階層
	 */
	public ConcurrentLayer getReceivableLayer(int lx, int ly)
	{
		for (ConcurrentLayer layer : subLayers) {
			ConcurrentLayer rLayer = layer.getReceivableLayer(lx, ly);
			if (rLayer != null) return rLayer;
		}

		int slx = start.getLogicalX();
		int sly = start.getLogicalY();
		int elx = end.getLogicalX();
		int ely = end.getLogicalY();
		
		if (lx >= slx && lx <= elx && ly >= sly && ly <= ely) return this;
		else return null;
	}

	/**
	 * 指定されたアクティビティが所属する同期階層を返す。
	 * 
	 * @param elem	アクティビティ
	 * @return	同期階層
	 */	
	public ConcurrentLayer getContainLayer(ActivityElement elem)
	{
		for (ConcurrentLayer layer : subLayers) {
			ConcurrentLayer cLayer = layer.getContainLayer(elem);
			if (cLayer != null) return cLayer;
		}

		if (elem == getStartElement()) return this;
		else if (elem == getEndElement()) return this;
		else if (activities.contains(elem)) return this;
		else return null;
	}

	/**
	 * この階層の並行処理開始アクティビティを返す。
	 * 
	 * @return	並行処理開始アクティビティ
	 */
	public ActivityElement getStartElement()
	{
		return start;
	}

	/**
	 * この階層の並行処理終了アクティビティを返す。
	 * 
	 * @return	並行処理終了アクティビティ
	 */
	public ActivityElement getEndElement()
	{
		return end;
	}
	/**
	 * 親の同期階層を返す。
	 * 
	 * @return	同期階層
	 */
	public ConcurrentLayer getParentLayer()
	{
		return parentLayer;
	}
	
	/**
	 * 親の同期階層を削除する。
	 */
	public void clearParentLayer()
	{
		parentLayer = null;
	}

	/**
	 * 再配置可能な上部位置を返す。
	 * 
	 * @return	論理座標
	 */
	public Point getMinimumUpperPoint()
	{
		int x = Integer.MAX_VALUE;
		int y = Integer.MAX_VALUE;
		
		for (ActivityElement act : activities) {
			if (x > act.getLogicalX()) x = act.getLogicalX();
			if (y > act.getLogicalY()) y = act.getLogicalY();
		}
		return new Point(x, y);
	}

	/**
	 * 再配置可能な下部位置を返す。
	 * 
	 * @return	論理座標
	 */	
	public Point getMinimumLowerPoint()
	{
		int x = 0;
		int y = 0;
		
		for (ActivityElement act : activities) {
			if (x < act.getLogicalX()) x = act.getLogicalX();
			if (y < act.getLogicalY()) y = act.getLogicalY();
		}
		if (x == -1) x = start.getLogicalX();
		if (y == -1) y = start.getLogicalY();
		return new Point(x, y);
	}

	/**
	 * 同期階層の背景を描画する。
	 * 
	 * @param g	描画対象グラフィックス
	 * @param c	描画色
	 * @param grid	単位幅
	 */
	public void paintLayerBackgroud(Graphics g, Color c, int grid)
	{
		int x = start.getLogicalX() * grid;
		int y = start.getLogicalY() * grid;
		int w = end.getLogicalX() * grid + grid - x;
		int h = end.getLogicalY() * grid + grid - y;
		
		g.setColor(c);
		g.fillRect(x, y, w, h);
		
		for (ConcurrentLayer layer : subLayers) {
			layer.paintLayerBackgroud(g, c, grid);
		}
	}

	/**
	 * 同期階層の枠を描画する。
	 * 
	 * @param g	描画対象グラフィックス
	 * @param c	描画色
	 * @param grid	単位幅
	 */
	public void paintLayerBorder(Graphics g, Color c, int grid)
	{
		int x = start.getLogicalX() * grid;
		int y = start.getLogicalY() * grid;
		int w = end.getLogicalX() * grid + grid - x;
		int h = end.getLogicalY() * grid + grid - y;

		g.setColor(c);
		g.drawRect(x, y, w, h);
		g.drawRect(x + 1, y + 1, w - 2, h - 2);
		
		for (ConcurrentLayer layer : subLayers) {
			layer.paintLayerBorder(g, c, grid);
		}
	}

	/**
	 * この階層の並行処理終了アクティビティを設定する。
	 * 
	 * @param element	並行処理終了アクティビティ
	 */
	public void setEndElement(ActivityElement element) {
		end = element;
	}

	/**
	 * 親の同期階層を設定する。
	 * 
	 * @param layer	同期階層
	 */
	public void setParentLayer(ConcurrentLayer layer) {
		parentLayer = layer;
	}

	/**
	 * この階層の並行処理開始アクティビティを設定する。
	 * 
	 * @param element	並行処理開始アクティビティ
	 */
	public void setStartElement(ActivityElement element) {
		start = element;
	}
}
