package hiro.yoshioka.ast.sql.util;

import hiro.yoshioka.ast.sql.AbsSQLParser;
import hiro.yoshioka.ast.sql.AbsSimpleNode;
import hiro.yoshioka.ast.sql.IToken;
import hiro.yoshioka.ast.sql.oracle.SimpleNode;
import hiro.yoshioka.ast.sql.oracle.WolfSQLParser;
import hiro.yoshioka.ast.sql.oracle.WolfSQLParserTreeConstants;
import hiro.yoshioka.ast.sql.oracle.util.BetweenSQLNodeVisitor;
import hiro.yoshioka.ast.sql.oracle.util.BindCheckSQLNodeVisitor;
import hiro.yoshioka.ast.sql.oracle.util.CodeFormatingVisitor;
import hiro.yoshioka.ast.sql.oracle.util.FoldingSQLNodeVisitor;
import hiro.yoshioka.ast.sql.oracle.util.NullCompareNodeVisitor;
import hiro.yoshioka.ast.sql.oracle.util.PastCheckSQLNodeVisitor;
import hiro.yoshioka.ast.sql.oracle.util.SimpleSelectionVisitor;
import hiro.yoshioka.ast.sql.oracle.util.SpecialTokenCollectorVisitor;
import hiro.yoshioka.ast.sql.oracle.util.TableListFindSQLNodeVisitor;
import hiro.yoshioka.ast.sql.oracle.util.WolfParserExceptionCollatorSQLNodeVisitor;
import hiro.yoshioka.sql.engine.SQLOperationType;
import hiro.yoshioka.sql.engine.TransactionRequest;
import hiro.yoshioka.sql.params.ConnectionProperties;
import hiro.yoshioka.sql.resource.DBRoot;
import hiro.yoshioka.sql.util.BindDialog;
import hiro.yoshioka.sql.util.SQLHistroyManager;
import hiro.yoshioka.util.StringUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.eclipse.swt.widgets.Display;

public class WolfProposalParserUtil extends ProposalableParserUtil {

	public WolfProposalParserUtil(File file, DBRoot root)
			throws FileNotFoundException {
		super(file, root);
	}

	public WolfProposalParserUtil(String string, DBRoot root) {
		super(string, root);
	}

	public WolfProposalParserUtil(FileInputStream stream, DBRoot root) {
		super(stream, root);
	}

	@Override
	public DBRoot getDBRoot() {
		return fDBRoot;
	}

	@Override
	public TransactionRequest createTransactionRequest(ConnectionProperties p) {
		TransactionRequest req = null;
		if (startsWithTrigFncProc()) {
			dumpSQLStatement();
			req = new TransactionRequest(SQLOperationType.CREATE_TRIG_FNC_PROC,
					p, fSQL_Statement);
		} else if (canDoBatchExecute()) {
			req = new TransactionRequest(SQLOperationType.EXECUTE_BAT, p,
					getSQLStatements());
		} else {
			boolean doQuery = doQuery();
			SQLOperationType operationCode;
			Object[] bindsObjects = null;
			BindInfo[] binds = binds();
			if (binds == null || binds.length == 0) {
				fLogger.trace("BIND LENGTH 0"); //$NON-NLS-1$
				if (doQuery) {
					operationCode = SQLOperationType.PREPARED_EXECUTE_QUERY;
				} else {
					operationCode = SQLOperationType.PREPARED_EXECUTE;
				}
			} else {
				// TODO: bindDialog
				SQLHistroyManager.getInstance().setBindValue(getSQLStatement(),
						binds);
				BindDialog dialog = new BindDialog(Display.getCurrent()
						.getActiveShell(), binds);
				dialog.setBlockOnOpen(true);
				if (dialog.open() == BindDialog.CANCEL) {
					fLogger.info("CANSELED..............................."); //$NON-NLS-1$
					return null;
				}
				bindsObjects = new Object[binds.length];
				fLogger.info("BIND LENGTH [" + bindsObjects.length + "]"); //$NON-NLS-1$ //$NON-NLS-2$
				for (int i = 0; i < bindsObjects.length; i++) {
					bindsObjects[i] = binds[i];
					fLogger.info("BIND[" + i + "] [" + bindsObjects[i] //$NON-NLS-1$ //$NON-NLS-2$
							+ "] class[" + bindsObjects[i].getClass() + "]"); //$NON-NLS-1$ //$NON-NLS-2$
				}
				if (doQuery) {
					operationCode = SQLOperationType.PREPARED_EXECUTE_QUERY;
				} else {
					operationCode = SQLOperationType.PREPARED_EXECUTE;
				}
			}
			req = new TransactionRequest(operationCode, p, getSQLStatement(),
					bindsObjects, getFirstTable());
			try {
				if (doQuery) {
					SimpleSelectionVisitor selectionVisitor = new SimpleSelectionVisitor(
							getSQLStatement());
					selectionVisitor.visit((SimpleNode) getRoot(), null);
					req.setSelectColumnList(selectionVisitor
							.getSelectFieldList());
					// TODO: binds at where??
					//
					if (binds != null) {
						for (int i = 0; i < binds.length; i++) {
							Map<String, Object> map = new HashMap<String, Object>();
							map.put(binds[i].name, binds[i].value);
							req.addParams(map);

						}
					}

				}
			} catch (Exception e) {
			}
		}
		return req;
	}

	@Override
	public String getSpecialTokenString() {
		try {
			if (!this.fParser.nodeCreated()) {
				return StringUtil.EMPTY_STRING;
			}
			if (doneParse && flg) {
				SpecialTokenCollectorVisitor visitor = new SpecialTokenCollectorVisitor();
				return (String) getMySimpleNode().jjtAccept(visitor, null);
			}
			return breakKeyWord(this.fSQL_Statement);
		} catch (RuntimeException e) {
			return StringUtil.EMPTY_STRING;
		}
	}

	public String getFormattedString(ASTFormatingInfo info) {
		try {
			if (!this.fParser.nodeCreated()) {
				return this.fSQL_Statement;
			}
			if (doneParse && flg) {
				CodeFormatingVisitor visitor = new CodeFormatingVisitor();
				return (String) getMySimpleNode().jjtAccept(visitor, info);
			}
			return breakKeyWord(this.fSQL_Statement);
		} catch (RuntimeException e) {
			e.printStackTrace();
			return this.fSQL_Statement;
		}
	}

	private SimpleNode getMySimpleNode() {
		return (SimpleNode) getRoot();
	}

	/**
	 * @param assist
	 * @return
	 */
	protected ASTAssist createAssistWithBetween() {
		ASTAssist ret = null;
		SimpleNode node = getMySimpleNode();
		if (node == null) {
			return null;
		}

		BetweenSQLNodeVisitor betweenVisitor = new BetweenSQLNodeVisitor();
		Vector v = (Vector) node.jjtAccept(betweenVisitor,
				fRowColumn.backWord());
		if (fLogger.isInfoEnabled()) {
			fLogger.info("Between::" + v);
		}

		ret = betweenVisitor.fLastAssist;
		if (betweenVisitor.fLastAssist == null && v.size() > 0) {
			SimpleNode last = (SimpleNode) v.lastElement();
			ret = last.fASTAssist;
			if (ret == null) {
				if (betweenVisitor.fASTSchemaTableColumn != null) {
					if (fLogger.isInfoEnabled()) {
						fLogger.info("fASTSchemaTableColumn::"
								+ betweenVisitor.fASTSchemaTableColumn
										.infomation());
					}
					ret = new ASTAssist(ASTAssist.ASSIST_COLUMN,
							betweenVisitor.fASTSchemaTableColumn);
				} else if (betweenVisitor.fASTQueryTableExpressionClause != null) {
					if (fLogger.isInfoEnabled()) {
						fLogger.info(" :"
								+ betweenVisitor.fASTQueryTableExpressionClause
										.infomation());
					}
					ret = new ASTAssist(ASTAssist.ASSIST_TABLE,
							betweenVisitor.fASTQueryTableExpressionClause);
				}
				if (WolfSQLParserTreeConstants.JJTFROM == last.getID()) {
					if (fLogger.isInfoEnabled()) {
						fLogger.info("FROM :");
					}
					ret = new ASTAssist(ASTAssist.ASSIST_TABLE);
				}
			}
		}
		if (fLogger.isInfoEnabled()) {
			fLogger.info("ret::" + ret);
		}

		return ret;
	}

	boolean forceClip;

	/**
	 * @param assist
	 * @return
	 */
	protected ASTAssist createAssist(ASTAssist assist) {
		SimpleNode node = getMySimpleNode();
		if (node == null) {
			return assist;
		}
		boolean fFlg = fBackWord.isFollow();
		forceClip = false;

		PastCheckSQLNodeVisitor visit2 = new PastCheckSQLNodeVisitor();
		Vector v2 = (Vector) getMySimpleNode().jjtAccept(visit2, fRowColumn);
		fLogger.info(StringUtil.EMPTY_STRING + v2);

		if (v2.isEmpty()) {
			return assist;
		}
		SimpleNode lastElement = (SimpleNode) v2.lastElement();
		if (visit2.fAssist != null) {
			return visit2.fAssist;
		}

		boolean follow = fBackWord.startsWithOrFollow(tableCompletion);
		if (matcheClassPattern(v2, ASSIST_COLUMN_PATTERN_ORDER)) {
			fLogger.info("Match:ASSIST_COLUMN_PATTERN_ORDER:" + v2);
			assist = new ASTAssist(ASTAssist.ASSIST_COLUMN);

		} else if (matcheClassPattern(v2, ASSIST_COLUMN_PATTERN_GROUP) && fFlg) {
			fLogger.info("Match:ASSIST_COLUMN_PATTERN_GROUP:" + v2);
			assist = new ASTAssist(ASTAssist.ASSIST_COLUMN);

		} else if (matcheClassPattern(v2, ASSIST_TABLE_COMPLETION_DELETE)) {
			if (fLogger.isInfoEnabled()) {
				fLogger.info("Match:ASSIST_TABLE_COMPLETION_DELETE:" + v2);
			}
			assist = new ASTAssist(ASTAssist.ASSIST_TABLE);
		} else if (matcheClassPattern(v2, ASSIST_TABLE_COMPLETION)) {
			if (fLogger.isInfoEnabled()) {
				fLogger.info("Match:ASSIST_TABLE_COMPLETION follow[" + follow
						+ "]:" + v2);
			}
			if (PATTERN_WHERE_SPACE.matcher(
					getSQLStatement().replaceAll("\r|\n", " ")).matches()) {
				assist = new ASTAssist(ASTAssist.ASSIST_COLUMN);
				forceClip = true;
			} else if (follow) {
				assist = new ASTAssist(ASTAssist.ASSIST_TABLE);
			}
			String[] keys = tableCompletion;
			if (matcheClassPattern(v2, ".*GroupByClause.*")) {
				keys = new String[] { "HAVING ", "ORDER BY " };
			}
			assist.addKeyWords(keys);
		} else {
			fLogger.info("No Match:" + v2);
		}

		return assist;
	}

	public AbsSQLParser createParser(StringReader reader) {
		return new WolfSQLParser(reader);
	}

	public AbsSQLParser createParser(FileInputStream stream) {
		return new WolfSQLParser(stream);
	}

	protected List<ASTEditorTableListHolder> getASTEditorTableListHolder() {
		TableListFindSQLNodeVisitor visit = new TableListFindSQLNodeVisitor();
		visit.forceClipDML = forceClip;
		visit.visit((SimpleNode) fParser.getRoot(), fRowColumn.backWord());
		System.out.println("getASTEditorTableListHolder[" + visit.v + "]");
		return visit.getTableListHolder();
	}

	@Override
	public boolean parse() {
		boolean ret = super.parse();
		if (fParser.nodeCreated()) {
			NullCompareNodeVisitor visitor = new NullCompareNodeVisitor();
			visitor.visit((SimpleNode) fParser.getRoot(), null);
		}
		return ret;
	}

	public List<IToken> getAllErrorTokens() {

		if (fParser.nodeCreated()) {
			List<IToken> retList = super.getAllErrorTokens();
			WolfParserExceptionCollatorSQLNodeVisitor visitor = new WolfParserExceptionCollatorSQLNodeVisitor();
			Vector v = (Vector) visitor.visit((SimpleNode) fParser.getRoot(),
					null);
			retList.addAll(visitor.v);
			return retList;
		}
		return super.getAllErrorTokens();
	}

	@Override
	public BindInfo[] binds() {
		if (!fParser.nodeCreated()) {
			return BindInfo.EMPTY_ARRAYS;
		}
		BindCheckSQLNodeVisitor visitor = new BindCheckSQLNodeVisitor(
				getFirstTable());
		visitor.visit((SimpleNode) getRoot(), null);
		return (BindInfo[]) visitor.v.toArray(new BindInfo[0]);
	}

	@Override
	public AbsSimpleNode[] getFoldingNodes() {
		if (!fParser.nodeCreated()) {
			return null;
		}
		FoldingSQLNodeVisitor visitor = new FoldingSQLNodeVisitor();
		visitor.visit((SimpleNode) getRoot(), null);
		return (AbsSimpleNode[]) visitor.v.toArray(new AbsSimpleNode[0]);
	}
}
