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

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

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

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

			for( GROUPING_FUNCTIONS fnc : values() ){
//				if( arg.indexOf( fnc.toString() ) > -1 ) {
				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 String
		 * @param args String[]
		 * @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() {
			public String apply(final String left , final String right, final boolean is_num) {
				if ( is_num ){
					return left + "\t=\t" + right ;
				}else {
					return left + "\t=\t'" + right + "'";
				}
			}
			public String[] symbol(){
				return new String[] {"="};
			}
		}  ,
		lk1() {
			public String apply(final String left , final String right, final boolean is_num) {
				return left + "\tlike\t'" + right + "%'";
			}
			public String[] symbol(){
				return new String[] {"like","","%"};
			}
		}  ,
		lk2() {
			public String apply(final String left , final String right, final boolean is_num) {
				return left + "\tlike\t'%" + right + "'";
			}
			public String[] symbol(){
				return new String[] {"like","%",""};
			}
		}  ,
		lk3() {
			public String apply(final String left , final String right, final boolean is_num) {
				return left + "\tlike\t'%" + right + "%'";
			}
			public String[] symbol(){
				return new String[] {"like","%","%"};
			}
		}  ,
		gt() {
			public String apply(final String left , final String right, final boolean is_num) {
				if ( is_num ) {
					return left + "\t>\t" + right + "";
				}else{
					return left + "\t>\t'" + right + "'";
				}
			}
			public String[] symbol(){
				return new String[] {">"};
			}
		}  ,
		ge() {
			public String apply(final String left , final String right, final boolean is_num) {
				if ( is_num ) {
					return left + "\t>=\t" + right + "";
				}else{
					return left + "\t>=\t'" + right + "'";
				}
			}
			public String[] symbol(){
				return new String[] {">="};
			}
		}  ,
		lt()  {
			public String apply(final String left , final String right, final boolean is_num) {
				if ( is_num ) {
					return left + "\t<\t" + right + "";
				}else {
					return left + "\t<\t'" + right + "'";
				}
			}
			public String[] symbol(){
				return new String[] {"<"};
			}
		}  ,
		le()  {
			public String apply(final String left , final String right, final boolean is_num) {
				if ( is_num ){
					return left + "\t<=\t" + right + "";
				}else{
					return left + "\t<=\t'" + right + "'";
				}
			}
			public String[] symbol(){
				return new String[] {"<="};
			}
		}  ,
		not() {
			public String apply(final String left , final String right, final boolean is_num) {
				if ( is_num ) {
					return left + "\t!=\t" + right + "";
				} else {
					return left + "\t!=\t'" + right + "'";
				}
			}
			public String[] symbol(){
				return new String[] {"!="};
			}
		} ,
		bw()  {
			public String apply(final String left , final String right, final boolean is_num) {
				if ( is_num ) {
					return left + "\tbetween\t" + betweenFormat( right , "_FROM" ) + " AND " + betweenFormat( right , "_TO" ) + "";
				}else {
					return left + "\tbetween\t'" + betweenFormat( right , "_FROM" ) + "' AND '" + betweenFormat( right , "_TO" ) + "'";
				}
			}
			public String[] symbol(){
				return new String[] {"between"};
			}
		} ,
		in() {
			public String apply(final String left , final String right, final boolean is_num) {
				return left + "\tin\t(" +inFormat( right , is_num ) + ")";
			}
			public String[] symbol(){
				return new String[] {"in"};
			}
		} ,
		;
		/**
		 * 与えられた左辺と右辺を元に演算子付きの文字列を作成する。
		 * 第３引数のbooleanは、trueの時に値が数値であることを意味する。
		 *
		 * @param left String
		 * @param right String
		 * @param is_num boolean
		 * @return	演算子を加えた結果
		 */
		abstract public String apply(final String left , final String right , final boolean is_num);

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

		/**
		 * IN句の値を組み立てなおします。
		 *
		 * @param str String
		 * @param is_number boolean
		 * @return	IN句のフォーマット
		 */
		static String inFormat(final String str , final boolean is_number){
			StringBuilder formated = new StringBuilder("");
			String[] ins = str.split( "," );
			for (String in :ins ){
				if (formated.length() > 0 ){
					formated.append( "," );
				}
				if ( is_number ) {
					formated.append( in );
				}else{
					formated.append( "'" ).append( in ).append( "'" );
				}
			}

			return formated.toString();

		}

		/**
		 * BETWEENを組み立てなおす。
		 *
		 * @param str String
		 * @param suffix String
		 * @return	BETWEENのフォーマット
		 */
		static String betweenFormat(final String str , final String suffix){
			StringBuilder sb = new StringBuilder(str);
			if ( str.indexOf("{@") == 0 ){
				sb.insert( sb.length() - 1 , suffix );
			}else{
				sb.append( suffix );
			}
			return sb.toString();
		}

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

		/**
		 * 演算子の記号を略語に変換する。
		 *
		 * @param arg	引数
		 * @return	演算子の記号の略語
		 */
		static String convert(final String arg){
			for( 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 = Pattern.compile("like\\s+\\'\\{@(\\w*?)\\}%\\'").matcher( arg );
					Matcher matcher = LK1_PTN.matcher( arg );
					if (matcher.find()){
						return lk1.toString();
					}
//					matcher = Pattern.compile("like\\s+\\'%\\{@(\\w*?)\\}\\'").matcher( arg );
					matcher = LK2_PTN.matcher( arg );
					if (matcher.find()){
						return lk2.toString();
					}
//					matcher = Pattern.compile("like\\s+\\'%\\{@(\\w*?)\\}%\\'").matcher( arg );
					matcher = LK3_PTN.matcher( arg );
					if (matcher.find()){
						return lk3.toString();
					}
				}
			}
			return "";
		}
	}

}
