/*
 * 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.taglib;

import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.hayabusa.html.ViewStackTableParam;
import org.opengion.hayabusa.resource.CalendarData;
import org.opengion.hayabusa.resource.CalendarFactory;
import static org.opengion.fukurou.util.StringUtil.nval ;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale ;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

/**
 * スタックガントチャート用のヘッダー情報(日付け等)を出力するタグです。
 *
 * スライドバー式ガントチャートの表示時に、スライドバーの長さと日付とを関連付けるための
 * ヘッダー情報(日付け等)を出力します。スライドバーと日付間隔は、zoom 属性によって、
 * CSSファイルのフォントサイズと、JavaScript の内部配列に依存しています。
 * また、このヘッダーでは、日付の休日情報を、カレンダテーブルを参照することで取得しています。
 * calDB 属性への指定は、CalendarFactory で生成されるテーブル名(CalendarDataの実装クラス)
 * を指定します。
 *
 * @og.formSample
 * ●形式：&lt;og:ganttHeader startDate="…" endDate="…" zoom="[…]" /&gt;
 * ●body：なし
 *
 * ●Tag定義：
 *   &lt;og:stackHeader
 *       startDate          【TAG】表示開始日付けを設定します(yyyyMMdd または、yyyyMMddHHmm 形式)
 *       endDate            【TAG】表示最終日を設定します(yyyyMMdd または、yyyyMMddHHmm 形式)
 *       zoom               【TAG】ズームを設定します(初期値:DAY)
 *       calDB              【TAG】DB検索するＤＢを指定します
 *       arg1               【TAG】ＤＢ検索する場合の第１のキーを指定します
 *       arg2               【TAG】ＤＢ検索する場合の第２のキーを指定します
 *       arg3               【TAG】ＤＢ検索する場合の第３のキーを指定します
 *       arg4               【TAG】ＤＢ検索する場合の第４のキーを指定します
 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
 *   /&gt;
 *
 * ●使用例
 *    参照：jsp/GANTT06 フォルダ
 *
 *     &lt;og:ganttHeader
 *         startDate = "{&#064;VIEW_DATE}"
 *         endDate   = "{&#064;DYMAX}"
 *         zoom      = "{&#064;VIEW_ZOOM}"
 *         calDB     = "GE13"
 *         arg1      = "{&#064;CDJGS}"
 *         arg2      = ""
 *     /og:ganttHeader&gt;
 *
 *
 * ●定義・解説
 *   stackedGanttView用のヘッダー作成タグです。
 *   
 *   表示のタイプ(zoom)は
 *   DAY（日単位）、WEEK（週単位）、MONTH(月単位）
 *   です。
 *   
 *
 * ヘッダー部のカレンダに、事業所カレンダを使用することが出来ます。
 * 使用する場合は、事業所コード( arg1 )属性のセットと、システムパラメータ の
 * USE_CALENDAR_DATABASE 属性を true に設定しておく必要があります。
 * このフラグは、事業所カレンダテーブル(GE13)を使用するために必要です。
 * 
 * zoom="WEEK"(週単位) を使用の週数の計算は、年初の最初の７日間が含まれる週を、第１週と数えます。
 *
 *
 *
 * @og.rev 5.5.7.0 (2012/10/01) 新規作成
 * @og.group 画面部品
 *
 * @version  5.0
 * @author	Takahashi Masakazu
 * @since    JDK5.0,
 */
public class StackHeaderTag extends CommonTagSupport {
	//* このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "5.0.0 (2012/10/01)" ;

	private static final long serialVersionUID = 4000 ;	// 4.0.0 (2005/01/31)

	private String	startDate	= null;
	private String	endDate		= null;
	private String	zoom		= ViewStackTableParam.STACK_ZOOM_DAY;
	private String	daySpan		= "0700-2000";	// １日の時間範囲(HHmm-HHmm) 
	private String	calDB	= null;	// DB検索するＤＢのキー
	private String	arg1	= null;	// DB検索の第１キー(事業所コードなど)
	private String	arg2	= null;	// DB検索の第２キー(ＷＣコードなど)
	private String	arg3	= null;	// DB検索の第３キー(事業所コードなど)
	private String	arg4	= null;	// DB検索の第４キー(ＷＣコードなど)
	private String 	calStr	= null; // 出勤日・休日のフラグ
	
	private ArrayList<String[]> calList = null; // 5.5.8.3 (2012/11/17) 開始、休日判定、次枠開始

	//private static final boolean USE_MONTH_DATE	= HybsSystem.sysBool( "USE_GANTTHEADER_MONTH_DATE" );


	private static final String BLUE_COLOR	= "<span style=\"color:Blue;\">" ;
	private static final String RED_COLOR	= "<span style=\"color:Red;\">" ;
	private static final String END_SPAN	= "</span>" ;
	private static final String BR			= "<br/>" ;

	/**
	 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
	 *
	 * @og.rev 5.5.8.3 (2012/11/17) 枠のListをObjectとしてviewformに渡す。
	 * @og.rev 5.5.9.0 (2012/12/03) ObjectではなくArrayList渡し
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doEndTag() {
		debugPrint();		// 4.0.0 (2005/02/28)
		if( startDate == null || endDate == null ||
			 startDate.length() < 8 || endDate.length() < 8 ) {
				String errMsg = "開始日と終了日は、どちらも yyyyMMdd 形式で、必須です。"
						+ " startDate=[" + startDate + "] endDate=[" + endDate + "]" ;
				throw new HybsSystemException( errMsg );
		}
		
		ViewFormTag viewform = (ViewFormTag)findAncestorWithClass( this,ViewFormTag.class );

		String header = "";
		if( ViewStackTableParam.STACK_ZOOM_MONTH.equals(zoom) ) {
			header = makeZoom( "yy/       "     ,"MM        "     ,  1 ,true ) ; 
		}
		else if( ViewStackTableParam.STACK_ZOOM_WEEK.equals(zoom) ) { // 月曜開始にはしていない
			header = makeZoom( "ww'W'   "      ,"MM/dd " ,7 ,false ) ;  
		}
		else{
				header = makeZoom( "MM/   "     ,"dd(EE)"     ,  1 ,false ) ; 
		}
		
		// 5.5.9.0 (2012/12/03) objectからArrayList
		// 5.5.8.3 (2012/11/17)
//		Map<String,Object> param = new HashMap<String,Object>();
//		param.put( ViewStackTableParam.STACK_CAL_KEY, (String[][])calList.toArray(new String[][]{}) );
//		viewform.setViewObject( param );
		viewform.setViewArrayList( calList ); 

		jspPrint( header );
		jspPrint(makeHeaderData()); 
		return(EVAL_PAGE);
	}

	/**
	 * タグリブオブジェクトをリリースします。
	 *
	 * キャッシュされて再利用されるので、フィールドの初期設定を行います。
	 *
	 */
	@Override
	protected void release2() {
		super.release2();
		startDate	= null;
		endDate		= null;
		zoom		= ViewStackTableParam.STACK_ZOOM_DAY;
		calDB		= null;	// DB検索するＤＢのキー
		arg1		= null;	// DB検索の第１キー(事業所コードなど)
		arg2		= null;	// DB検索の第２キー(ＷＣコードなど)
		arg3		= null;	// DB検索の第３キー
		arg4		= null;	// DB検索の第４キー
		calStr		= null;
		daySpan		= "0700-2000";	// 開始時刻(HHmm-HHmm) 
		calList = null;
	}

	/**
	 * 上段・下段のフォーマットと、加算日に応じたヘッダー文字列を作成します。
	 *
	 *
	 * @param	upper	上段の表示フォーマット
	 * @param	lower	下段の表示フォーマット
	 * @param	add  	表示日付けの加算
	 * @param	useMonth	月日表示でなく、月表示のみにする
	 * 
	 * @og.rev 5.5.8.3 (2012/11/17) 内部積上げ対応
	 *
	 * @return  ヘッダー文字列
	 */
	private String makeZoom( final String upper ,final String lower ,final int add, final boolean useMonth ) {
		DateFormat format1 = new SimpleDateFormat( upper,Locale.JAPAN );
		DateFormat format2 = new SimpleDateFormat( lower,Locale.JAPAN );
		
		DateFormat formatCal = new SimpleDateFormat( "yyyyMMdd",Locale.JAPAN );
		
		// 5.5.8.3 (2012/11/17) スタック用にカレンダー枠の情報を保持
		calList = new ArrayList<String[]>(); 
		String[] calArray = new String[3]; // 開始、休日判定、次枠開始

		Calendar str = HybsSystem.getCalendar( startDate );
		Calendar end = HybsSystem.getCalendar( endDate );

		// 週単位の場合の特殊処理。
		// 年の第一週を完全な一週間分(７日間)として、設定します。
		if( ViewStackTableParam.STACK_ZOOM_WEEK.equals( zoom ) ) {
			str.setMinimalDaysInFirstWeek( 7 );
			format1.setCalendar( str );
			format2.setCalendar( str );
		}
		
		if( useMonth ){
			str.set(Calendar.DAY_OF_MONTH, 1);
			startDate = new SimpleDateFormat( "yyyyMMdd",Locale.JAPAN ).format(str.getTime());
		}
		

		StringBuilder buf1   = new StringBuilder( HybsSystem.BUFFER_SMALL );
		StringBuilder buf2   = new StringBuilder( HybsSystem.BUFFER_SMALL );
		StringBuilder bufcal = new StringBuilder( HybsSystem.BUFFER_SMALL ); 

		//  カレンダDB検索機能
		CalendarData calData = CalendarFactory.getCalendarData( calDB,arg1,arg2,arg3,arg4 );

		boolean modifyFlag = false;
		while( str.before( end ) ) {
			Date dt = str.getTime();
			calArray[0] = formatCal.format( dt ); // 5.5.8.3 (2012/11/17)

			// 本日は、青色で示します。
			if( calData.isContainedToday( str,add ) ) {
				buf1.append( BLUE_COLOR );
				buf2.append( BLUE_COLOR );
				bufcal.append( "0" ); 
				calArray[1] = "0"; // 5.5.8.3 (2012/11/17)
				modifyFlag = true;
			}

			// 休日判断を、事業所カレンダを使用
			// modifyFlag が立っていない場合 を条件に追加します。
			if( !modifyFlag && add == 1 && calData.isHoliday( str ) && !useMonth) {
				buf1.append( RED_COLOR );
				buf2.append( RED_COLOR );
				bufcal.append( "1" ); // 休日
				calArray[1] = "1"; // 5.5.8.3 (2012/11/17)
				modifyFlag = true;
			}

			buf1.append( format1.format( dt ) );
			buf2.append( format2.format( dt ) );

			if( modifyFlag ) {
				buf1.append( END_SPAN );
				buf2.append( END_SPAN );
				modifyFlag = false;
			}
			else{
				bufcal.append( "0" ); 
				calArray[1] = "0"; // 5.5.8.3 (2012/11/17)
			}
			
			// カレンダーを進める
			if( useMonth ){
				str.add(Calendar.MONTH, add);
			}
			else{
				str.add(Calendar.DATE, add);
			}
			
			dt = str.getTime();
			calArray[2] = formatCal.format( dt ); // 5.5.8.3 (2012/11/17)
			calList.add( calArray.clone() );
		}
		calStr = bufcal.toString(); 

		return "<pre>" + buf1.toString() + BR + buf2.toString() + "</pre>";
	}

	/**
	 * Headerの属性情報をdisplay:noneで作成します。
	 * ganttHeaderというタグで、id,class共にganttHeaderDataという名称で出力します。
	 * （ganttHeaderと同じ形式での出力）
	 *
	 * @og.rev 5.5.4.0 (2012/07/02) 新規作成
	 *
	 * @return  ヘッダー情報の文字列
	 */
	private String makeHeaderData() {

		StringBuilder buf1 = new StringBuilder( HybsSystem.BUFFER_SMALL );

		buf1.append( "\n" )
			.append( "<ganttHeader style='display:none' id='ganttHeaderData' class='ganttHeaderData' " )
			.append( "startDate='" ).append( startDate ).append( "' " )
			.append( "endDate='" ).append( endDate ).append( "' " )
			.append( "zoom='" ).append( zoom ).append( "' " )
			.append( "calDB='" ).append( calStr ).append( "' " )
			.append( "daySpan='" ).append( daySpan ).append( "' " )
			.append( " />" );

		return buf1.toString();
	}

	/**
	 * 【TAG】表示開始日付けを設定します(yyyyMMdd または、yyyyMMddHHmm 形式)。
	 *
	 * @og.tag
	 * この値は、ヘッダーの先頭に表示する日付けを指定します。
	 * 通常の日付ヘッダーの場合は、 yyyyMMdd 形式ですが、時間ヘッダーの場合は、
	 * yyyyMMddHHmm 形式で指定します。
	 *
	 * @param	val 表示開始日付(yyyyMMdd または、yyyyMMddHHmm 形式)
	 */
	public void setStartDate( final String val ) {
		startDate = nval( getRequestParameter( val ),startDate );
	}

	/**
	 * 【TAG】表示最終日を設定します(yyyyMMdd または、yyyyMMddHHmm 形式)。
	 *
	 * @og.tag
	 * 表示開始日から終了日までを、zoom 変数の値に応じてヘッダーラベルを
	 * 作成します。
	 * 通常の日付ヘッダーの場合は、 yyyyMMdd 形式ですが、時間ヘッダーの場合は、
	 * yyyyMMddHHmm 形式で指定します。
	 *
	 * @param	val 表示最終日(yyyyMMdd または、yyyyMMddHHmm 形式)
	 */
	public void setEndDate( final String val ) {
		endDate = nval( getRequestParameter( val ),endDate );
	}

	/**
	 * 【TAG】ズームを設定します(初期値:DAY)。
	 *
	 * @og.tag
	 * CSSファイルでの定義と、背景画像との関係で、作成します。
	 * ズーム変数は、CSSファイルや、JavaScript、PL/SQLと連動して、日付間隔
	 * あたりのピクセルを計算して、ヘッダーを作成しています。
	 * また、現在の標準的なクライアントでの換算になる為、高解像度のディスプレイや
	 * 特殊設定された環境では、表示間隔にずれが発生する可能性があります。
	 * DAY,WEEK,MONTHの３つの設定が可能です。
	 *
	 *
	 * @param	val ズーム変数(MONTH,WEEK,DAY)
	 */
	public void setZoom( final String val ) {
		zoom = nval( getRequestParameter( val ),zoom );
	}

	/**
	 * 【TAG】DB検索するＤＢを指定します。
	 *
	 * @og.tag
	 * カレンダデータは、システムパラメータ の CalendarQuery_**** で
	 * 指定する、CalendarQuery クラスの QUERY 文で、実際に取得します。
	 * 上記の **** 部分に、ここで指定するキーワードを設定して置きます。
	 * 通常は、データベースID (GE13, ZY01 など)を指定します。
	 * このキーワードに対する実装クラスを、先のシステムパラメータ の
	 * キーワード以下に記述しておきます。
	 *
	 *
	 * @param	db	DB検索するＤＢを指定
	 */
	public void setCalDB( final String db ) {
		calDB = nval( getRequestParameter( db ),calDB );
	}

	/**
	 * 【TAG】ＤＢ検索する場合の第１のキーを指定します。
	 *
	 * @og.tag
	 * 例えば、GE13, ZY01 では、事業所コード(CDJGS)を設定します。
	 * この値は、システムパラメータ の CalendarQuery_**** で
	 * 指定する、CalendarQuery クラスの QUERY 文に依存します。
	 * データベース定義の統一を望むところです。
	 *
	 *
	 * @param	arg DB検索の第１キー(事業所コードなど)
	 */
	public void setArg1( final String arg ) {
		arg1 = nval( getRequestParameter( arg ),arg1 );
	}

	/**
	 * 【TAG】ＤＢ検索する場合の第２のキーを指定します。
	 *
	 * @og.tag
	 * 例えば、TP652 では、事業部コード(CDJGS) と 物件工程コード(CDKTEI)の２つの
	 * キーで、カレンダを取得します。(＋年月で、行を決定)
	 * 引数が、２つまで対応しています
	 * この値は、システムパラメータ の CalendarQuery_**** で
	 * 指定する、CalendarQuery クラスの QUERY 文に依存します。
	 * データベース定義の統一を望むところです。
	 *
	 *
	 * @param	arg DB検索の第２キー(ＷＣコードなど)
	 */
	public void setArg2( final String arg ) {
		arg2 = nval( getRequestParameter( arg ),arg2 );
	}

	/**
	 * 【TAG】ＤＢ検索する場合の第３のキーを指定します。
	 *
	 * @og.tag
	 * この値は、システムパラメータ の CalendarQuery_**** で
	 * 指定する、CalendarQuery クラスの QUERY 文に依存します。
	 * データベース定義の統一を望むところです。
	 *
	 *
	 * @param	arg DB検索の第３キー
	 */
	public void setArg3( final String arg ) {
		arg3 = nval( getRequestParameter( arg ),arg3 );
	}

	/**
	 * 【TAG】ＤＢ検索する場合の第４のキーを指定します。
	 *
	 * @og.tag
	 * この値は、システムパラメータ の CalendarQuery_**** で
	 * 指定する、CalendarQuery クラスの QUERY 文に依存します。
	 * データベース定義の統一を望むところです。
	 *
	 *
	 * @param	arg DB検索の第４キー
	 */
	public void setArg4( final String arg ) {
		arg4 = nval( getRequestParameter( arg ),arg4 );
	}

	/**
	 * このオブジェクトの文字列表現を返します。
	 * 基本的にデバッグ目的に使用します。
	 *
	 * @return このクラスの文字列表現
	 */
	@Override
	public String toString() {
		return org.opengion.fukurou.util.ToString.title( this.getClass().getName() )
				.println( "VERSION"			,VERSION		)
				.println( "startDate"		,startDate		)
				.println( "endDate"			,endDate		)
				.println( "zoom"			,zoom			)
				.println( "calDB"			,calDB			)
				.println( "arg1"			,arg1			)
				.println( "arg2"			,arg2			)
				.println( "arg3"			,arg3			)
				.println( "arg4"			,arg4			)
				.println( "Other..."		,getAttributes().getAttribute() )
				.fixForm().toString() ;
	}
}
