/* $Id: ElementReceivableView.java,v 1.3 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.Dimension;
import java.awt.Graphics;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.border.Border;

import jp.co.argo21.nautica.tool.wfd.element.Element;
import jp.co.argo21.nautica.tool.wfd.inspector.ChangeElementListener;

/**
 * 要素アイコンのドロップを受け入れ可能なビュー
 * 
 * 
 * @author  Norihiro Itoh(ARGO 21 Corp.)
 * @version $Revision: 1.3 $
 * @since   Nautica Workflow 1.0
 */
public class ElementReceivableView extends JComponent
implements MouseListener, DropTargetListener
{
	/** シリアルバージョンID */
	private static final long serialVersionUID = 1L;

	/** ビューのサイズ */
	private static final Dimension VIEW_SIZE = new Dimension(60, 60);
	/** マスクする色 */
	private static final Color MASK_COLOR = new Color(255, 255, 255, 128);
	/** 枠線 */
	private static final Border border = BorderFactory.createLoweredBevelBorder();
	/** リスナー */
	private List<ChangeElementListener> listeners = new ArrayList<ChangeElementListener>();

	/** 受け入れ中かどうかを示すフラグ */
	private boolean isReceiving;
	/** 受け入れる要素の型 */
	private String elementType;
	/** 受け入れた要素 */
	private Element element;

	/**
	 * ビューを生成する。
	 * 
	 * @param type 受け入れる要素の型
	 */
	public ElementReceivableView(String type)
	{
		elementType = type;
		setSize(VIEW_SIZE);
		setPreferredSize(VIEW_SIZE);
		setMinimumSize(VIEW_SIZE);
		new DropTarget(this, this);
		addMouseListener(this);
	}

	/**
	 * アイコン表示エリアを描画する。
	 *
	 * @param g 描画対象グラフィックス
	 */
	public void paintComponent(Graphics g)
	{
		Dimension size = this.getSize();

		//描画範囲以外の背景の描画
		g.setColor(Color.white);
		g.fillRect(0, 0, size.width, size.height);
		//アクティビティアイコンの描画
		if (isReceiving || element != null) {
			Icon icon = IconManager.getIcon(elementType);

			int iconX = (size.width - icon.getIconWidth()) / 2;
			int iconY = (size.height - icon.getIconHeight()) / 2;
			icon.paintIcon(this, g, iconX, iconY);
		}

		if (isReceiving) {
			g.setColor(MASK_COLOR);
			g.fillRect(0, 0, size.width, size.height);
		}
		border.paintBorder(this, g, 0, 0, size.width, size.height);

		g.dispose();
	}

	/**
	 * 要素を返す。
	 * 
	 * @return	要素
	 */
	public Element getElement()
	{
		return element;
	}

	/**
	 * 要素を設定する
	 * 
	 * @param element	要素
	 */
	public void setElement(Element element)
	{
		this.element = element;
		repaint();
	}

	/*
	 * インタフェースMouseListener実装
	 */
	/**
	 * マウスクリックを判定する。
	 *
	 * @param e マウスイベント
	 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
	 */
	public void mouseClicked(MouseEvent e)
	{
		if (element == null) return;
		
		if (e.getClickCount() >= 2) {
			//要素のダイアログを表示
			InspectorDialog.showDialog(ElementReceivableView.this, element);
			//アイコンが更新されたことを同報
			fireElementChanged();
		}

		repaint();
	}

	/**
	 * マウス押下を判定する。
	 *
	 * @param e マウスイベント
	 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
	 */
	public void mousePressed(MouseEvent e) { /* NOP */ }

	/**
	 * マウスの押下解除を判定する。
	 *
	 * @param e マウスイベント
	 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
	 */
	public void mouseReleased(MouseEvent e) { /* NOP */ }

	/**
	 * マウスの侵入を判定する。
	 *
	 * @param e マウスイベント
	 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
	 */
	public void mouseEntered(MouseEvent e) { /* NOP */ }

	/**
	 * マウスの脱出を判定する。
	 *
	 * @param e マウスイベント
	 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
	 */
	public void mouseExited(MouseEvent e) { /* NOP */ }

	/**
	 * ドラッグがビュー上に入った場合に呼ばれる。
	 * 
	 * @param e DropTargetDragEvent
	 * @see java.awt.dnd.DropTargetListener#dragEnter(java.awt.dnd.DropTargetDragEvent)
	 */
	public void dragEnter(DropTargetDragEvent e)
	{
		e.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
	}

	/**
	 * ドラッグがビューから出た場合に呼ばれる。
	 * 
	 * @param e DropTargetEvent
	 * @see java.awt.dnd.DropTargetListener#dragExit(java.awt.dnd.DropTargetEvent)
	 */
	public void dragExit(DropTargetEvent e)
	{
		isReceiving = false;
		repaint();
	}

	/**
	 * ドラッグがビュー上にある場合に呼ばれる。
	 * 
	 * @param e DropTargetDragEvent
	 * @see java.awt.dnd.DropTargetListener#dragOver(java.awt.dnd.DropTargetDragEvent)
	 */
	public void dragOver(DropTargetDragEvent e)
	{
		try {
			DataFlavor[] flavors = e.getCurrentDataFlavors();
			if (flavors == null) return;
			if (flavors.length != 1) return;
			DataFlavor df = flavors[0];
			if (df == null) return;

			if (df.getPrimaryType().equals(WorkflowDesignerConstants.PTYPE)
				&& df.getSubType().equals(WorkflowDesignerConstants.STYPE_ELEMENT)) {
				if (df.getHumanPresentableName().equals(elementType)) {
					isReceiving = true;
					repaint();
				}
			}
		} catch (Exception ex) {
		} finally {
			repaint();
		}
	}

	/**
	 * ドロップが行われたときに呼ばれる。
	 * 
	 * @param e DropTargetDropEvent
	 * @see java.awt.dnd.DropTargetListener#drop(java.awt.dnd.DropTargetDropEvent)
	 */
	public void drop(DropTargetDropEvent e)
	{
		e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);

		try {
			boolean invalid = false;
			Transferable t = e.getTransferable();
			if (t == null) invalid = true;
			DataFlavor[] flavors = t.getTransferDataFlavors();
			if (flavors == null) invalid = true;
			if (flavors.length != 1) invalid = true;
			DataFlavor df = flavors[0];
			if (df == null) invalid = true;
			if (df.getPrimaryType().equals(WorkflowDesignerConstants.PTYPE)
				&& df.getSubType().equals(WorkflowDesignerConstants.STYPE_ELEMENT)) {
				if (df.getHumanPresentableName().equals(elementType)) {
					//OK
				} else {
					invalid = true;
				}
			} else {
				invalid = true;
			}

			if (invalid) {
				e.dropComplete(false);
				return;
			}

			setElement((Element)t.getTransferData(df));
			
			//アイコンがドロップされたことを同報
			fireElementDropped();
			e.dropComplete(true);
		} catch (Exception ex) {
			e.dropComplete(false);
		} finally {
			isReceiving = false;
			repaint();
		}
	}

	/**
	 * ドロップアクションが切り替わったときに呼ばれる。
	 * 未使用
	 * 
	 * @param e DropTargetDragEvent
	 * @see java.awt.dnd.DropTargetListener#dropActionChanged(java.awt.dnd.DropTargetDragEvent)
	 */
	public void dropActionChanged(DropTargetDragEvent e) { /* nop */ }

	/**
	 * リスナーを追加する。
	 * 
	 * @param l リスナー
	 */
	public void addChangeElementListener(ChangeElementListener l)
	{
		if (listeners.contains(l) == false) listeners.add(l);
	}

	/**
	 * リスナーを削除する。
	 * 
	 * @param l リスナー
	 */
	public void removeChangeElementListener(ChangeElementListener l)
	{
		if (listeners.contains(l)) listeners.remove(l);
	}

	/**
	 * リスナーに要素の変更を同報する。
	 */
	private void fireElementChanged()
	{
		for (ChangeElementListener l : listeners) {
			l.elementChanged(element);
		}
	}

	/**
	 * リスナーに要素のドロップを同報する。
	 */
	private void fireElementDropped()
	{
		for (ChangeElementListener l : listeners) {
			l.elementDropped(element);
		}
	}
}

