/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.hayabusa.filter;

import java.util.regex.Pattern ;
import java.util.regex.Matcher ;

/**
 * FileFilter で使用する、紙芝居用HTMLファイル作成時に内部文字列を変換するクラスです。
 *
 * @og.group フィルター処理
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class FileResponseTransform {
	private static final ChangeData[] data = new ChangeData[] {
//		 new ChangeData( null,"=\"/[^/]*/jsp/","=\"../" )							// 5.5.7.2 (2012/10/09) 廃止
//		,new ChangeData( null,"='/[^/]*/jsp/","='../" )								// 5.5.7.2 (2012/10/09) 廃止
		 new ChangeData( null,"\"/[^/]*/jsp/","\"../" )								// 5.5.7.2 (2012/10/09) マッチ条件を広げる
		,new ChangeData( null,"'/[^/]*/jsp/","'../" )								// 5.5.7.2 (2012/10/09) マッチ条件を広げる
		,new ChangeData( null,"=\"/[^/]*/[^/]*/ChartTemp/","=\"../ChartTemp/" )		// 5.5.2.5 (2012/05/21) JfreeChart関係の画像のアドレス変換
		,new ChangeData( null,"='/[^/]*/[^/]*/ChartTemp/","='../ChartTemp/" )		// 5.5.2.5 (2012/05/21) JfreeChart関係の画像のアドレス変換
		,new ChangeData( null,"=\"/[^/]*/help/","=\"../help/" )						// 5.5.2.5 (2012/05/21) JfreeChart関係の画像のアドレス変換
		,new ChangeData( null,"='/[^/]*/help/","='../help/" )						// 5.5.2.5 (2012/05/21) JfreeChart関係の画像のアドレス変換
//		,new ChangeData( "multiMenu.htm","=\"../../../mr/jsp/","=\"../" )
		,new ChangeData( null,"\\.jsp" ,".htm" )
		,new ChangeData( "forward.htm" ,"type=\"submit\"","type=\"button\"" )
		,new ChangeData( "forward.htm" ,"frame src=\"result","frame src=\"forward" )							// 5.6.3.4 (2013/04/26) result.jsp にフレームを使うパターン(３ペイン)
		,new ChangeData( "index.htm"   ,"frame src=\"forward.htm","frame src=\"../common/dummy.html" )
		,new ChangeData( "index.htm"   ,"frame src=\"entry.htm"  ,"frame src=\"../common/dummy.html" )			// 5.6.3.4 (2013/04/26) ENTRY系の特殊対応
		,new ChangeData( "indexRNW.htm","frame src=\"forward.htm","frame src=\"renew.htm" )
		,new ChangeData( "indexNW.htm" ,"frame src=\"query.htm"  ,"frame src=\"queryNW.htm" )
		,new ChangeData( "indexNW.htm" ,"frame src=\"entry.htm"  ,"frame src=\"../common/dummy.html" )			// 5.6.3.4 (2013/04/26) ENTRY系の特殊対応
//		,new ChangeData( "forward.htm" ,"onSubmit=\"return oneClick\\(\\);\"","onSubmit=\"return false;\"" )	// 5.5.7.2 (2012/10/09) 廃止
		,new ChangeData( null ,"onSubmit=\"return oneClick\\(\\);\"","onSubmit=\"return false;\"" )				// 5.5.7.2 (2012/10/09) 変更は、すべて行う。
		,new ChangeData( null ,"onSubmit=\"\""						,"onSubmit=\"return false;\"" )				// 5.6.3.4 (2013/04/26) onSubmit 引数のないケースへの対応
		,new ChangeData( null ,"src=\"\\.\\./common/option/ajaxSubmit\\.js\\?v=[^\"]+\"","" )					// 5.6.3.4 (2013/04/26) ajaxSubmit.js を削除
		// 4.3.3.0 (2008/10/01) 戻るリンクの対応
		,new ChangeData( "queryNW.htm" ,"=\"http://.*jsp/+?","=\"../" )
// 4.3.3.0 (2008/10/01) Matrixメニュー対応を一箇所に集めます。
//		,new ChangeData( "matrixMenu.htm" ,"result\\.htm\\?.*GAMENID=","" ) // マトリクスメニュー対応
//		,new ChangeData( "matrixMenu.htm" ,"&","/index.htm?" ) // マトリクスメニュー対応
		 // 漢字のボタンでは、後ろにショートカット文字が入る為、前方一致で挿入する。
	//	,new ChangeData( "forward.htm","value=\"追加","onClick=\"location.href='insert.htm'\" value=\"追加" )
	//	,new ChangeData( "forward.htm","value=\"複写","onClick=\"location.href='copy.htm'\"   value=\"複写" )
	//	,new ChangeData( "forward.htm","value=\"変更","onClick=\"location.href='modify.htm'\" value=\"変更" )
	//	,new ChangeData( "forward.htm","value=\"削除","onClick=\"location.href='delete.htm'\" value=\"削除" )
//		,new ChangeData( null		,"value=\"登録","onClick=\"location.href='entry.htm'\"  value=\"登録" )
	//	,new ChangeData( "query.htm","index.htm\\?command=RENEW"	,"indexRNW.htm?command=RENEW" )
	//	,new ChangeData( null		,"index.htm\\?command=NEW"		,"indexNW.htm?command=NEW" )
		,new IndexMatrixMenuData()		// 4.3.3.0 (2008/10/01) マトリクスメニュー対応
		,new IndexChangeData()
		,new HrefChangeData()
		,new NoTranHrefChangeData()		// 5.6.3.4 (2013/04/26) entry.htm に "noTransitionUrl" が存在するときの処理。
	};

//	private final ChangeData entryChange = new ChangeData( "query.htm" ,"action=\"forward.htm\"","action=\"entry.htm\"" );

	/**
	 * 変換を行います。
	 * 実際には、各内部クラスのメソッドで処理を行います。
	 *
	 * @param	file 	対象ファイル名
	 * @param	inStr	対象データ
	 *
	 * @return	変換後データ
	 */
	public String replace( final String file,final String inStr ) {
		String rtnStr = inStr;

		// query 画面で、登録コマンドが発行された場合の特殊処理
		if( file.indexOf( "query.htm" ) >= 0 && inStr.indexOf( "name=\"command\" value=\"登録" ) >= 0 ) {
			rtnStr = inStr.replace( "forward.jsp","entry.htm" );
		}

		for( int i=0; i<data.length; i++ ) {
			rtnStr = data[i].replace( file,rtnStr );
		}
		return rtnStr;
	}

	/**
	 * 個々の変換データを管理している、データクラス
	 * このクラスは、不変クラスのため、マルチスレッドでの同時使用に対して、安全です。
	 *
	 */
	private static class ChangeData {
		private final String filename ;
		private final String org ;
		private final String rep ;

		/**
		 * デフォルトコンストラクター
		 * サブクラス作成用に用意された、コンストラクタ。このクラス自身には不要。
		 */
		public ChangeData() {
			this( null,null,null );
		}

		/**
		 * コンストラクター
		 * 初期設定を行います。
		 * 対象ファイル名は、処理を実行するかどうかの判定に使用します。
		 * 指定の文字列が含まれているかどうかで判定します。
		 * null の場合は、すべてのファイルを対象にします。
		 *
		 * @param	filename	対象ファイル名
		 * @param	org	変換元データ
		 * @param	rep	変換後データ
		 */
		public ChangeData( final String filename,final String org,final String rep ) {
			this.filename = filename;
			this.org = org;
			this.rep = rep;
		}

		/**
		 * 実際に変換を行うメソッド
		 * 内部的には、入力文字列.replaceAll( 変換元,変換後 ) メソッドを実行します。
		 *
		 * @param	file	対象ファイル名
		 * @param	inStr	対象データ
		 * @return	変換後データ
		 */
		public String replace( final String file,final String inStr ) {
			String rtnStr = inStr;
			if( filename == null || file.indexOf( filename ) >= 0 ) {
				rtnStr = inStr.replaceAll( org,rep );
			}
			return rtnStr;
		}
	}

	/**
	 * マトリクスメニュー対応のデータ置き換えクラスです。
	 * このクラスは、不変クラスのため、マルチスレッドでの同時使用に対して、安全です。
	 *
	 * @og.rev 4.3.3.0 (2008/10/01) Matrixメニュー対応
	 * @og.rev 5.5.2.5 (2012/05/21) TopMenuTag の ONELEVEL メニューリンク対応
	 */
	private static final class IndexMatrixMenuData extends ChangeData {
		public String replace( final String file,final String inStr ) {
			String rtnStr = inStr;

			if( file.indexOf( "matrixMenu" ) >= 0 ) {
//				rtnStr = inStr.replaceAll( "../index.htm\\?[^>]*GAMENID=([^&]*)&","../jsp/index$1.htm?" );
				rtnStr = inStr.replaceAll( "../index.htm\\?[^>]*GAMENID=([^&]*)&","../$1/index.htm?" );
				rtnStr = rtnStr.replaceAll( "=\"../../../mr/jsp/","=\"../" );
				rtnStr = rtnStr.replaceAll( "matrixMenu.htm\\?[^>]*group=([^&]*)&","matrixMenu$1.htm?" );
			}
			else if( file.indexOf( "jsp/index" ) >= 0 ) {
				rtnStr = inStr.replaceAll( "menu/multiMenu.htm\\?[^>]*GAMENID=([^&]*)&","../jsp/menu$1.htm?" );
			}
			else if( file.indexOf( "jsp/menu" ) >= 0 ) {
				rtnStr = inStr.replaceAll( "matrixMenu.htm","../menu/matrixMenu.htm" );
			}
			// 5.5.2.5 (2012/05/21) TopMenuTag の ONELEVEL メニューリンク対応
			else if( file.indexOf( "query" ) >= 0 ) {
				rtnStr = inStr.replaceAll( "../result.htm\\?GAMENID=([^\"]*)\"","../$1/index.htm\"" );
			}
			return rtnStr;
		}
	}

	/**
	 * index.htm のコマンド単位のファイル名の置き換えクラスです。
	 * このクラスは、不変クラスのため、マルチスレッドでの同時使用に対して、安全です。
	 *
	 */
	private static final class IndexChangeData extends ChangeData {
		// <a href="aaaa/index.htm?command=RENEW&GAMENID=bbbb 形式とマッチし、index.htm 部分を前方参照します。
		private static final Pattern PTN1 = Pattern.compile( "index.htm\\?[^\"]*command=(NEW|RENEW)" );

		public String replace( final String file,final String inStr ) {
			String rtnStr = inStr;
	//		if( file.indexOf( "query" ) >= 0 ) {		// query の場合
				Matcher mch = PTN1.matcher( rtnStr );
				int adrs = 0;
				while( mch.find( adrs ) ) {
					int indx = mch.start() ;
					String cmd = mch.group(1);		// command=(NEW|RENEW) 部分の()内文字列
					// index.htm 文字列部に、NW または RNW を追加し、indexNW.html を作成する。
					if( "NEW".equalsIgnoreCase( cmd ) ) {
						rtnStr = rtnStr.substring(0,indx+5) + "NW" + rtnStr.substring(indx+5) ;
					}
					else if( "RENEW".equalsIgnoreCase( cmd ) ) {
						rtnStr = rtnStr.substring(0,indx+5) + "RNW" + rtnStr.substring(indx+5) ;
					}
					adrs = mch.end() ;
					mch.reset( rtnStr );
				}
	//		}
			return rtnStr;
		}
	}

	/**
	 * コマンド転送先を、onClick="location.href=XXX" で指定するように、変換します。
	 * <input type="hidden" name="hX_複写(C)" value="copy.htm" /> を見つけ、
	 * 前方参照で、複写(C) と、copy.htm を取り出します。
	 * その後、<input name="command" value="複写(C)" という文字列をキーに、
	 * <input name="command" onClick="location.href='copy.htm'" value="複写(C)" という
	 * 文字列に置き換えます。
	 * これにより、ボタンを押したときに、ボタンごとに異なる画面に遷移します。
	 * 前提条件として、下記の項目を満たしておく必要がある。
	 *   ・form には、onSubmit="return false;" を記述し、フォームを無効化しておく。
	 *   ・input タグの type="submit" を、type="button" にしておく。(ボタンイベント)
	 *   ・query.htm 以外のファイルのみ適用。location.href では、フレームのtarget指定
	 *     まで行っていない。
	 *   ・上と同意で、query.htm の登録時処理は、別に行う。
	 *
	 * @og.rev 5.5.2.5 (2012/05/21) update.jsp に出力されるファイルを、コマンド名.htm に出力するように機能追加
	 */
	private static final class HrefChangeData extends ChangeData {
		private static final String PTN1 = "<input type=\"hidden\" name=\"hX_([^\"]*)\" value=\"([^\"]*.htm)" ;
//		private static final Pattern ptn = Pattern.compile( PTN1 );
		private static final Pattern ptnObj1 = Pattern.compile( PTN1 );

//		private static final String PTN2 = "<input name=\"command\"" ;
//		private static final String PTN3 = "<input name=\"command\" onClick=\"location.href='" ;
//		private static final String PTN4 = "<input name=\"command\" onClick=\"parent.location.href='" ;

		// 5.5.7.2 (2012/10/09) 定数名の変更
		private static final String ORG  = "<input name=\"command\"" ;
		private static final String SELF = "<input name=\"command\" onClick=\"location.href='" ;
		private static final String PRNT = "<input name=\"command\" onClick=\"parent.location.href='" ;
		private static final String TOP  = "<input name=\"command\" onClick=\"top.location.href='" ;

		// 5.5.7.2 (2012/10/09) formのtargetを取得。location.href に利用する。
		private static final String PTN2 = "<form .*target=\"([^\"]*)\"" ;
		private static final Pattern ptnObj2 = Pattern.compile( PTN2 );

		/**
		 * コマンド転送先を、onClick="location.href=XXX" で指定するように、変換します。
		 * <input type="hidden" name="hX_複写(C)" value="copy.htm" /> を見つけ、
		 * 前方参照で、複写(C) と、copy.htm を取り出します。
		 * その後、<input name="command" value="複写(C)" という文字列をキーに、
		 * <input name="command" onClick="location.href='copy.htm'" value="複写(C)" という
		 * 文字列に置き換えます。
		 *
		 * @og.rev 5.5.2.5 (2012/05/21) update.jsp に出力されるファイルを、コマンド名.htm に出力するように機能追加
		 * @og.rev 5.5.7.2 (2012/10/09) 定数名の変更。formのtargetを加味した、location.href を作成する。
		 *
		 * @param	file	対象ファイル名
		 * @param	inStr	対象データ
		 * @return	変換後データ
		 */
		public String replace( final String file,final String inStr ) {
			String rtnStr = inStr;
			if( file.indexOf( "query.htm" ) < 0 ) {		// query.htm 以外の場合
				// 5.5.7.2 (2012/10/09) formのtargetを加味した、location.href を作成する。
				Matcher mch2 = ptnObj2.matcher( rtnStr );
				String ptnHref = SELF;			// 標準は、location.href
				if( mch2.find() ) {
					String frmTgt = mch2.group(1);
					if( "CONTENTS".equals( frmTgt ) )  { ptnHref = PRNT; }
					else if( "_top".equals( frmTgt ) ) { ptnHref = TOP;  }
					else if( !"RESULT".equals( frmTgt ) && frmTgt != null ) {
						ptnHref = "<input name=\"command\" onClick=\"parent." + frmTgt + ".location.href='" ;
					}
				}

//				Matcher mch = ptn.matcher( rtnStr );
				Matcher mch = ptnObj1.matcher( rtnStr );
				int adrs = 0;
				while( mch.find( adrs ) ) {
					String cmd = mch.group(1);
					if( !cmd.endsWith( "CMD" ) ) {
						String val = mch.group(2);
//						String str1 = PTN2 + " value=\"" + cmd ;
						String str1 = ORG + " value=\"" + cmd ;
						String str2 ;

						if( val != null && val.startsWith( "../" ) ) {
//							str2 = PTN4 + val + "'\" value=\"" + cmd ;
							str2 = PRNT + val + "'\" value=\"" + cmd ;
						}
						// 5.5.2.5 (2012/05/21) update.jsp に出力されるファイルを、コマンド名.htm に出力するように機能追加
						else if( val != null && val.startsWith( "update" ) ) {
//							str2 = PTN3 + cmd + ".htm'\" value=\"" + cmd ;
							str2 = ptnHref + cmd + ".htm'\" value=\"" + cmd ;
						}
						else {
//							str2 = PTN3 + val + "'\" value=\"" + cmd ;
							str2 = ptnHref + val + "'\" value=\"" + cmd ;
						}
						rtnStr = rtnStr.replace( str1,str2 );
					}
					adrs = mch.end();
					mch.reset( rtnStr );
				}
			}
			return rtnStr;
		}
	}

	/**
	 * 雛形自動作成 で、useAjaxSubmit="true" の対策
	 *
	 * update.jsp で、useAjaxSubmit="true" の場合、entry.htm は、update.jsp の 
	 * JavaScriptでforward されるため、雛形には、HTMLの結果は出力されません。
	 * (result.jsp に出力されます。)
	 * そこで、雛形作成時には、entry.htm にJavaScriptを入れて、forward させます。
	 * このクラスは、不変クラスのため、マルチスレッドでの同時使用に対して、安全です。
	 * 挿入するのは、BODYタグの最後です。BODYタグがなければ、最後に追加します。
	 *
	 * @og.rev 5.6.3.4 (2013/04/26) entry.htm に "noTransitionUrl" が存在するときの処理。
	 */
	private static final class NoTranHrefChangeData extends ChangeData {
		private static final String BODY_END   = "</body>" ;
		private static final String APPEND_JS  = "<script type=\"text/javascript\" src=\"../common/option/noTranHref.js\" ><!-- --></script>" ;

		public String replace( final String file,final String inStr ) {
			String rtnStr = inStr;
			// entry.jsp で、かつ noTransitionUrl 文字列を含む場合のみ
			if( file.indexOf( "entry" ) >= 0 && inStr.indexOf( "noTransitionUrl" ) >= 0 ) {
				int adrs = inStr.indexOf( BODY_END );
				if( adrs > 0 ) {		// </body> タグが存在した場合は、その直前に挿入する。
					rtnStr = inStr.substring( 0,adrs ) + APPEND_JS + inStr.substring( adrs ) ;
				}
				else {					// 存在しない場合は、最後に挿入する。
					rtnStr = inStr + APPEND_JS ;
				}
			}
			return rtnStr;
		}
	}
}
