/*
 * "Peko" Visual Novel System
 *
 * All Rights Reserved.
 * Copyright (c) 1999-2003 Tsukuba Bunko.
 *
 * $Id: FontManager.java,v 1.1.2.1 2003/12/11 11:11:53 ppoi Exp $
 */
package tsukuba_bunko.peko.resource;

import	java.awt.Font;
import	java.awt.GraphicsEnvironment;
import	java.awt.font.TextAttribute;

import	java.io.InputStream;

import	java.net.URL;

import	java.util.Iterator;
import	java.util.Map;
import	java.util.List;
import	java.util.Locale;
import	java.util.Set;

import	tsukuba_bunko.peko.Logger;

import	tsukuba_bunko.peko.resource.ResourceManager;


/**
 * フォントの管理を提供します。
 * @author	$Author: ppoi $
 * @version	$Revision: 1.1.2.1 $
 */
public class FontManager	{

	/**
	 * 唯一のインスタンス
	 */
	private static FontManager	_instance = null;


	/**
	 * フォントキャッシュ
	 */
	private Map	_fonts = null;

	/**
	 * 使用可能なフォントファミリセット
	 */
	private	Set	_fontFamilies = null;

	/**
	 * 新規に作成したフォント
	 */
	private Map	_bundledFonts = null;


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


	/**
	 * <code>attributes</code> で指定された属性を持つフォントを取得します。
	 * @param	attributes	フォントに適用される属性マップ
	 * @return	取得されたフォント
	 */
	public Font getFont( Map attributes )
	{
		Font	font = (Font)_fonts.get( attributes );
		if( font == null )	{
			String	family = (String)attributes.get( TextAttribute.FAMILY );
			Logger.debug( "[font] search font: \"" + family + "\"" );
			if( family == null )	{
				Logger.debug( "[font] not specified font family. using serif family (default)" );
				attributes.put( TextAttribute.FAMILY, "Serif" );
			}

			if( _bundledFonts.keySet().contains(family) )	{
				Logger.debug( "[font] using bundled font" );
				attributes.remove( TextAttribute.FAMILY );
				font = ((Font)_bundledFonts.get(family)).deriveFont( attributes );
				attributes.put( TextAttribute.FAMILY, family );
			}
			else if( _fontFamilies.contains(family) )	{
				Logger.debug( "[font] using system family" );
				font = new Font( attributes );
			}
			else	{
				Logger.error( "[font] invalid font family specified." + family );
				Logger.debug( "[font] using serif family (default)" );
				attributes.remove( TextAttribute.FAMILY );
				font = new Font("Serif", 1, Font.PLAIN).deriveFont( attributes );
				attributes.put( TextAttribute.FAMILY, family );
			}
			_fonts.put( attributes, font );
		}
		return font;
	}

	/**
	 * 初期化
	 */
	private void initialize()
	{
		ResourceManager	resources = ResourceManager.getInstance();

		_fontFamilies = new java.util.HashSet( 89 );

		Locale	locale = (Locale)resources.getResource( "peko.system.locale" );
		if( locale == null )	{
			locale = Locale.getDefault();
		}
		String[]	families = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames( locale );
		for( int i = 0; i < families.length; ++i )	{
			_fontFamilies.add( families[i] );
		}

		_bundledFonts = new java.util.HashMap( 89 );
		List	additional = (List)resources.getResource( "peko.system.fonts", true );
		if( (additional != null) && !additional.isEmpty() )	{
			InputStream	fontStream = null;
			URL	basedir = resources.getLocationResources().getMiscDirecotryURL();
			String	fontPath = null;
			Iterator	itr = additional.iterator();
			while( itr.hasNext() )	{
				try	{
					fontPath = (String)itr.next();
					if( (fontPath == null) || (fontPath.length() == 0) )	{
						continue;
					}
					Logger.debug( "[font] loading font \"" + fontPath + "\"." );
					fontStream = new URL(basedir, fontPath).openStream();
					Font	font = Font.createFont(Font.TRUETYPE_FONT, fontStream );
					_bundledFonts.put( font.getAttributes().get(java.awt.font.TextAttribute.FAMILY), font );
				}
				catch( Exception e )	{
					Logger.warn( "[font] fail to load font \"" + fontPath + "\".", e );
				}
				finally	{
					if( fontStream != null )	{
						try	{
							fontStream.close();
						}
						catch( Exception e )	{
							Logger.error( "[font] fail to close font stream. \"" + fontPath + "\".", e );
						}
						fontStream = null;
					}
				}
			}
		}

		_fonts = new java.util.WeakHashMap( 89 );
	}



	/**
	 * 唯一の <code>FontManager</code> インスタンスを取得します。
	 * @return	唯一の <code>FontManager</code> インスタンス
	 */
	public static FontManager getInstance()
	{
		if( _instance == null )	{
			synchronized( FontManager.class )	{
				if( _instance == null )	{
					_instance = new FontManager();
					_instance.initialize();
				}
			}
		}
		return _instance;
	}
}
