/*
 * "Peko" Visual Novel System
 *
 * All Rights Reserved.
 * Copyright (c) 1999-2003 Tsukuba Bunko.
 *
 * $Id: StageCanvas.java,v 1.1.2.2 2003/12/14 20:19:20 ppoi Exp $
 */
package tsukuba_bunko.peko.canvas.stage;

import	java.awt.Dimension;
import	java.awt.Graphics;
import	java.awt.Graphics2D;
import	java.awt.Image;

import	java.awt.image.BufferedImage;

import	java.util.Iterator;
import	java.util.Map;

import	javax.swing.JComponent;

import	tsukuba_bunko.peko.Logger;

import	tsukuba_bunko.peko.resource.ResourceManager;


/**
 * 舞台（ステージ）を描画するキャンバスです。
 * @author	$Author: ppoi $
 * @version	$Revision: 1.1.2.2 $
 */
public class StageCanvas	extends JComponent	{

	/**
	 * ステージ
	 */
	private Stage	_stage = null;

	/**
	 * エフェクトを使用するかどうか
	 */
	private boolean	_usingEffect = false;

	/**
	 * エフェクトマップ
	 */
	private Map	_effects = null;

	/**
	 * 描画用オフスクリーンバッファ
	 */
	private BufferedImage	_screen = null;

	/**
	 * 作業用オフスクリーンバッファ
	 */
	private BufferedImage	_working = null;

	/**
	 * size cache
	 */
	private Dimension	_size = new Dimension();


	/**
	 * <code>StageCanvas</code> のインスタンスを作成します。
	 */
	public StageCanvas()
	{
		super();
		initialize();
	}


	/**
	 * このキャンバスで描画するステージを設定します。
	 * @param	stage	ステージ
	 */
	public void setStage( Stage stage )
	{
		if( _stage != null )	{
			_stage.dispose();
		}

		_stage = stage;
		_stage.setStageCanvas( this );
	}

	/**
	 * このキャンバスが現在描画しているステージを取得します。
	 * @return	ステージ
	 */
	public Stage getStage()
	{
		return _stage;
	}

	/**
	 * エフェクトを使用するかどうかを設定します。
	 * @param	useEffect	エフェクトを使用する場合 <code>true</code>、それ以外の場合 <code>false</code>
	 */
	public void setUsingEffect( boolean useEffect )
	{
		_usingEffect = useEffect;
	}

	/**
	 * エフェクトを使用するかどうかを取得します。
	 * @return	エフェクトを使用する場合 <code>true</code>、それ以外の場合 <code>false</code>
	 */
	public boolean isUsingEffect()
	{
		return _usingEffect;
	}

	/**
	 * ステージキャンバスを初期化します。
	 */
	private void initialize()
	{
		ResourceManager	resources = ResourceManager.getInstance();
		Dimension	canvasSize = (Dimension)resources.getResource( "peko.canvas.canvas-size" );

		super.setPreferredSize( canvasSize );
		super.setSize( canvasSize );
		_screen = new BufferedImage( canvasSize.width, canvasSize.height, BufferedImage.TYPE_INT_RGB );
		_working = new BufferedImage( canvasSize.width, canvasSize.height, BufferedImage.TYPE_INT_RGB );

		super.setLayout( null );

		_effects = new java.util.HashMap( 89 );

		Effect	effect = new tsukuba_bunko.peko.canvas.stage.effect.MofingEffect();
		_effects.put( effect.getName(), effect );

		effect = new tsukuba_bunko.peko.canvas.stage.effect.PataPataEffect();
		_effects.put( effect.getName(), effect );

		effect = (Effect)_effects.get( "immediate" );
		if( effect == null )	{
			effect = new tsukuba_bunko.peko.canvas.stage.effect.ImmediateEffect();
			_effects.put( "immediate", effect );
		}
		_effects.put( null, effect );
	}


	/**
	 * キャンバスの描画内容を更新します。
	 */
	public void updateCanvas()
	{
		updateCanvas( null );
	}

	/**
	 * 指定されたエフェクトを用いて、キャンバスの描画内容を更新します。
	 * @param	effectName	更新時に使用するエフェクト名
	 */
	public void updateCanvas( String effectName )
	{
		if( !isEnabled() )	{
			return;
		}

		Graphics2D	g2 = _working.createGraphics();
		Dimension	size = getSize( _size );

		if( _stage.getSlide() == null )	{
			Image	image = _stage.getBackgroundImage();
			if( image != null )	{
				g2.drawImage( image, 0, 0, this );
			}
			else	{
				g2.setColor( _stage.getBackgroundColor() );
				g2.fillRect( 0, 0, size.width, size.height );
			}

			Map	actors = _stage.getActors();
			if( actors != null )	{
				Iterator	itr = actors.values().iterator();
				Actor	actor = null;
				Image	looks = null;
				int	x = 0;
				int	y = 0;
				while( itr.hasNext() )	{
					actor = (Actor)itr.next();
					looks = actor.getLooksImage();
					if( looks != null )	{
						x = computePosition( actor );
						y = size.height - looks.getHeight( this );
						g2.drawImage( looks, x, y, this );
					}
				}
			}
		}
		else	{
			Image	image = _stage.getSlideImage();
			if( image != null )	{
				g2.drawImage( image, 0, 0, this );
			}
			else	{
				g2.fillRect( 0, 0, size.width, size.height );
			}
		}

		if( _usingEffect )	{
			Effect	effect = (Effect)_effects.get( effectName );
			if( effect != null )	{
				effect.process( this, _screen, _working );
			}
			else	{
				Logger.error( "[canvas.stage] unsuported effect name. :" + effect );
				_screen.setData( _working.getRaster() );
				repaint();
			}
			synchronized( this )	{
				try	{
					wait( 100 );
				}
				catch( InterruptedException ie )	{
					Logger.debug( "[canvas.stage] interrupted after effect.", ie );
				}
			}
		}
		else	{
			_screen.setData( _working.getRaster() );
			repaint();
		}
	}

	public void paintPageBackground( boolean visiblePage )
	{
		Graphics2D	g2 = _screen.createGraphics();
		g2.drawImage( _working, 0, 0, this );
		if( visiblePage )	{
			tsukuba_bunko.peko.PekoSystem.getInstance().getCanvasManager().getTextCanvas().paintPageBackground( g2 );
		}
		g2.dispose();
		repaint();
	}


	/**
	 * 立ち位置を計算します。
	 * @param	position	立ち位置の文字列表現
	 */
	private int computePosition( Actor actor )
	{
		int	halfWidth = actor.getLooksImage().getWidth( this ) / 2;
		Dimension	size = getSize( _size );
		switch( actor.getPosition() )	{
		case Actor.POSITION_CENTER:
			return ((int)((float)size.width / 2f)) - halfWidth;
		case Actor.POSITION_LEFT:
			return ((int)((float)size.width / 4f)) - halfWidth;
		case Actor.POSITION_RIGHT:
			return ((int)((size.width / 4f) * 3f)) - halfWidth;
		default:
			return (int)actor.getAbsolutePosition() - halfWidth;
		}
	}

//
//	JComponent の実装
//
	protected void paintComponent( Graphics g )
	{
		g.drawImage( _screen, 0, 0, this );
	}
}
