package org.arefgard.flow.core;

import java.util.Map;
import java.util.Queue;
import java.util.StringTokenizer;

/**
 * ユースケース式。
 * 
 * @author Takashi Yamashina
 * @since 1.0.0
 */
public class UsecaseExpression {

	/** トークンを保持するキュー */
	private Queue<String> tokens = new java.util.concurrent.ConcurrentLinkedQueue<String>();
	
	/** 引数 */
	private Map<String, Object> args = new java.util.HashMap<String, Object>();
	
	/**
	 * コンストラクタ。
	 * 
	 * @param expr ユースケース式
	 */
	public UsecaseExpression(String expr) {
		StringTokenizer tokenizer = new StringTokenizer(expr, ".!=<>\"", true);
		while(tokenizer.hasMoreTokens()) {
			this.tokens.offer(tokenizer.nextToken());
		}
	}

	/**
	 * 引数を追加する。
	 * 
	 * @param arg 引数名
	 * @param value 引数の値
	 */
	public void addArg(String arg, Object value) {
		this.args.put(arg, value);
	}
	
	/**
	 * 引数マップを設定する。
	 * 
	 * @param args 引数マップ
	 */
	public void setArgs(Map<String, Object> args) {
		this.args = args;
	}

	/**
	 * ユースケース式を評価する。
	 * 
	 * @return 評価結果
	 */
	public boolean evaluateBoolean() {
		String token = null;
		Object left = null;
		Object right = null;
		boolean isEqual = false;
		boolean isNotEqual = false;
		boolean isLessThan = false;
		boolean isLessThanEqual = false;
		boolean isGreaterThan = false;
		boolean isGreaterThanEqual = false;
		boolean isString = false;
		boolean result = false;
		boolean isLeft = true;
		// トークンを解析する
		while((token = this.tokens.peek()) != null) {
			if(this.args.containsKey(token) && isLeft) {
				left = this.args.get(token);
				this.tokens.remove();
			}else if(token.equals("=")) {
				isEqual = true;
				isLeft = false;
				this.tokens.remove();
			}else if(token.equals("<")) {
				this.tokens.remove();
				if(this.tokens.peek().equals("=")) {
					isLessThanEqual = true;
					this.tokens.remove();
				}else {
					isLessThan = true;					
				}
				isLeft = false;
			}else if(token.equals(">")) {
				this.tokens.remove();
				if(this.tokens.peek().equals("=")) {
					isGreaterThanEqual = true;
					this.tokens.remove();
				}else {
					isGreaterThan = true;
				}
				isLeft = false;
			}else if(token.equals("!")) {
				this.tokens.remove();
				if(this.tokens.peek().equals("=")) {
					isNotEqual = true;
					isLeft = false;
					this.tokens.remove();
				}
			}else if(token.equals("\"")) {
				if(isString == false) {
					isString = true;
				}else {
					isString = false;
				}
				this.tokens.remove();
			}else {
				if(isString) {
					right = token;
				}else if(this.args.containsKey(token)) {
					right = this.args.get(token);
				}else {
					right = token;
				}
				this.tokens.remove();
			}
		}
		// 評価を行う
		if(left instanceof java.lang.Integer) {
			Integer leftInt = (Integer)left;
			Integer rightInt = null;
			if(right instanceof java.lang.Integer) {
				rightInt = (Integer)right;
			}else {
				rightInt = Integer.parseInt((String)right);
			}
			if(isEqual) {
				result = leftInt.compareTo(rightInt) == 0;
			}else if(isNotEqual) {
				result = leftInt.compareTo(rightInt) != 0;
			}else if(isLessThan) {
				result = leftInt.compareTo(rightInt) < 0;
			}else if(isLessThanEqual) {
				result = leftInt.compareTo(rightInt) <= 0;
			}else if(isGreaterThan) {
				result = leftInt.compareTo(rightInt) > 0;
			}else if(isGreaterThanEqual) {
				result = leftInt.compareTo(rightInt) >= 0;
			}
		}else if(left instanceof java.lang.Long) {
			Long leftLong = (Long)left;
			Long rightLong = null;
			if(right instanceof java.lang.Long) {
				rightLong = (Long)right;
			}else {
				rightLong = Long.parseLong((String)right);
			}
			if(isEqual) {
				result = leftLong.compareTo(rightLong) == 0;
			}else if(isNotEqual) {
				result = leftLong.compareTo(rightLong) != 0;
			}else if(isLessThan) {
				result = leftLong.compareTo(rightLong) < 0;
			}else if(isLessThanEqual) {
				result = leftLong.compareTo(rightLong) <= 0;
			}else if(isGreaterThan) {
				result = leftLong.compareTo(rightLong) > 0;
			}else if(isGreaterThanEqual) {
				result = leftLong.compareTo(rightLong) >= 0;
			}
		}else {
			if(isEqual) {
				result = left.equals(right);
			}else if(isNotEqual) {
				result = !left.equals(right);	
			}
		}
		return result;
	}
	
	public Object evaluateObject() {
		return null;
	}

}
