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

import	java.util.StringTokenizer;

import	org.xml.sax.Attributes;

import	tsukuba_bunko.peko.Logger;

import	tsukuba_bunko.peko.scenario.SceneContext;


/**
 * PSML の処理に必要なユーティリティメソッドを提供します。
 * @author	$Author: ppoi $
 * @version	$Revision: 1.1.2.1 $
 */
public final class PSMLUtil	{

	/**
	 * <code>PSMLUtil</code> のインスタンスは生成できません。
	 */
	private PSMLUtil()
	{
	}


	/**
	 * ISO 制御文字を除去します。
	 * @param	string	除去対象の文字列
	 * @return	除去処理後の文字列
	 */
	public static String removeISOControlChar( String string )
	{
		if( (string == null) || (string.length() == 0) )	{
			return string;
		}

		int	beginIndex = -1;
		boolean	isISOControlCharSequence = false;

		int	length = string.length();
		StringBuffer	buffer = null;
		for( int current = 0; current < length; ++current )	{
			if( Character.isISOControl(string.charAt(current)) )	{
				if( !isISOControlCharSequence )	{
					if( buffer == null )	{
						if( current == 0 )	{
							buffer = new StringBuffer();
						}
						else	{
							buffer = new StringBuffer( string.substring(0, current) );
						}
					}
					else	{
						buffer.append( string.substring(beginIndex, current) );
					}
					isISOControlCharSequence = true;
					beginIndex = -1;
				}
			}
			else	{
				if( isISOControlCharSequence )	{
					beginIndex = current;
					isISOControlCharSequence = false;
				}
			}
		}
		if( beginIndex != -1 )	{
			buffer.append( string.substring(beginIndex) );
		}

		if( buffer != null )	{
			return new String( buffer );
		}
		else	{
			return string;
		}
	}


	/**
	 * PSML 1.0 "Scene" ネームスペース、ローカルネームスペースの順で属性値を検索します。
	 * @param	localName	属性名のローカル名
	 * @return	属性値。属性が定義されていない場合は <code>null</code>
	 */
	public static String getAttributeValue( Attributes attrs, String localName )
	{
		String	value = attrs.getValue( ElementHandler.NAMESPACE_SCENE, localName );
		if( value == null )	{
			value = attrs.getValue( localName );
		}

		if( (value == null) || (value.length() == 0) )	{
			return null;
		}
		else	{
			value = PSMLUtil.removeISOControlChar(value).trim();
			if( value.length() == 0 )	{
				Logger.warn( MessageIDs.SCN0011W, new Object[]{localName} );
				return null;
			}
			else	{
				return value;
			}
		}
	}

	/**
	 * <code>attrs</code> からテストを取り出し、シナリオ文脈 <code>context</code> で評価可能かどうかを判定します。
	 * @param	attrs	要素の属性セット
	 * @param	context	現在のシナリオ文脈
	 * @return	評価可能の場合 <code>true</code>、評価不可の場合 <code>false</code>
	 */
	public static final boolean isEvaluatable( Attributes attrs, SceneContext context )
	{
		return isEvaluatable( PSMLUtil.getAttributeValue(attrs, "test"), context );
	}

	/**
	 * <code>target</code> で指定されるテストを使用し、シナリオ文脈 <code>context</code> で評価可能かどうかを判定します。
	 * @param	target	テスト
	 * @param	context	現在のシナリオ文脈
	 * @return	評価可能の場合 <code>true</code>、評価不可の場合 <code>false</code>
	 */
	public static final boolean isEvaluatable( String target, SceneContext context )
	{
		if( (target == null) || (target.length() == 0) )	{
			return true;
		}
		else if( context == null )	{
			return false;
		}
		else	{
			boolean	result = false;
	
			boolean	multiple = (target.charAt(0) == '[');
			boolean	inBracket = false;
			boolean	individualResult = true;
	
			StringTokenizer	st = new StringTokenizer( target, "[],", true );
			String	token = null;
			String	lastToken = null;
			int	position = 0;
			while( st.hasMoreTokens() )	{
				lastToken = token;
				token = st.nextToken();
				position += token.length();
				token = token.trim();
	
				if( !multiple )	{
					if( "[".equals(token) || "]".equals(token) )	{
						Logger.warn( MessageIDs.SCN0008W, new Object[]{String.valueOf(position), token} );
						return false;
					}
					else if( !",".equals(token) )	{
						if( token.charAt(0) == '!' )	{
							if( token.length() == 1 )	{
								Logger.warn( MessageIDs.SCN0008W, new Object[]{String.valueOf(position), token} );
								return false;
							}
							token = token.substring( 1 );
							individualResult = individualResult && !context.isDeclaredFlag(token);
						}
						else	{
							individualResult = individualResult && context.isDeclaredFlag(token);
						}
					}
				}
				else	{
					if( inBracket )	{
						if( "]".equals(token) )	{
							if( (lastToken != null) && ",".equals(lastToken) )	{
								Logger.warn( MessageIDs.SCN0008W, new Object[]{String.valueOf(position), token} );
								return false;
							}
							else	{
								result = result || individualResult;
								individualResult = true;
								inBracket = false;
							}
						}
						else if( !",".equals(token) )	{
							if( token.charAt(0) == '!' )	{
								if( token.length() == 1 )	{
									Logger.warn( MessageIDs.SCN0008W, new Object[]{String.valueOf(position), token} );
									return false;
								}
								token = token.substring( 1 );
								individualResult = individualResult && !context.isDeclaredFlag(token);
							}
							else	{
								individualResult = individualResult && context.isDeclaredFlag(token);
							}
						}
					}
					else	{
						if( "[".equals(token) )	{
							inBracket = true;
						}
						else	{
							Logger.warn( MessageIDs.SCN0008W, new Object[]{String.valueOf(position), token} );
							return false;
						}
					}
				}
			}
	
			if( multiple )	{
				if( !"]".equals(token) )	{
					Logger.warn( MessageIDs.SCN0008W, new Object[]{String.valueOf(position), token} );
					return false;
				}
			}
			else	{
				if( ",".equals(token) )	{
					Logger.warn( MessageIDs.SCN0008W, new Object[]{String.valueOf(position), token} );
					return false;
				}
				else	{
					result = individualResult;
				}
			}
			return result;
		}
	}
}
