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

import hiro.yoshioka.ast.sql.AbsSimpleNode;
import hiro.yoshioka.ast.sql.IToken;
import hiro.yoshioka.ast.sql.oracle.ASTCondition;
import hiro.yoshioka.ast.sql.oracle.ASTConditionElement;
import hiro.yoshioka.ast.sql.oracle.ASTOrderByClause;
import hiro.yoshioka.ast.sql.oracle.ASTOrderByElement;
import hiro.yoshioka.ast.sql.oracle.ASTQueryTableExpressionClause;
import hiro.yoshioka.ast.sql.oracle.ASTSelectColumns;
import hiro.yoshioka.ast.sql.oracle.ASTSelectColumnsElement;
import hiro.yoshioka.ast.sql.oracle.ASTSelectThird;
import hiro.yoshioka.ast.sql.oracle.SimpleNode;
import hiro.yoshioka.ast.sql.oracle.Token;
import hiro.yoshioka.ast.sql.oracle.WolfSQLParserConstants;
import hiro.yoshioka.ast.sql.oracle.WolfSQLParserTreeConstants;
import hiro.yoshioka.ast.sql.oracle.util.DefaultSQLNodeVisitor;
import hiro.yoshioka.sql.resource.DBSchema;
import hiro.yoshioka.sql.resource.DBTable;
import hiro.yoshioka.sql.resource.IDBTable;
import hiro.yoshioka.util.StringUtil;

import java.util.List;

import com.mongodb.BasicDBObject;

public class Sql2MongoVisitor extends DefaultSQLNodeVisitor {
	private BasicDBObject insertObject;
	private BasicDBObject updateObject;
	private BasicDBObject removeMustMatch;
	private BasicDBObject queryUsedToSearch;
	private BasicDBObject returnFields;
	private BasicDBObject orderBy;
	private String sql_statement;
	private String database;
	private String collection;
	private boolean needsRety;

	public Sql2MongoVisitor(String sql) {
		this.sql_statement = sql;
	}

	public String getDatabase() {
		return database;
	}

	public String getCollection() {
		return collection;
	}

	public boolean needsRetry() {
		return needsRety;
	}

	public BasicDBObject getReturnFields() {
		return returnFields;
	}

	public BasicDBObject getQueryUsedToSearch() {
		return queryUsedToSearch;
	}

	public BasicDBObject getOrderBy() {
		return orderBy;
	}

	public IDBTable getFirstTable() {
		DBSchema schema = new DBSchema(null);
		schema.setName(database);
		IDBTable firstTable = new DBTable(schema);
		firstTable.setName(collection);
		return firstTable;

	}

	String getLine(AbsSimpleNode node) {
		String stext = sql_statement;
		String line = new BackWord(stext).backWord(node.getFirstToken(),
				node.getLastToken());
		return line;
	}

	public class BackWord {
		String fTargetText;

		public BackWord(String targetText) {
			fTargetText = targetText;
		}

		public String backWord(IToken begin, IToken end) {
			String lsp = StringUtil.getThisLineSeparator(fTargetText);
			String[] lineData = fTargetText.split(lsp, -1);
			StringBuffer ret = new StringBuffer();
			for (int i = begin.getBeginLine() - 1; i <= end.getEndLine() - 1; i++) {
				if (i == begin.getBeginLine() - 1) {
					if (i == end.getEndLine() - 1) {
						ret.append(lineData[i].substring(
								begin.getBeginColumn() - 1, end.getEndColumn()));
					} else {
						ret.append(lineData[i].substring(begin.getBeginColumn() - 1));
					}
				} else if (i > begin.getBeginLine() - 1) {
					if (i == end.getEndLine() - 1) {
						ret.append(lineData[i].substring(0, end.getEndColumn()));
					} else {
						ret.append(lineData[i]);
					}
				} else {

					System.out.println("ELSE[" + lineData[i]);
				}
			}
			return ret.toString();
		}

	}

	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 data;
	}

	public Object visit(ASTSelectThird node, Object data) {

		// appendln("SELECT ");

		AbsSimpleNode[] nodes = node.getChildren();
		for (int i = 0; i < nodes.length; i++) {
			((SimpleNode) nodes[i]).jjtAccept(this, data);
		}
		return null;
	}

	// public Object visit(ASTInsertStatement node, Object data) {
	// append("INSERT INTO ");
	// SimpleNode n;
	// for (int i = 0; i < node.jjtGetNumChildren(); i++) {
	// n = (SimpleNode) node.jjtGetChild(i);
	// n.jjtAccept(this, data);
	// }
	// return null;
	// }
	//

	//
	// public Object visit(ASTValueClauses node, Object data) {
	// SimpleNode n;
	//
	// for (int i = 0; i < node.jjtGetNumChildren(); i++) {
	// if (i > 0) {
	// appendln(",");
	// }
	// n = (SimpleNode) node.getChild(i);
	// switch (n.getID()) {
	// case WolfSQLParserTreeConstants.JJTVALUECLAUSE:
	// n.jjtAccept(this, data);
	// break;
	// case WolfSQLParserTreeConstants.JJTSUBQUERY:
	// appendln(getLine(n));
	// break;
	// default:
	// break;
	// }
	// }
	// return null;
	// }
	//
	// public Object visit(ASTValueClause node, Object data) {
	// appendln("VALUES (");
	// SimpleNode child = (SimpleNode) node.getChild(0);
	// if (child.getID() == WolfSQLParserTreeConstants.JJTEXPRESSIONLIST) {
	// deepSplitComma(child, data);
	// } else {
	// child.jjtAccept(this, data);
	// }
	// appendln(")");
	// return null;
	// }
	//
	// public Object visit(ASTInsertColumns node, Object data) {
	// appendln("(");
	// deep();
	// childrenAppendComma(node, data);
	// undeep();
	// appendln(")");
	// return null;
	// }

	public Object visit(ASTSelectColumns node, Object data) {
		returnFields = new BasicDBObject();
		return super.visit(node, data);
	}

	public Object visit(ASTSelectColumnsElement node, Object data) {
		returnFields.put(node.getLastToken().getImage(), 1);
		return null;
	}

	public Object visit(ASTQueryTableExpressionClause node, Object data) {
		SimpleNode n = (SimpleNode) node.jjtGetChild(0);
		if (n.getID() == WolfSQLParserTreeConstants.JJTSUBQUERY) {
			return null;
		} else {
			// [ IDDot() ] Identifier() ( AtID() )?
			if (node.getUpperSchema().length() > 0) {
				database = node.getSchemaString();
			}
			collection = node.getTableNameString();
			System.out.println("     f:" + node.getFirstToken());
			System.out.println("     s:" + node.getSecondToken());
			System.out.println("     t:" + node.getThirdToken());
			IToken third = node.getThirdToken();
			if (third != null) {
				IToken dot = third.getNext();
				if (dot != null && dot.getImage().equals(".")) {
					IToken tableNameTail = dot.getNext();
					System.out.println("     tail:" + tableNameTail);
					if (tableNameTail != null) {
						needsRety = true;
						collection = String.format("%s.%s", collection,
								tableNameTail.getImage());
					}
				}
			}
		}
		return data;
	}

	// public Object visit(ASTWhereClause node, Object data) {
	// appendln("WHERE");
	// deep();
	// SimpleNode n;
	// if (node.jjtGetNumChildren() <= 0) {
	// return data;
	// }
	// n = (SimpleNode) node.getChild(0);
	// if (_info.isSimpleCodition()) {
	// appendln(getLine(n));
	// } else {
	// n.jjtAccept(this, data);
	//
	// }
	// undeep();
	// return data;
	// }
	//
	public Object visit(ASTCondition node, Object data) {
		SimpleNode n;
		IToken[] tokens = node.getTokens();
		queryUsedToSearch = new BasicDBObject();
		System.out.println("quesy new!!!!!!");
		for (int ord = 0; ord < node.jjtGetNumChildren(); ord++) {
			n = (SimpleNode) node.jjtGetChild(ord);
			if (tokens.length > ord) {
				System.out.println("nazo[" + ord + "]=" + getLine(n) + " "
						+ ((Token) tokens[ord]).image);
			} else {
				System.out.println("nazo[" + ord + "]=" + getLine(n));
			}
			n.jjtAccept(this, data);
		}
		System.out.println("search=" + queryUsedToSearch);
		return data;
	}

	private static String cnvKind2MongoCondition(IToken t) {
		switch (t.getKind()) {
		case WolfSQLParserConstants.EQUAL:
			return null;
		case WolfSQLParserConstants.NOTEQUAL:
			return "$ne";
		case WolfSQLParserConstants.NOTEQUAL2:
			return "$ne";
		case WolfSQLParserConstants.NOTEQUAL3:
			return "$ne";
		case WolfSQLParserConstants.GREATER:
			return "$gt";
		case WolfSQLParserConstants.GREATEREQUAL:
			return "$ge";
		case WolfSQLParserConstants.LESS:
			return "$lt";
		case WolfSQLParserConstants.LESSEQUAL:
			return "$le";
		}
		return null;
	}

	public Object visit(ASTConditionElement node, Object data) {
		AbsSimpleNode[] children = ((SimpleNode) node).getChildren();
		if (children.length == 2) {

			if (children[0].getID() == WolfSQLParserTreeConstants.JJTEXPR) {
				String key = getLine(children[0]);
				String secondImage = children[1].getSecondToken().getImage();
				if (children[1].getID() == WolfSQLParserTreeConstants.JJTSIMPLECOMPARISONCONDITION) {
					IToken t = children[1].getFirstToken();
					if (t.getKind() == WolfSQLParserConstants.EQUAL) {
						queryUsedToSearch.put(key, secondImage);
					} else {
						if (queryUsedToSearch.containsKey(key)) {
							Object o = queryUsedToSearch.get(key);
							if (o instanceof BasicDBObject) {
								((BasicDBObject) o).append(
										cnvKind2MongoCondition(t), secondImage);
							} else {
								System.out.println("o=" + o);
							}
						} else {
							queryUsedToSearch.put(key, new BasicDBObject(
									cnvKind2MongoCondition(t), secondImage));
						}
					}
				} else if (children[1].getID() == WolfSQLParserTreeConstants.JJTNULLCONDITION) {
					if ("not".equalsIgnoreCase(secondImage)) {
						queryUsedToSearch.put(key, new BasicDBObject("$exists",
								true));
					} else {
						queryUsedToSearch.put(key, new BasicDBObject("$exists",
								false));
					}
				}
			}
		}
		return doJob(node, data);
	}

	public Object visit(ASTOrderByClause node, Object data) {
		orderBy = new BasicDBObject();
		return data;
	}

	public Object visit(ASTOrderByElement node, Object data) {
		String colName = node.getFirstToken().getImage();
		if ("desc".equalsIgnoreCase(node.getLastToken().getImage())) {
			orderBy.put(colName, -1);
		} else {
			orderBy.put(colName, 1);
		}
		return data;
	}
	//
	// public Object visit(ASTGroupByClause node, Object data) {
	// appendln("GROUP BY");
	//
	// deep();
	// IToken t3 = node.getThirdToken();
	// if (WolfSQLParserConstants.CUBE == t3.getKind()
	// || WolfSQLParserConstants.ROLLUP == t3.getKind()) {
	// appendln(t3.getImage() + " (");
	// deep();
	// childrenAppendComma(node, data);
	// undeep();
	// appendln(")");
	// } else {
	// childrenAppendComma(node, data);
	// }
	//
	// undeep();
	// return data;
	// }

}
