/*
 * 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.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Locale;

import static org.opengion.fukurou.util.HybsConst.BUFFER_MIDDLE;	// 6.1.0.0 (2014/12/26) refactoring

/**
 * JSPの生成・データ取り込み処理で必要な列挙型をまとめたクラス。
 *
 * 主にキーワード管理とプログラム中のswitch文の削減を目的として作成。
 *
 *
 * @author Takeshi.Takada
 *
 */
public class JspEnumeration {
	/**
	 * GROUP BY句を必要とする関数を列挙します。
	 *
	 * contains、searchと言った独自メソッドも実装しています。
	 *
	 */
	public static enum GROUPING_FUNCTIONS { MAX , MIN , SUM , COUNT  ;

		/**
		 * 与えられた文字が自身に列挙された値の何れかと一致するか検証する。
		 * 一致する場合に真を返す。
		 * 一致しない場合に偽を返す。
		 *
		 * @param arg	引数
		 * @return	検証の結果
		 */
		public static boolean contains( final String arg ) {
			for( final GROUPING_FUNCTIONS fnc : values() ){
				if( arg.equalsIgnoreCase( fnc.toString() ) ){
					return true;
				}
			}
			return false;
		}

		/**
		 * 自身に列挙されている値が与えられた文字列に含まれているか検証する。
		 * 含まれている場合は真を返す。
		 * 含まれていない場合は偽を返す。
		 *
		 * @param arg	引数
		 * @return	検証の結果
		 */
		public static boolean search( final String arg ) {
			final String argU = arg.toUpperCase(Locale.JAPAN);

			for( final GROUPING_FUNCTIONS fnc : values() ){
				if( argU.indexOf( fnc.toString() ) > -1 ) {
					return true;
				}
			}
			return false;
		}
	}

	/**
	 * データ上はただの文字列として扱う関数を列挙します。
	 * (注：現在、列挙中の関数はOracleの内容です。)
	 *
	 */
	public static enum TREATS_STRING_FUNCTIONS {
		CASE ,
		CEIL , ROUND , FLOOR , TRUNC , MOD , CHR , CONCAT , SUBSTR , INITCAP ,
		SUBSTRB , LOWER , TRIM , LPAD   , LTRIM , UPPER , REPLACE , USER , RPAD ,
		ASCII , LENGTH , LENGTHB , INSTR , POSITION , INSTRB  , ADD_MONTHS , DAYOFMONTH ,
		MONTHNAME , TIMESTAMPADD , CURDATE , DAYOFWEEK , MONTHS_BETWEEN  , TIMESTAMPDIFF ,
		CURRENT_DATE  , DAYOFYEAR , NEXT_DAY  , CURRENT_TIME  , HOUR , NOW , WEEK , CURRENT_TIMESTAMP  ,
		LAST_DAY  , YEAR , CURTIME , MINUTE , SECOND , DAYNAME , MONTH , SYSDATE  , CAST , AVG   ,
		CONVERT , DATABASE  , TO_CHAR  , DECODE , TO_NUMBER  , EXTRACT , TO_DATE  , GREATEST , STDDEV ,
		INTERVAL , VARIANCE , LEAST , LOCATE , NVL  ;

		/**
		 * 関数の内容に第二引数の内容を付加する処理を実装します
		 * 第二引数の内容
		 * 0:カラムへ付与するテーブル名(テーブル別名)
		 *
		 * @param column カラム名
		 * @param args 引数配列(可変長引数)
		 * @return	関数を更新した結果
		 */
		public String update( final String column , final String... args ) {
			return column;
		}

	}

	/**
	 * 演算子を列挙する。
	 *
	 * ●使用例
	 * WHERE_OPERATORS op = WHERE_OPERATORS.valueOf("eq");
	 * System.out.println(op.apply("GF92.CLM","{&#064;CLM}",false));
	 *
	 * ●上記処理結果
	 * GF92.CLM	=	'{&#064;CLM}'
	 *
	 */
	public static enum WHERE_OPERATORS {
		eq() {
			/**
			 * 与えられた左辺と右辺を元に演算子付きの文字列を作成する。
			 * 第３引数のbooleanは、trueの時に値が数値であることを意味する。
			 *
			 * @param	left	左辺文字列
			 * @param	right	右辺文字列
			 * @param	is_num	数値かどうか(trueで、数値を指定)
			 * @return	演算子を加えた結果
			 * @og.rtnNotNull
			 */
			public String apply(final String left , final String right, final boolean is_num) {
				if( is_num ) {
					return leftVal(left) + "=\t " + right ;
				}else {
					return leftVal(left) + "=\t '" + right + "'";
				}
			}
			/**
			 * 演算子(シンボル)の文字列配列を返します。
			 *
			 * @return	シンボル文字列配列
			 * @og.rtnNotNull
			 */
			public String[] symbol(){
				return new String[] {"="};
			}
		}  ,
		lk1() {
			/**
			 * 与えられた左辺と右辺を元に演算子付きの文字列を作成する。
			 * 第３引数のbooleanは、trueの時に値が数値であることを意味する。
			 *
			 * @param	left	左辺文字列
			 * @param	right	右辺文字列
			 * @param	is_num	数値かどうか(trueで、数値を指定)
			 * @return	演算子を加えた結果
			 * @og.rtnNotNull
			 */
			public String apply(final String left , final String right, final boolean is_num) {
				return leftVal(left) + "like '" + right + "%'";
			}
			/**
			 * 演算子(シンボル)の文字列配列を返します。
			 *
			 * @return	シンボル文字列配列
			 * @og.rtnNotNull
			 */
			public String[] symbol(){
				return new String[] {"like","","%"};
			}
		}  ,
		lk2() {
			/**
			 * 与えられた左辺と右辺を元に演算子付きの文字列を作成する。
			 * 第３引数のbooleanは、trueの時に値が数値であることを意味する。
			 *
			 * @param	left	左辺文字列
			 * @param	right	右辺文字列
			 * @param	is_num	数値かどうか(trueで、数値を指定)
			 * @return	演算子を加えた結果
			 * @og.rtnNotNull
			 */
			public String apply(final String left , final String right, final boolean is_num) {
				return leftVal(left) + "like '%" + right + "'";
			}
			/**
			 * 演算子(シンボル)の文字列配列を返します。
			 *
			 * @return	シンボル文字列配列
			 * @og.rtnNotNull
			 */
			public String[] symbol(){
				return new String[] {"like","%",""};
			}
		}  ,
		lk3() {
			/**
			 * 与えられた左辺と右辺を元に演算子付きの文字列を作成する。
			 * 第３引数のbooleanは、trueの時に値が数値であることを意味する。
			 *
			 * @param	left	左辺文字列
			 * @param	right	右辺文字列
			 * @param	is_num	数値かどうか(trueで、数値を指定)
			 * @return	演算子を加えた結果
			 * @og.rtnNotNull
			 */
			public String apply(final String left , final String right, final boolean is_num) {
				return leftVal(left) + "like '%" + right + "%'";
			}
			/**
			 * 演算子(シンボル)の文字列配列を返します。
			 *
			 * @return	シンボル文字列配列
			 * @og.rtnNotNull
			 */
			public String[] symbol(){
				return new String[] {"like","%","%"};
			}
		}  ,
		gt() {
			/**
			 * 与えられた左辺と右辺を元に演算子付きの文字列を作成する。
			 * 第３引数のbooleanは、trueの時に値が数値であることを意味する。
			 *
			 * @param	left	左辺文字列
			 * @param	right	右辺文字列
			 * @param	is_num	数値かどうか(trueで、数値を指定)
			 * @return	演算子を加えた結果
			 * @og.rtnNotNull
			 */
			public String apply(final String left , final String right, final boolean is_num) {
				if( is_num ) {
					return leftVal(left) + ">\t " + right;
				}else{
					return leftVal(left) + ">\t '" + right + "'";
				}
			}
			/**
			 * 演算子(シンボル)の文字列配列を返します。
			 *
			 * @return	シンボル文字列配列
			 * @og.rtnNotNull
			 */
			public String[] symbol(){
				return new String[] {">"};
			}
		}  ,
		ge() {
			/**
			 * 与えられた左辺と右辺を元に演算子付きの文字列を作成する。
			 * 第３引数のbooleanは、trueの時に値が数値であることを意味する。
			 *
			 * @param	left	左辺文字列
			 * @param	right	右辺文字列
			 * @param	is_num	数値かどうか(trueで、数値を指定)
			 * @return	演算子を加えた結果
			 * @og.rtnNotNull
			 */
			public String apply(final String left , final String right, final boolean is_num) {
				if( is_num ) {
					return leftVal(left) + ">=\t " + right;
				}else{
					return leftVal(left) + ">=\t '" + right + "'";
				}
			}
			/**
			 * 演算子(シンボル)の文字列配列を返します。
			 *
			 * @return	シンボル文字列配列
			 * @og.rtnNotNull
			 */
			public String[] symbol(){
				return new String[] {">="};
			}
		}  ,
		lt()  {
			/**
			 * 与えられた左辺と右辺を元に演算子付きの文字列を作成する。
			 * 第３引数のbooleanは、trueの時に値が数値であることを意味する。
			 *
			 * @param	left	左辺文字列
			 * @param	right	右辺文字列
			 * @param	is_num	数値かどうか(trueで、数値を指定)
			 * @return	演算子を加えた結果
			 * @og.rtnNotNull
			 */
			public String apply(final String left , final String right, final boolean is_num) {
				if( is_num ) {
					return leftVal(left) + "<\t " + right;
				}else {
					return leftVal(left) + "<\t '" + right + "'";
				}
			}
			/**
			 * 演算子(シンボル)の文字列配列を返します。
			 *
			 * @return	シンボル文字列配列
			 * @og.rtnNotNull
			 */
			public String[] symbol(){
				return new String[] {"<"};
			}
		}  ,
		le()  {
			/**
			 * 与えられた左辺と右辺を元に演算子付きの文字列を作成する。
			 * 第３引数のbooleanは、trueの時に値が数値であることを意味する。
			 *
			 * @param	left	左辺文字列
			 * @param	right	右辺文字列
			 * @param	is_num	数値かどうか(trueで、数値を指定)
			 * @return	演算子を加えた結果
			 * @og.rtnNotNull
			 */
			public String apply(final String left , final String right, final boolean is_num) {
				if( is_num ) {
					return leftVal(left) + "<=\t " + right;
				}else{
					return leftVal(left) + "<=\t '" + right + "'";
				}
			}
			/**
			 * 演算子(シンボル)の文字列配列を返します。
			 *
			 * @return	シンボル文字列配列
			 * @og.rtnNotNull
			 */
			public String[] symbol(){
				return new String[] {"<="};
			}
		}  ,
		not() {
			/**
			 * 与えられた左辺と右辺を元に演算子付きの文字列を作成する。
			 * 第３引数のbooleanは、trueの時に値が数値であることを意味する。
			 *
			 * @param	left	左辺文字列
			 * @param	right	右辺文字列
			 * @param	is_num	数値かどうか(trueで、数値を指定)
			 * @return	演算子を加えた結果
			 * @og.rtnNotNull
			 */
			public String apply(final String left , final String right, final boolean is_num) {
				if( is_num ) {
					return leftVal(left) + "!=\t " + right;
				} else {
					return leftVal(left) + "!=\t '" + right + "'";
				}
			}
			/**
			 * 演算子(シンボル)の文字列配列を返します。
			 *
			 * @return	シンボル文字列配列
			 * @og.rtnNotNull
			 */
			public String[] symbol(){
				return new String[] {"!="};
			}
		} ,
		bw()  {
			/**
			 * 与えられた左辺と右辺を元に演算子付きの文字列を作成する。
			 * 第３引数のbooleanは、trueの時に値が数値であることを意味する。
			 *
			 * @param	left	左辺文字列
			 * @param	right	右辺文字列
			 * @param	is_num	数値かどうか(trueで、数値を指定)
			 * @return	演算子を加えた結果
			 * @og.rtnNotNull
			 */
			public String apply(final String left , final String right, final boolean is_num) {
				if( is_num ) {
					return leftVal(left) + "between " + betweenFormat( right , "_FROM" ) + " and " + betweenFormat( right , "_TO" );
				}else {
					return leftVal(left) + "between '" + betweenFormat( right , "_FROM" ) + "' and '" + betweenFormat( right , "_TO" ) + "'";
				}
			}
			/**
			 * 演算子(シンボル)の文字列配列を返します。
			 *
			 * @return	シンボル文字列配列
			 * @og.rtnNotNull
			 */
			public String[] symbol(){
				return new String[] {"between"};
			}
		} ,
		in() {
			/**
			 * 与えられた左辺と右辺を元に演算子付きの文字列を作成する。
			 * 第３引数のbooleanは、trueの時に値が数値であることを意味する。
			 *
			 * @param	left	左辺文字列
			 * @param	right	右辺文字列
			 * @param	is_num	数値かどうか(trueで、数値を指定)
			 * @return	演算子を加えた結果
			 * @og.rtnNotNull
			 */
			public String apply(final String left , final String right, final boolean is_num) {
				return leftVal(left) + "in\t (" +inFormat( right , is_num ) + ")";
			}
			/**
			 * 演算子(シンボル)の文字列配列を返します。
			 *
			 * @return	シンボル文字列配列
			 * @og.rtnNotNull
			 */
			public String[] symbol(){
				return new String[] {"in"};
			}
		} ,
		;

		// 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。
		private static final String[] TABS = { "\t\t\t\t" , "\t\t\t" , "\t\t" , "\t" };		// 5.6.4.4 (2013/05/31)

		private static final Pattern LK1_PTN = Pattern.compile("like\\s+\\'\\{@(\\w*?)\\}%\\'");
		private static final Pattern LK2_PTN = Pattern.compile("like\\s+\\'%\\{@(\\w*?)\\}\\'");
		private static final Pattern LK3_PTN = Pattern.compile("like\\s+\\'%\\{@(\\w*?)\\}%\\'");

		/**
		 * 演算子の記号を略語に変換する。
		 *
		 * @og.rev 5.6.4.4 (2013/05/31) タブによる位置合わせの計算方法修正。
		 *
		 * @param left	引数
		 * @return	演算子の記号の略語
		 * @og.rtnNotNull
		 */
//		static String leftVal( final String left ) {
		private static String leftVal( final String left ) {
			final int adrs = ((left.length()-1)/4 > 3 ) ? 3 : (left.length()-1)/4 ;
			return left + TABS[adrs] ;												// ４タブを想定。
		}

		/**
		 * 与えられた左辺と右辺を元に演算子付きの文字列を作成する。
		 * 第３引数のbooleanは、trueの時に値が数値であることを意味する。
		 *
		 * @param	left	左辺文字列
		 * @param	right	右辺文字列
		 * @param	is_num	数値かどうか(trueで、数値を指定)
		 * @return	演算子を加えた結果
		 * @og.rtnNotNull
		 */
		public abstract String apply(final String left , final String right , final boolean is_num);

		/**
		 * 演算子を返却する。
		 *
		 * @return String[] 演算子
		 */
		public abstract String[] symbol();

		/**
		 * IN句の値を組み立てなおします。
		 *
		 * @og.rev 6.3.9.1 (2015/11/27) 修飾子 なし → private に変更。
		 *
		 * @param str String
		 * @param is_number boolean
		 * @return	IN句のフォーマット
		 * @og.rtnNotNull
		 */
//		static String inFormat(final String str , final boolean is_number){
		private static String inFormat(final String str , final boolean is_number){
			final StringBuilder formated = new StringBuilder( BUFFER_MIDDLE );
			final String[] ins = str.split( "," );
			for( final String in :ins ){
				if( formated.length() > 0 ){
					formated.append( ',' );					// 6.0.2.5 (2014/10/31) char を append する。
				}
				if( is_number ) {
					formated.append( in );
				}else{
					formated.append( '\'' ).append( in ).append( '\'' );
				}
			}

			return formated.toString();

		}

		/**
		 * BETWEENを組み立てなおす。
		 *
		 * @og.rev 6.3.9.1 (2015/11/27) 修飾子 なし → private に変更。
		 *
		 * @param str String
		 * @param suffix String
		 * @return	BETWEENのフォーマット
		 * @og.rtnNotNull
		 */
//		static String betweenFormat( final String str , final String suffix ){
		private static String betweenFormat( final String str , final String suffix ){
			final StringBuilder sb = new StringBuilder( str );
			if( str.indexOf("{@") == 0 ){
				sb.insert( sb.length() - 1 , suffix );
			}else{
				sb.append( suffix );
			}
			return sb.toString();
		}

		/**
		 * 演算子の記号を略語に変換する。
		 *
		 * @og.rev 6.3.9.1 (2015/11/27) 修飾子 なし → private に変更。
		 *
		 * @param arg	引数
		 * @return	演算子の記号の略語
		 * @og.rtnNotNull
		 */
//		static String convert(final String arg){
		private static String convert(final String arg){
			for( final WHERE_OPERATORS fnc : values() ){
				if( fnc.symbol().length == 1 && arg.trim().indexOf( fnc.symbol()[0] ) == 0 ){
					return fnc.toString();
				}
				if( fnc.symbol().length == 3){
					Matcher matcher = LK1_PTN.matcher( arg );
					if( matcher.find() ){
						return lk1.toString();
					}
					matcher = LK2_PTN.matcher( arg );
					if( matcher.find() ){
						return lk2.toString();
					}
					matcher = LK3_PTN.matcher( arg );
					if( matcher.find() ){
						return lk3.toString();
					}
				}
			}
			return "";
		}
	}
}
