/*
 * Copyright 2006 Takahiro Nakamura.
 *
 * 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 woolpack.validator;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import woolpack.utils.UtilsConstants;

/**
 * 定数・静的メソッドの集まり。
 * @author nakamura
 *
 */
public class ValidatorConstants {
	private static final Pattern LOCAL_CLASS_NAME = Pattern.compile("([^\\.]+\\.)*([^\\.]*)");
	
	/**
	 * 常に true を返す{@link ValidatorExpression}。
     * {@link ValidatorContext}に対して状態を変更する操作を行わない。
	 */
	public static final ValidatorExpression TRUE = new ValidatorExpression(){
		public boolean interpret(final ValidatorContext context) {
			return true;
		}
		public void appendTo(final Appendable sb) throws IOException{
			sb.append("new TrueValidator()");
		}
		public void addMessageTo(final Collection<String> messageCollection) {
		}
	};
	
	/**
	 * 常に false を返す{@link ValidatorExpression}。
     * {@link ValidatorContext}に対して状態を変更する操作を行わない。
	 */
	public static final ValidatorExpression FALSE = new ValidatorExpression(){
		public boolean interpret(final ValidatorContext context) {
			return false;
		}
		public void appendTo(final Appendable sb) throws IOException {
			sb.append("new FalseValidator()");
		}
		public void addMessageTo(final Collection<String> messageCollection) {
		}
	};
	
	/**
	 * {@link ValidatorContext#getTmpValue()}がnullでない、
	 * かつ文字列の場合は空でないことを検証しその結果を返す{@link ValidatorExpression}。
     * {@link ValidatorContext}に対して状態を変更する操作を行わない。
	 */
	public static final ValidatorExpression REQUIRED = new ValidatorExpression(){
		public boolean interpret(final ValidatorContext context) {
			final Object value = context.getTmpValue();
			return value != null && (!(value instanceof String) || ((String)value).length() > 0);
		}
		public void appendTo(final Appendable sb) throws IOException {
			sb.append("new RequiredValidator()");
		}
		public void addMessageTo(final Collection<String> messageCollection) {
		}
	};
	
	/**
	 * 全ての{@link ValidatorExpression}を呼び出す、
	 * 論理和の{@link ValidatorIterable}。
     * {@link ValidatorContext}に対して状態を変更する操作を行わない。
	 */
	public static final ValidatorIterable OR = new ValidatorIterable(){
		public boolean interpret(final ValidatorContext context, final Iterable<? extends ValidatorExpression> iterable) {
			boolean result = false;
			boolean empty = true;
			for(final ValidatorExpression e:iterable){
				empty = false;
				result |= e.interpret(context);
			}
			return result || empty;
		}
		public void appendTo(final Appendable sb) throws IOException {
			sb.append("new OrValidatorIterable()");
		}
	};
	
	/**
	 * 検証結果が true になった時点で{@link ValidatorExpression}の呼び出しを停止する、
	 * 論理和の{@link ValidatorIterable}。
     * {@link ValidatorContext}に対して状態を変更する操作を行わない。
	 */
	public static final ValidatorIterable OROR = new ValidatorIterable(){
		public boolean interpret(final ValidatorContext context, final Iterable<? extends ValidatorExpression> iterable) {
			boolean empty = true;
			for(final ValidatorExpression e:iterable){
				empty = false;
				if(e.interpret(context)){
					return true;
				}
			}
			return empty;
		}
		public void appendTo(final Appendable sb) throws IOException {
			sb.append("new OrOrValidatorIterable()");
		}
	};
	
	/**
	 * 全ての{@link ValidatorExpression}を呼び出す、
	 * 論理積の{@link ValidatorIterable}。
     * {@link ValidatorContext}に対して状態を変更する操作を行わない。
	 */
	public static final ValidatorIterable AND = new ValidatorIterable(){
		public boolean interpret(final ValidatorContext context, final Iterable<? extends ValidatorExpression> iterable) {
			boolean flag = true;
			for(final ValidatorExpression e:iterable){
				flag &= e.interpret(context);
			}
			return flag;
		}
		public void appendTo(final Appendable sb) throws IOException {
			sb.append("new AndValidatorIterable()");
		}
	};
	
	/**
	 * 検証結果が false になった時点で{@link ValidatorExpression}の呼び出しを停止する、
	 * 論理積の{@link ValidatorIterable}。
     * {@link ValidatorContext}に対して状態を変更する操作を行わない。
	 */
	public static final ValidatorIterable ANDAND = new ValidatorIterable(){
		public boolean interpret(final ValidatorContext context, final Iterable<? extends ValidatorExpression> iterable) {
			for(final ValidatorExpression e:iterable){
				final boolean flag = e.interpret(context);
				if(!flag){
					return false;
				}
			}
			return true;
		}
		public void appendTo(final Appendable sb) throws IOException {
			sb.append("new AndAndValidatorIterable()");
		}
	};
	
	/**
	 * 全ての{@link ValidatorExpression}を呼び出す、
	 * 同値の{@link ValidatorIterable}。
     * {@link ValidatorContext}に対して状態を変更する操作を行わない。
	 */
	public static final ValidatorIterable EQ = new ValidatorIterable(){
		public boolean interpret(final ValidatorContext context, final Iterable<? extends ValidatorExpression> iterable) {
			boolean trueFlag = true;
			boolean falseFlag = true;
			for(final ValidatorExpression e:iterable){
				boolean flag = e.interpret(context);
				trueFlag &= flag;
				falseFlag &= !flag;
			}
			return trueFlag || falseFlag;
		}
		public void appendTo(final Appendable sb) throws IOException {
			sb.append("new EqValidatorIterable()");
		}
	};
	
	/**
	 * 検証結果が同値でなくなった時点で{@link ValidatorExpression}の呼び出しを停止する、
	 * 同値の{@link ValidatorIterable}。
     * {@link ValidatorContext}に対して状態を変更する操作を行わない。
	 */
	public static final ValidatorIterable EQEQ = new ValidatorIterable(){
		public boolean interpret(final ValidatorContext context, final Iterable<? extends ValidatorExpression> iterable) {
			boolean trueFlag = true;
			boolean falseFlag = true;
			for(final ValidatorExpression e:iterable){
				boolean flag = e.interpret(context);
				trueFlag &= flag;
				falseFlag &= !flag;
				if(!(trueFlag || falseFlag)){
					return false;
				}
			}
			return true;
		}
		public void appendTo(final Appendable sb) throws IOException {
			sb.append("new EqEqValidatorIterable()");
		}
	};

	private ValidatorConstants(){
	}
	
	/**
	 * パッケージ名を取り除いたクラス名を返す。
	 * JavaScriptコンストラクタを生成する際に使用する。
	 * @param clazz 変換対象のクラス。
	 * @return パッケージ名を取り除いたクラス名。
	 * @throws NullPointerException 引数が null の場合。
	 */
	public static String getLocalClassName(final Class clazz){
		Matcher m = LOCAL_CLASS_NAME.matcher(clazz.getName());
		m.matches();
		return m.group(2);
	}

	/**
	 * 引数のキーが全て文字列型・値が全てオブジェクトの一覧とみなして変換する。
	 * request.getParameterMap()は Generics に対応していないので Map<String,Object> を生成してコピーする。
	 * @param map 変換元。
	 * @return 変換結果。キーが文字列、値が文字列の{@link List}になる。変換先を更新しても変換元には影響しない。
	 * @throws NullPointerException 引数が null の場合。
	 * @throws ClassCastException 引数のがキーが文字列型でない場合。
	 */
	public static Map<String,List<Object>> convert(final Map map){
		final Map<String,List<Object>> map1 = new HashMap<String,List<Object>>();
		for(final Object key:map.keySet()){
			final Iterable c = UtilsConstants.toIterable(map.get(key));
			final List<Object> list = new ArrayList<Object>();
			for(final Object o:c){
				list.add(o);
			}
			map1.put((String)key, list);
		}
		return map1;
	}
	
	/**
	 * 文字列の一覧の文字列表現を生成する。
	 * @param iterable 表現対象。
	 * @param sb 文字列の追加先。
	 * @throws IOException 
	 * @throws NullPointerException 引数のいずれかが null の場合。
	 */
	public static void appendTo(final Appendable sb, final Iterable<String> iterable) throws IOException{
		sb.append('[');
		{
			boolean flag = true;
			for(final String v:iterable){
				if(!flag){
					sb.append(',');
				}
				flag = false;
				appendTo(sb, v);
			}
		}
		sb.append(']');
	}
	
	/**
	 * 正規表現の文字列表現を生成する。
	 * @param sb 文字列の追加先。
	 * @param pattern 正規表現。
	 * @throws IOException 
	 * @throws NullPointerException 引数のいずれかが null の場合。
	 */
	public static void appendTo(final Appendable sb, final Pattern pattern) throws IOException{
		// TODO javascriptの正規表現方法を調査する
		appendTo(sb, pattern.pattern());
	}
	
	/**
	 * {@link Map}の文字列表現を生成する。
	 * @param sb 文字列の追加先。
	 * @param map 表現対象。
	 * @throws IOException 
	 * @throws NullPointerException 引数のいずれかが null の場合。
	 */
	public static void appendTo(final Appendable sb, final Map<? extends Object, ValidatorExpression> map) throws IOException{
		sb.append('{');
		boolean flag = false;
		for(final Object key:map.keySet()){
			if(flag){
				sb.append(',');
			}
			flag = true;
			final ValidatorExpression expression = map.get(key);
			appendTo(sb, key.toString());
			sb.append(':');
			expression.appendTo(sb);
		}
		sb.append('}');
	}
	
	/**
	 * 文字列の文字列表現を生成する。
	 * @param sb 文字列の追加先。
	 * @param s 表現対象。
	 * @throws IOException
	 */
	public static void appendTo(final Appendable sb, final String s) throws IOException{
		sb.append('\"');
		sb.append(s);
		sb.append('\"');
	}
}
