package hiro.yoshioka.ast.sql.oracle.util;

import hiro.yoshioka.ast.sql.AbsSimpleNode;
import hiro.yoshioka.ast.sql.IToken;
import hiro.yoshioka.ast.sql.oracle.ASTConditionElement;
import hiro.yoshioka.ast.sql.oracle.ASTExpressionList;
import hiro.yoshioka.ast.sql.oracle.ASTInsertColumns;
import hiro.yoshioka.ast.sql.oracle.ASTSchemaTableColumn;
import hiro.yoshioka.ast.sql.oracle.ASTSelectColumnsElement;
import hiro.yoshioka.ast.sql.oracle.ASTSetClause;
import hiro.yoshioka.ast.sql.oracle.ASTUpdateStatement;
import hiro.yoshioka.ast.sql.oracle.ASTtableIdentifier;
import hiro.yoshioka.ast.sql.oracle.SimpleNode;
import hiro.yoshioka.ast.sql.oracle.WolfSQLParserConstants;
import hiro.yoshioka.ast.sql.oracle.WolfSQLParserTreeConstants;
import hiro.yoshioka.ast.sql.util.BindInfo;
import hiro.yoshioka.sql.resource.IDBTable;

import java.util.ArrayList;
import java.util.List;

public class BindCheckSQLNodeVisitor extends DefaultSQLNodeVisitor {
	IDBTable table;
	List<IToken> targetColumns = new ArrayList<IToken>();
	boolean bindInsert;
	boolean bindUpdate;
	boolean inSetup;
	int targetColumnsIndex;

	public BindCheckSQLNodeVisitor() {
	}

	public BindCheckSQLNodeVisitor(IDBTable table) {
		this.table = table;
	}

	// UPDATE START
	// -----------------------------------------------------------------
	public Object visit(ASTUpdateStatement node, Object data) {
		bindUpdate = true;
		SimpleNode n;
		for (int ord = 0; ord < node.jjtGetNumChildren(); ord++) {
			n = (SimpleNode) node.jjtGetChild(ord);
			n.jjtAccept(this, data);
		}
		return v;
	}

	public Object visit(ASTSetClause node, Object data) {
		SimpleNode n;
		inSetup = true;
		for (int ord = 0; ord < node.jjtGetNumChildren(); ord++) {
			n = (SimpleNode) node.jjtGetChild(ord);
			n.jjtAccept(this, data);
			if (bindUpdate) {
				targetColumnsIndex++;
			}
		}
		inSetup = false;
		return v;
	}

	// INSERT START
	// -----------------------------------------------------------------
	public Object visit(ASTInsertColumns node, Object data) {
		bindInsert = true;
		SimpleNode n;
		for (int ord = 0; ord < node.jjtGetNumChildren(); ord++) {
			n = (SimpleNode) node.jjtGetChild(ord);
			n.jjtAccept(this, data);
		}
		return v;
	}

	/**
	 * ?の左側
	 */
	public Object visit(ASTtableIdentifier node, Object data) {
		if (bindInsert || (bindUpdate && inSetup)) {
			targetColumns.add(node.getFirstToken());
		}
		SimpleNode n;
		for (int ord = 0; ord < node.jjtGetNumChildren(); ord++) {
			n = (SimpleNode) node.jjtGetChild(ord);
			n.jjtAccept(this, data);
		}
		return v;
	}

	public Object visit(ASTExpressionList node, Object data) {
		SimpleNode n;
		for (int ord = 0; ord < node.jjtGetNumChildren(); ord++) {
			n = (SimpleNode) node.jjtGetChild(ord);
			n.jjtAccept(this, data);
			if (bindInsert) {
				targetColumnsIndex++;
			}
		}
		return v;
	}

	// INSERT END
	// -----------------------------------------------------------------

	public Object doJob(SimpleNode node, Object data) {
		SimpleNode n;
		for (int ord = 0; ord < node.jjtGetNumChildren(); ord++) {
			n = (SimpleNode) node.jjtGetChild(ord);
			n.jjtAccept(this, data);
		}
		return v;
	}

	public Object visit(ASTConditionElement node, Object data) {
		AbsSimpleNode[] children = node.getChildren();
		if (log.isTraceEnabled()) {
			log.trace("♪ children.length=" + children.length);
		}
		if (children.length == 2) { // select column
			int s0 = countBind(children[0]);
			int s2 = countBind(children[1]);
			BindInfo info = null;

			if (log.isTraceEnabled()) {
				log.trace("♪ bind Count Left=" + s0 + " bind Count right=" + s2);
			}
			if (s0 == 1 && s2 == 0) {
				info = new BindInfo(table, children[1].getLastToken(), null);
				v.add(info);
				return data;
			} else if (s0 == 0 && s2 == 1) {
				info = new BindInfo(table, children[0].getFirstToken(), null);
				v.add(info);
				return data;
			}
		}

		for (int i = 0; i < children.length; i++) {
			((SimpleNode) children[i]).jjtAccept(this, data);
		}
		return data;
	}

	public Object visit(ASTSelectColumnsElement node, Object data) {
		AbsSimpleNode[] children = node.getChildren();
		for (int i = 0; i < children.length; i++) {
			int s0 = countBind(children[i]);

			BindInfo info = null;

			if (s0 > 0) {
				info = new BindInfo(children[i].getFirstToken().getImage());
				v.add(info);
				return data;
			}
		}

		for (int i = 0; i < children.length; i++) {
			((SimpleNode) children[i]).jjtAccept(this, data);
		}
		return data;
	}

	private int countBind(AbsSimpleNode node) {
		int sum = 0;
		sum = sub_countBind(node, sum);
		return sum;
	}

	private int sub_countBind(AbsSimpleNode node, int sum) {
		if (node.getLastToken().getKind() == WolfSQLParserConstants.QUESTIONMARK) {
			if (node.getID() == WolfSQLParserTreeConstants.JJTSCHEMATABLECOLUMN
					|| node.getID() == WolfSQLParserTreeConstants.JJTLIKECONDITION) {
				if (log.isInfoEnabled()) {
					log.info("♪♪ node=" + node);
				}
				sum++;
			}
		}
		AbsSimpleNode[] children = node.getChildren();
		for (int i = 0; i < children.length; i++) {
			sum = sub_countBind(children[i], sum);
		}
		return sum;
	}

	/**
	 * ?の部分
	 */
	public Object visit(ASTSchemaTableColumn node, Object data) {
		IToken last = node.getLastToken();
		if (last.getKind() == WolfSQLParserConstants.QUESTIONMARK) {
			boolean ui = bindInsert || bindUpdate;
			if (ui && targetColumns.size() > targetColumnsIndex) {
				v.add(new BindInfo(table,
						targetColumns.get(targetColumnsIndex), last));
			} else {
				v.add(new BindInfo("STRING" + v.size(), null, last));
			}
		}
		return data;
	}

}
