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

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

import static org.opengion.fukurou.util.StringUtil.isNull;

/**
 * GF91.GF92テーブルとJSPの変換オブジェクト
 *
 *
 * @author Takeshi.Takada
 *
 */
public final class JspConvertEntity {
	// 5.5.2.6 (2012/05/25) findbugs対応
	private static final String[] DBKEY = {"SYSTEM_ID","PGID","NMSYORI","SEQ","CLM","CLM_NAME","KBACCS",
											"MUST","DEFAULT_VAL","TABLE_NAME","ZOKUSEI","SETU",
											"NMSYS","NMPG","HPGID","USE_ORDER","AS_CLM","JOINTYPE","AS_TABLE","CLS_NAME"};

	// 5.1.1.0 (2009/12/01) データのアクセス用の配列番号のID
	private static final int PGID			= 1;		// COMMENTのみ
	private static final int NMSYORI		= 2;
 //	private static final int SEQ			= 3;		// 未使用
	private static final int CLM			= 4;
	private static final int CLM_NAME		= 5;		// 5.6.4.4 (2013/05/31) カラム名 追加
 //	private static final int KBACCS			= 6;		// 未使用
	private static final int MUST 			= 7;
	private static final int DEFAULT_VAL	= 8;
	private static final int TABLE_NAME		= 9;
	private static final int ZOKUSEI		= 10;
 //	private static final int SETU			= 11;		// 未使用
	private static final int NMSYS			= 12;		// COMMENTのみ
	private static final int NMPG			= 13;		// COMMENTのみ
 //	private static final int HPGID			= 14;		// 未使用
	private static final int USE_ORDER		= 15;
	private static final int AS_CLM			= 16;
	private static final int JOINTYPE		= 17;
	private static final int AS_TABLE		= 18;
	private static final int CLS_NAME		= 19;

	private final String _type;				// GF92.NMSYORI
	private final String _column_name;		// GF92.CLM
	private final String _clm_name_ja;		// 5.6.4.4 (2013/05/31) カラム名 追加(GF92にはなく、リソースから変換した名称)
	private final String _table_name;		// GF92.TABLE_NAME GF91.TABLE_NAME
	private final String _as_table_name;	// GF91.AS_TABLE
	private final String _as_column_name;	// GF92.AS_CLM
	private final String _default_value;	// GF92.DEFAULT_VAL
	private final String _remarks;			// GF92.ZOKUSEI
	private final String _must;				// GF92.MUST

	private final String _nmsys;			// GF90.NMSYS
	private final String _pgid;				// GF92.PGID
	private final String _nmpg	;			// GF90.NMPG

	private final String _use_order;		// GF92.USE_ORDER
	private final boolean _is_number;		// 
	private final String _join_type;		// GF92.JOIN_TYPE

	private final JspConvertEntity _join_column;

	/**
	 * ファクトリクラス
	 * QUERY、JOIN、CONST は、ZOKUSEIデータが 存在しないとき、作成しません。
	 * ここでは、null を返します。
	 *
	 * @param	data	(GF92.NMSYORI)
	 * @param	clmNo	カラム番号配列
	 *
	 * @return 新しく作成された JspConvertEntity
	 */
	public static JspConvertEntity newInstance( final String[] data, final int[] clmNo ) {
		final String nmSyori = data[clmNo[NMSYORI]];
		final String zokusei = data[clmNo[ZOKUSEI]];

		// 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
		return isNull( zokusei ) && ( "QUERY".equals(nmSyori) || "JOIN".equals(nmSyori) || "CONST".equals(nmSyori) )
					? null : new JspConvertEntity( data, clmNo ) ;

	}

	/**
	 * コンストラクタ
	 *
	 * @og.rev 5.6.4.4 (2013/05/31) カラム名 追加
	 *
	 * @param	data	データ配列
	 * @param	clmNo	カラムの配列番号
	 */
	private JspConvertEntity( final String[] data, final int[] clmNo ) {
		_type			=	data[clmNo[NMSYORI]];		// GF92.NMSYORI
		_table_name		=	data[clmNo[TABLE_NAME]];	// GF92.TABLE_NAME GF91.TABLE_NAME
		_as_table_name	=	data[clmNo[AS_TABLE]];		// GF91.AS_TABLE
		_column_name	=	data[clmNo[CLM]];			// GF92.CLM
		_clm_name_ja	=	data[clmNo[CLM_NAME]];		// 5.6.4.4 (2013/05/31) カラム名
		_as_column_name	=	data[clmNo[AS_CLM]];		// GF92.AS_CLM
		_default_value	=	data[clmNo[DEFAULT_VAL]];	// GF92.DEFAULT_VAL
		_remarks		=	data[clmNo[ZOKUSEI]];		// GF92.ZOKUSEI
		_must			=	data[clmNo[MUST]];			// GF92.MUST
		_use_order		=	data[clmNo[USE_ORDER]];		// GF92.USE_ORDER
		_is_number		=	"NUMBER".equals( data[clmNo[CLS_NAME]]);		// 
		_join_type		=	data[clmNo[JOINTYPE]];		// GF92.JOIN_TYPE

		_nmsys			=	data[clmNo[NMSYS]];			// GF90.NMSYS
		_pgid			=	data[clmNo[PGID]];			// GF92.PGID
		_nmpg			=	data[clmNo[NMPG]];			// GF90.NMPG

		if( "JOIN".equals(_type) ) {
			_join_column = createLeftTable( _remarks );
		}
		else {
			_join_column = null;
		}
	}

	/**
	 * コンストラクタ(通常利用していない)
	 * createLeftTable( String zokusei ) から呼び出す、内部だけで利用しているコンストラクタ
	 *
	 * @og.rev 5.6.4.4 (2013/05/31) カラム名 追加
	 *
	 * @param	type			処理名(GF92.NMSYORI)
	 * @param	table_name		テーブル名(GF92.TABLE_NAME,GF91.TABLE_NAME)
	 * @param	as_table_name	テーブル別名(GF91.AS_TABLE)
	 * @param	column_name		カラム名(GF92.CLM)
	 */
	private JspConvertEntity( final String type, final String table_name, final String as_table_name, final String column_name ) {
		_type			=	type;				// GF92.NMSYORI
		_table_name		=	table_name;			// GF92.TABLE_NAME GF91.TABLE_NAME
		_as_table_name	=	as_table_name;		// GF91.AS_TABLE
		_column_name	=	column_name;		// GF92.CLM
		_clm_name_ja	=	null;				// 5.6.4.4 (2013/05/31) カラム名
		_as_column_name	=	null;				// GF92.AS_CLM
		_default_value	=	null;				// GF92.DEFAULT_VAL
		_remarks		=	null;				// GF92.ZOKUSEI
		_must			=	null;				// GF92.MUST
		_use_order		=	null;				// GF92.USE_ORDER
		_is_number		=	false;				// 
		_join_type		=	null;				// GF92.JOIN_TYPE
		_join_column	=	null;
		_nmsys			=	null;				// GF90.NMSYS
		_pgid			=	null;				// GF92.PGID
		_nmpg			=	null;				// GF90.NMPG
	}

	/**
	 * データのアクセス用のカラム名配列を返します。
	 * これを利用して、カラム名の番号を取得し、JspConvertEntity#newInstance( String[],int[] ) の
	 * ２番目の引数に指定します。
	 *
	 * @og.rev 5.5.2.6 (2012/05/25) findbugs対応。JspConvertEntity.DBKEY を、JspConvertEntity.getDBKEY() に変更。
	 *
	 * @return	DBKEY配列のクローン
	 * @og.rtnNotNull
	 */
	public static String[] getDBKEY() {
		return DBKEY.clone();
	}

	/**
	 * データのタイプを取得。(GF92.NMSYORI)
	 *
	 * @return	データのタイプ
	 */
	public String getType(){
		return _type;
	}

	/**
	 * テーブル名を取得。(GF92.TABLE_NAME GF91.TABLE_NAME)
	 *
	 * @return	テーブル名
	 */
	public String getTableName(){
		return _table_name;
	}

	/**
	 * カラム名を取得。(GF92.CLM)
	 *
	 * @return	カラム名
	 */
	public String getColumnName(){
		return _column_name;
	}

	/**
	 * カラム名称を取得。
	 * 
	 * カラム名称は、GF92.CLM をキーにリソースを検索した結果の日本語になります。SELECT文のコメントに使います。
	 *
	 * @og.rev 5.6.4.4 (2013/05/31) カラム名 追加
	 *
	 * @return	カラム名称
	 */
	public String getColumnCommentName(){
		return _clm_name_ja;
	}

	/**
	 * テーブル名が先頭に付いたカラム名を取得。
	 *
	 * @return	カラム名(テーブル名付き)
	 * @og.rtnNotNull
	 */
	public String getFullColumnName(){
		String preffix = "";

		if( isNull( _as_table_name ) ) {
			preffix = _table_name;
		}
		else {
			preffix = _as_table_name;
		}
		//集計関数が利用されている場合は、関数別に特別な処理を実装する必要がある。
		//現在は、テーブル名やテーブル別名を付加せずにスルーする様に実装してあります。
		Matcher matcher = null;
		for( final JspEnumeration.TREATS_STRING_FUNCTIONS func : JspEnumeration.TREATS_STRING_FUNCTIONS.values()){
			final String k = func.toString();
			matcher = Pattern.compile("(((\\s*?)|\\()" + k + "(\\s+?))|(((\\s*?)|\\()"  + k.toLowerCase( Locale.JAPAN ) + "(\\s+?))").matcher( _column_name );
			if( matcher.find() ) {
				return func.update( _column_name ,  new String[]{ } );
			}
		}
		return preffix + "." + _column_name;
	}

	/**
	 * 結合先のカラム情報を取得。
	 *
	 * @return	結合先のカラム
	 */
	public JspConvertEntity getJoinColumn(){
		return _join_column;
	}

	/**
	 * テーブルに付ける別名を取得(GF91.AS_TABLE)。
	 *
	 * @return	テーブルに付ける別名
	 */
	public String getAsTableName(){
		return _as_table_name;
	}

	/**
	 * カラムに付ける別名を取得(GF92.AS_CLM)。
	 *
	 * @return	カラムに付ける別名
	 */
	public String getAsColumnName(){
		return _as_column_name;
	}

	/**
	 * Select句ですぐに利用可能なカラム名を取得。
	 *
	 * @return	カラム名
	 * @og.rtnNotNull
	 */
	public String getSelectPartColumnName(){
		if( isNull( _remarks ) || "DISP".equalsIgnoreCase( _remarks ) ) {
			if( isNull( _as_column_name ) ) {
				return getFullColumnName();
			}
			else {
				return getFullColumnName() + " as " + _as_column_name;
			}
		}
		else {
			if( isNull( _as_column_name ) ) {
				return _remarks + "(" + getFullColumnName() + ")";
			}
			else {
				return _remarks + "(" + getFullColumnName() + ") as " + _as_column_name;
			}
		}
	}

	/**
	 * From句ですぐに利用可能なカラム名を取得。
	 *
	 * @return	別名のついたテーブル名
	 */
	public String getFromPartTableName(){
		// 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
		return isNull( _as_table_name ) ? _table_name : _table_name + " " + _as_table_name;

	}

	/**
	 * 初期値を取得。(GF92.DEFAULT_VAL)
	 *
	 * @return	初期値
	 */
	public String getDefaultValue(){
		return _default_value;
	}

	/**
	 * 属性内容を取得。(GF92.ZOKUSEI)
	 *
	 * @return	属性内容
	 */
	public String getRemarks(){
		return _remarks;
	}

	/**
	 * 必須を取得。(GF92.MUST)
	 *
	 * @return	必須
	 */
	public String getMust(){
		return _must;
	}

	/**
	 * 並び替え設定か否かを判定(GF92.USE_ORDER)。
	 *
	 * @return	判定結果
	 */
	public String getUseOrder(){
		return _use_order;
	}

	/**
	 * 数値項目か否かを判定。
	 *
	 * @return	判定結果
	 */
	public boolean isNumber(){
		return _is_number;
	}

	/**
	 * システムの名称を取得します(GF90.NMSYS)。
	 *
	 * @return	名称
	 */
	public String getNmSys(){
		return _nmsys;
	}

	/**
	 * プログラムIDを取得します。(GF92.PGID)
	 *
	 * @return	プログラムID
	 */
	public String getPgid(){
		return _pgid;
	}

	/**
	 * プログラムの名称を取得します(GF90.NMPG)。
	 *
	 * @return	名称
	 */
	public String getNmpg(){
		return _nmpg;
	}

	/**
	 * 外部結合かを判定(GF92.JOIN_TYPE)。
	 *
	 * @return	判定結果
	 */
	public String getJoinType(){
		return _join_type;
	}

	/**
	 * 外部結合かを判定(GF92.JOIN_TYPE)。
	 *
	 * @param	zokusei	属性情報
	 *
	 * @return	外部結合のJspConvertEntityオブジェクト
	 */
	private JspConvertEntity createLeftTable( final String zokusei ){
		JspConvertEntity rgt = null;

		final String[] rgt_data = zokusei.split( "__" );
		if( rgt_data.length == 3 ){
			final String rgt_tbl		= rgt_data[1];
			final String rgt_as_tbl	= rgt_data[2].substring( 0 , rgt_data[2].indexOf('.') );
			final String rgt_clm		= rgt_data[2].substring( rgt_data[2].indexOf('.') + 1 );

			rgt = new JspConvertEntity( "JOIN",rgt_tbl,rgt_as_tbl,rgt_clm );
		}

		return rgt;
	}
}
