
import "xml_document.rs" ;


class	SampleApp
{
	// モーダル優先度
	public static const int	MODAL_NORMAL	= 0 ;		// 通常
	public static const int	MODAL_SYSTEM	= 1 ;		// 設定画面等
	public static const int	MODAL_EXCLUSIVE	= 2 ;		// メッセージボックス等

	// 入力処理
	class	InputDispatcher
	{
		public abstract boolean onCommand( VirtualInput.Command cmd, int modal ) ;
		public abstract boolean onInputEvent( VirtualInput.InputEvent iev, int modal ) ;
	}

	// エスケープ例外
	class	Escape	extends Exception
	{
		public int	m_escape ;

		public Escape( int escape )
		{
			super( null ) ;
			m_escape = escape ;
		}
	}

	public static const String	PROFILE_PATH = "profile.xml" ;

	public static const int		SCREEN_WIDTH	= 1280 ;
	public static const int		SCREEN_HEIGHT	= 720 ;

	public static const int		ESCAPE_NOTHING	= 0 ;
	public static const int		ESCAPE_APP_EXIT	= 1 ;
	public static const int		ESCAPE_RESTART	= 2 ;

	protected boolean			m_initWindow ;
	protected XMLDocument		m_profile ;
	protected WindowSprite		m_window ;
	protected VirtualInput		m_input ;
	protected InputDispatcher[]	m_dispatchers ;
	public int					m_modal ;
	public int[]				m_modalStack ;

	// 構築
	public SampleApp( void )
	{
		m_initWindow = false ;
		m_dispatchers = new InputDispatcher[] ;
		m_modal = MODAL_NORMAL ;
		m_modalStack = new int[] ;
	}

	// 初期化
	public void initialize( String strCaption )
	{
		System.setWritableFilePath( "$(DOCUMENTS)\\.rosetta\\sample" ) ;
		System.addReadableFilePath( "$(SCRIPT_DIR)\\..\\res" ) ;

		m_profile = new XMLDocument() ;
		m_window = new WindowSprite() ;
		m_input = new VirtualInput() ;

		loadProfile() ;

		if ( getProfileString( "window\\x" ) != null )
		{
			Size	sizeInit = new Size() ;
			int		xPos = getProfileInt( "window\\x" ) ;
			int		yPos = getProfileInt( "window\\y" ) ;
			sizeInit.w = getProfileInt( "window\\width", SCREEN_WIDTH ) ;
			sizeInit.h = getProfileInt( "window\\height", SCREEN_HEIGHT ) ;
			//
			m_window.initWindowPosition( xPos, yPos, sizeInit ) ;
		}
		m_window.setOptionalFlags
			( WindowSprite.flagAllowClose
				| WindowSprite.flagVariableWindowSize ) ;
		m_window.createDisplay
			( strCaption,
				WindowSprite.modeWindow,
				SCREEN_WIDTH, SCREEN_HEIGHT ) ;

		m_input.loadPostfilter( "input_filter.xml" ) ;
		m_input.attachPostListenerToWindow( m_window ) ;

		m_initWindow = true ;
	}

	// 終了処理
	public void release( void )
	{
		if ( m_initWindow )
		{
			Point	ptWindow = new Point() ;
			Size	sizeWindow = new Size() ;
			if ( m_window.getNormalWindowPosition( ptWindow, sizeWindow ) )
			{
				setProfileInt( "window\\x", ptWindow.x ) ;
				setProfileInt( "window\\y", ptWindow.y ) ;
				setProfileInt( "window\\width", sizeWindow.w ) ;
				setProfileInt( "window\\height", sizeWindow.h ) ;
			}
			saveProfile() ;

			releaseWindow() ;
		}
		m_dispatchers = new InputDispatcher[] ;
	}

	public void releaseWindow( void )
	{
		if ( m_initWindow )
		{
			m_input.detachPostListenerToWindow( m_window ) ;
			m_window.detachAllChildren() ;
			m_window.closeDisplay() ;
			m_initWindow = false ;
		}
	}

	public void finalize( void )
	{
		// ※例外エラーなどで予期しない状態で終了すると
		// 　正しくない順序でウィンドウに関連するオブジェクトが破棄されてしまい
		// 　Rosetta の例外ハンドラーがエラーメッセージを出力する前に
		// 　実際の例外エラーが発生してアプリケーションが終了してしまうため
		releaseWindow() ;
	}

	// 入力処理
	public void attachDispatcher( InputDispatcher idsp )
	{
		if ( m_dispatchers.indexOf( idsp ) < 0 )
		{
			m_dispatchers.add( idsp ) ;
		}
	}

	public void detachDispatcher( InputDispatcher idsp )
	{
		int	i = m_dispatchers.indexOf( idsp ) ;
		if ( i >= 0 )
		{
			m_dispatchers.remove( i ) ;
		}
	}

	// 実行
	public void run( Runnable r )
	{
		InputDispatcher	idsp = null ;
		if ( r instanceof InputDispatcher )
		{
			idsp = (InputDispatcher) r ;
		}
		for ( ; ; )
		{
			if ( idsp != null )
			{
				attachDispatcher( idsp ) ;
			}
			try
			{
				r.run() ;
			}
			catch ( Escape e )
			{
				if ( e.m_escape == ESCAPE_APP_EXIT )
				{
					break ;
				}
			}
			finally
			{
				if ( idsp != null )
				{
					detachDispatcher( idsp ) ;
				}
			}
		}
	}

	// エスケープ
	public void throwEscape( int escape )
	{
		throw	new Escape( escape ) ;
	}

	public void escapeAppExit( void )
	{
		throwEscape( ESCAPE_APP_EXIT ) ;
	}

	public void escapeRestart( void )
	{
		throwEscape( ESCAPE_RESTART ) ;
	}

	// ウィンドウ取得
	public const WindowSprite getWindow( void )
	{
		return	m_window ;
	}

	// 入力フィルタ取得
	public const VirtualInput getInput( void )
	{
		return	m_input ;
	}

	// 待機ループ
	public void waitFor( Function<boolean> func )
	{
		while ( !func() )
		{
			processInput() ;
		}
	}

	// 入力処理
	public void processInput( int sleepTime = 1 )
	{
		VirtualInput.Command	cmd = new VirtualInput.Command() ;
		VirtualInput.InputEvent	iev = new VirtualInput.InputEvent() ;
		if ( m_input.getCommand( cmd ) )
		{
			dispatchCommand( cmd ) ;
		}
		if ( m_input.getInputEvent( iev ) )
		{
			dispatchInput( iev ) ;
		}
		if ( sleepTime > 0 )
		{
			try
			{
				Thread.sleep( sleepTime ) ;
			}
			catch( Exception e )
			{
				break ;
			}
		}
	}

	public void dispatchCommand( VirtualInput.Command cmd )
	{
		for ( int i = 0; i < m_dispatchers.length(); i ++ )
		{
			if ( m_dispatchers[i].onCommand( cmd, m_modal ) )
			{
				return ;
			}
		}
		if ( cmd.strID == VirtualInput.Command.AppExit )
		{
			escapeAppExit() ;
		}
	}

	public void dispatchInput( VirtualInput.InputEvent iev )
	{
		for ( int i = 0; i < m_dispatchers.length(); i ++ )
		{
			if ( m_dispatchers[i].onInputEvent( iev, m_modal ) )
			{
				return ;
			}
		}
	}

	// モーダル状態
	public void pushModal( int modal )
	{
		m_modalStack.push( m_modal ) ;
		if ( modal > m_modal )
		{
			m_modal = modal ;
		}
	}

	public void popModal( void )
	{
		if ( m_modalStack.length() > 0 )
		{
			m_modal = m_modalStack.pop() ;
		}
	}

	public int getCurrentModal( void )
	{
		return	m_modal ;
	}

	// プロファイル読み込み
	protected boolean loadProfile( void )
	{
		return	m_profile.loadDocument( PROFILE_PATH, new ParserErrorTracer() ) ;
	}

	// プロファイル保存
	public boolean saveProfile( void )
	{
		return	m_profile.saveDocument( PROFILE_PATH ) ;
	}

	// プロファイル数値取得
	public long getProfileInt( String strPath, long nDefault = 0 )
	{
		return	m_profile.getContentsAsInteger( "profile\\" + strPath, nDefault ) ;
	}
	
	public String getProfileString( String strPath )
	{
		return	m_profile.getContentsValue( "profile\\" + strPath ) ;
	}

	// プロファイル数値設定
	public void setProfileInt( String strPath, long nValue )
	{
		String	strElPath = ("profile\\" + strPath).getFileDirectoryPart() ;
		while ( strElPath.charLastCodeAt(0) == '\\' )
		{
			strElPath = strElPath.chopRight(1) ;
		}
		m_profile.createContentsElement( strElPath ).
			setAttrIntegerAs( strPath.getFileNamePart(), nValue ) ;
	}
}


