/*
 * "Peko" Visual Novel System
 *
 * All Rights Reserved.
 * Copyright (c) 1999-2003 Tsukuba Bunko.
 *
 * $Id: TextCanvas.java,v 1.1.2.1 2003/12/11 10:22:54 ppoi Exp $
 */
package tsukuba_bunko.peko.canvas.text;

import	java.awt.AlphaComposite;
import	java.awt.Dimension;
import	java.awt.Graphics;
import	java.awt.Graphics2D;
import	java.awt.Insets;
import	java.awt.Point;
import	java.awt.RenderingHints;

import	java.awt.font.FontRenderContext;

import	java.util.List;

import	javax.swing.BorderFactory;
import	javax.swing.JComponent;

import	javax.swing.border.Border;

import	tsukuba_bunko.peko.Logger;


/**
 * テキストを表示するキャンバスです。
 * @author	$Author: ppoi $
 * @version	$Revision: 1.1.2.1 $
 */
public class TextCanvas	extends JComponent	{

	/**
	 * Page
	 */
	private Page	_page = null;


	/**
	 * Lines
	 */
	private List	_lines = null;

	/**
	 * alpha-composite
	 */
	private AlphaComposite	_alphaComposite = AlphaComposite.getInstance( AlphaComposite.DST_OVER, 0.5f );

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

	/**
	 * location
	 */
	private Point	_location = new Point( 0, 0 );

	/**
	 * padding
	 */
	private Insets	_padding = new Insets( 0, 0, 0, 0 );


	/**
	 * cached FontRenderContext
	 */
	private FontRenderContext	_frc = null;


	/**
	 * marker
	 */
	private Marker	_marker = null;


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


	/**
	 * ページに文字を描画するのに使用する <code>FontRenderContext</code> を取得します。
	 * @return	ページに文字を描画するのに使用する <code>FontRenderContext</code>
	 */
	public FontRenderContext getFontRenderContext()
	{
		if( _frc == null )	{
			synchronized( this )	{
				if( _frc == null )	{
					try	{
						Logger.debug( "[canvas.text] waiting for create cached FontRenderContext." );
						wait();
						Logger.debug( "[canvas.text] creating cached FontRenderContext done." );
					}
					catch( InterruptedException ie )	{
						Logger.error( "[canvas.text] interrupted." );
					}
				}
			}
		}
		return _frc;
	}

	/**
	 * 背景色を描画する際に使用する AlphaComposite を取得します。
	 * @return	背景色を描画する際に使用する AlphaComposite インスタンス
	 */
	public AlphaComposite getAlphaComposite()
	{
		return _alphaComposite;
	}

	/**
	 * キャンバスを最新の状態に更新します。
	 */
	public void updateCanvas()
	{
		if( !isEnabled() )	{
			return;
		}

		Logger.debug( "[canvas.text] update TextCanvas view" );
		_lines = null;
		_page.getSize( _size );
		_page.getLocation( _location );
		_page.getPadding( _padding );
		setForeground( _page.getForeground() );
		setBackground( _page.getBackground() );
		float	trans = _page.getTransparency();
		if( (_alphaComposite == null) || (_alphaComposite.getAlpha() != trans) )	{
			_alphaComposite = AlphaComposite.getInstance( AlphaComposite.SRC_OVER, trans );
		}
		_marker.setText( "▼", _page );
	}

	/**
	 * キャンバスに描画するテキストを最新の状態に更新します。
	 */
	public void updateText()
	{
		if( !isEnabled() )	{
			return;
		}

		Logger.debug( "[canvas.text] update texts." );
		_marker.setVisible( false );

		List	lines = _page.getLines();
		int size = lines.size();
		for( int i = 0; i < size; ++i )	{
			((Line)lines.get(i)).prepare( _page );
		}
		_lines = lines;

//		setVisible( true );
		tsukuba_bunko.peko.PekoSystem.getInstance().getCanvasManager().showTextCanvas();
		repaint();

		try	{
			synchronized( this )	{
				wait( 100 );
			}
		}
		catch( Exception e )	{
		}
		_marker.setVisible( true );
	}


	/**
	 * このキャンバスで描画するページを設定します。
	 * @param	page	このキャンバスで描画するページ
	 */
	public void setPage( Page page )
	{
		_page = page;
		_page.setTextCanvas( this );
	}

	/**
	 * 現在このキャンバスで描画中のページを取得します。
	 * @return	現在このキャンバスで描画中のページ
	 */
	public Page getPage()
	{
		return _page;
	}

	public void paintPageBackground( Graphics g )
	{
		Dimension	size = _size;
		Point	location = _location;

		Graphics2D	g2 = (Graphics2D)g.create( location.x, location.y, size.width, size.height );
		g2.setComposite( _alphaComposite );
		g2.setColor( getBackground() );
		g2.fillRect( 0, 0, size.width, size.height );
		g2.dispose();
	}

	/**
	 * TextCanvas を初期化します。
	 */
	private void initialize()
	{
		setLayout( null );
		setDoubleBuffered( false );
		setBorder( BorderFactory.createEtchedBorder() );

		_marker = new Marker();
		add( _marker );
		_marker.setLocation( 0, 0 );
		_marker.setVisible( false );
	}


//
//	JComponent の実装
//
	public void setVisible( boolean visibility )
	{
		Logger.debug( "[canvas.text] set visibility :" + visibility );
		if( visibility )	{
			_marker.start();
		}
		super.setVisible( visibility );
	}

	public void addNotify()
	{
		super.addNotify();
		Logger.debug( "[canvas.text] added notify to TextCavas" );
		if( _frc == null )	{
			Logger.debug( "[canvas.text] try create new font render context." );
			synchronized( this )	{
				if( _frc == null )	{
					Graphics2D	g2 = (Graphics2D)getGraphics();
					g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
					_frc = g2.getFontRenderContext();
					Logger.debug( "[canvas] notify all thread waiting at TextCanvas" );
					notifyAll();
				}
			}
		}
	}

	public void paint( Graphics g )
	{
		List	lines = _lines;
		if( (lines == null) || lines.isEmpty() )	{
			return;
		}
		else	{
			super.paint( g );
		}		
	}

	public void paintBorder( Graphics g )
	{
		Border	border = getBorder();
		if( border != null )	{
			border.paintBorder( this, g, _location.x, _location.y, _size.width, _size.height );
		}
	}

	public void paintComponent( Graphics g )
	{
		List	lines = _lines;

		Dimension	size = _size;
		Point	location = _location;
		Insets	padding = _padding;

//		paintPageBackground( g );

		Graphics2D	g2 = (Graphics2D)g;
		float	x = (float)(location.x + padding.left);
		float	y = (float)(location.y + padding.top);
		float	tail = 0f;
		int	length = lines.size();
		Line	line = null;
		for( int i = 0; i < length; ++i )	{
			line = (Line)lines.get( i );
			y += line.getAscent();
			line.draw( g2, x, y );
			y += line.getDescent();
			tail = line.getAdavance();
		}
		_marker.setPosition( (int)(tail + 5f + x), (int)y );
	}
}
