package hiro.yoshioka.ast.sql.util;

import hiro.yoshioka.ast.sql.IQueryTableExpression;
import hiro.yoshioka.ast.sql.ISchemaTableColumn;
import hiro.yoshioka.ast.sql.IToken;
import hiro.yoshioka.ast.sql.RowColumn;
import hiro.yoshioka.ast.sql.oracle.Token;
import hiro.yoshioka.sql.engine.TransactionRequest;
import hiro.yoshioka.sql.params.ConnectionProperties;
import hiro.yoshioka.sql.resource.DBRoot;
import hiro.yoshioka.sql.resource.IDBColumn;
import hiro.yoshioka.sql.resource.IDBResource;
import hiro.yoshioka.sql.resource.IDBSchema;
import hiro.yoshioka.sql.resource.IDBTable;
import hiro.yoshioka.util.StringUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;

import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.swt.graphics.Image;

public abstract class ProposalableParserUtil extends ParserUtil implements
		IProposalableParserUtil {
	DBRoot fDBRoot;

	public abstract String getFormattedString(ASTFormatingInfo info);

	public abstract String getSpecialTokenString();

	public abstract TransactionRequest createTransactionRequest(
			ConnectionProperties p);

	public ASTAssist getASTAssist1() {
		try {
			ASTAssist assist = createAssistWithBetween();
			if (assist == null) {
				assist = new ASTAssist(ASTAssist.ASSIST_NOTHING);
				assist = createAssist(assist);
			}
			if (fParser.hasException()) {
				if (assist.isKey()) {
					assist.setExpedtedTokens(getExpectedTokens());
				}
				if (assist.isNothing()) {
					assist = new ASTAssist(ASTAssist.ASSIST_KEY_WORD);
					assist.setExpedtedTokens(getExpectedTokens());
				}
			}
			assist.setBackWord(fBackWord);
			return assist;
		} catch (Throwable e) {
			if (fLogger.isInfoEnabled()) {
				fLogger.info(StringUtil.EMPTY_STRING, e);
			}
			ASTAssist assist = new ASTAssist(ASTAssist.ASSIST_NOTHING);
			if (fParser.hasException()) {
				assist = new ASTAssist(ASTAssist.ASSIST_KEY_WORD);
				assist.setExpedtedTokens(getExpectedTokens());
			}
			return assist;

		}
	}

	protected abstract ASTAssist createAssistWithBetween();

	protected abstract ASTAssist createAssist(ASTAssist assist);

	public ProposalableParserUtil(File file, DBRoot root)
			throws FileNotFoundException {
		super(file);
		fDBRoot = root;
	}

	public ProposalableParserUtil(String string, DBRoot root) {
		super(string);
		fDBRoot = root;
	}

	/**
	 * @param stream
	 */
	public ProposalableParserUtil(FileInputStream stream, DBRoot root) {
		super(stream);
		fDBRoot = root;
	}

	public void clearAlias() {
		if (fDBRoot != null) {
			// fDBRoot.clearAlias();
		}
	}

	public boolean parse() {
		if (!doneParse) {
			doneParse = true;
			try {
				// if (fDBRoot != null) {
				// fDBRoot.clearAlias();
				// }
				if (fLogger.isInfoEnabled()) {
					fLogger.info("froot=" + fDBRoot);
				}
				flg = fParser.parse(fDBRoot);
				flg = flg && fParser.nodeCreated();
			} catch (Throwable e) {
				if (fLogger.isDebugEnabled()) {
					fLogger.debug(StringUtil.EMPTY_STRING, e);
				}
				flg = false;
			}

		}
		if (fLogger.isInfoEnabled()) {
			fLogger.info("Parsing result[" + flg + "]");
		}

		return flg;
	}

	public ASTAssist getASTAssist() {
		ASTProposalableAssist assist = new ASTProposalableAssist(
				getASTAssist1());
		if (fParser.nodeCreated()) {
			setProposalResource(assist);
		} else {
			// parse failed... Node Nothing
			String lastWord = getBackWord().getLastWord();
			if (StringUtil.isEmpty(lastWord)) {
				RowColumn b = fRowColumn.back();
				if (b != null) {
					// [UPDATE | DELETE | INSERT INTO ] following CTRL+SPACE
					createAssistTableOnNodeUncreated(getBackWordAt(b)
							.getLastWord(), assist, StringUtil.EMPTY_STRING);
				}
			} else { // etc UPDATE M_C...
				BackWord bw = getBackWord().getNextBackWord();
				createAssistTableOnNodeUncreated(bw.getLastWord(), assist,
						lastWord);
			}
		}
		String[] keys = assist.getKeyWords(fBackWord);
		for (int i = 0; i < keys.length; i++) {
			String keyword = keys[i] + " ";
			CompletionProposal comp = new CompletionProposal(keyword,
					fBackWord.getReplacementOffset(true),
					fBackWord.getReplacementLength(true), keyword.length());
			assist.addProposal(comp);
		}
		if (fLogger.isInfoEnabled()) {
			fLogger.info("assist last::" + assist);
		}
		return assist;

	}

	private void createAssistTableOnNodeUncreated(String lastOrTheNextWord,
			ASTProposalableAssist assist, String backWord) {
		for (String target : ASSIST_TABLE_NAMES) {
			if (lastOrTheNextWord.toUpperCase().equals(target)) {
				createTableProposal(assist, fDBRoot.getCurrentSchema()
						.startsWithResourceLists(backWord), " ");
				assist.resetAssistType2Table();
			}
		}
	}

	protected abstract List<ASTEditorTableListHolder> getASTEditorTableListHolder();

	/**
	 * @param assist
	 */
	private void setProposalResource(ASTProposalableAssist assist) {
		if (fLogger.isInfoEnabled()) {
			fLogger.info("assist=" + assist);
		}
		if (fDBRoot == null) {
			return;
		}
		if (assist.isColumn()) { // assist column
			List<ASTEditorTableListHolder> holders = getASTEditorTableListHolder();
			if (holders != null) {
				for (ASTEditorTableListHolder holder : holders) {
					ISchemaTableColumn columnInfo = assist.fASTSchemaTableColumn;
					if (columnInfo != null) {
						if (fLogger.isInfoEnabled()) {
							fLogger.info("assist column Info="
									+ columnInfo.infomation());
						}
						for (Iterator<IDBTable> ite = holder.iterator(); ite
								.hasNext();) {
							IDBTable table = ite.next();
							Set<String> tableAliasSet = fParser
									.getTableAliasSet(table);
							for (String alias : tableAliasSet) {
								if (alias.startsWith(columnInfo
										.getColumnString())) {
									// SELECT cXXXXX FROM FP.M_CODE co
									createAliasProposal(assist, table);
								}
							}

							IDBSchema schema = fDBRoot.getCurrentSchema();
							List<IDBResource> list = table
									.getProposal(schema,
											columnInfo.getSchemaString(),
											columnInfo.getTableString(),
											columnInfo.getColumnString(),
											tableAliasSet);
							if (fLogger.isInfoEnabled()) {
								fLogger.info("list=" + list);
							}
							createColumnProposal(assist, list, " ",
									holder.hasMultiTable(),
									columnInfo.containDot());
						}
					} else {
						if (holder == null) {
							return;
						}
						for (Iterator ite = holder.iterator(); ite.hasNext();) {
							IDBTable table = (IDBTable) ite.next();
							fLogger.debug("table=" + table);
							Set<String> tableAliasSet = fParser
									.getTableAliasSet(table);
							IDBSchema schema = fDBRoot.getCurrentSchema();

							String[] parts = fBackWord
									.getWords(BackWord.LEVEL_COLUMN);

							fLogger.info("schema[" + schema + "] parts:0["
									+ parts[0] + "] parts1:[" + parts[1]
									+ "] parts2:[" + parts[2] + "]");

							if (tableAliasSet.size() > 0) {
								String alias = tableAliasSet.iterator().next();
								if (fBackWord.endOfDot
										&& alias.equalsIgnoreCase(parts[2])) {
									// SELECT co.XXXXX FROM FP.M_CODE co
									parts[2] = StringUtil.EMPTY_STRING;
								}
							}
							List list = table.getProposal(schema, parts[0],
									parts[1], parts[2], tableAliasSet);
							createColumnProposal(assist, list, " ",
									holder.hasMultiTable(), fBackWord.hasDot);
						}
					}
				}
			}
		} else if (assist.isTable()) {
			fLogger.debug("assist.fASTQueryTableExpressionClause=["
					+ assist.fASTQueryTableExpressionClause + "]");

			IQueryTableExpression tableInfo = assist.fASTQueryTableExpressionClause;

			if (tableInfo == null) {
				String[] words = fBackWord.getWords(BackWord.LEVEL_TABLE);
				System.out.println("words[0]=" + words[0]);
				System.out.println("words[1]=" + words[1]);
				System.out.println("fBackWord.endOfDot:" + fBackWord.endOfDot);
				System.out.println("fBackWord.lastWord:["
						+ fBackWord.getLastWord() + "]");
				if (fBackWord.endOfDot) { // endof dot
					if (StringUtil.isEmpty(words[0])) {// nothing schema
						createTableProposal(
								assist,
								fDBRoot.getCurrentSchema()
										.startsWithResourceLists(
												StringUtil.EMPTY_STRING), " ");
					} else {
						IDBResource res = fDBRoot.getResource(words[0]);
						if (res != null && res instanceof IDBSchema) {
							createTableProposal(
									assist,
									res.startsWithResourceLists(StringUtil.EMPTY_STRING),
									" ");
						}
					}
				} else {
					if (StringUtil.isEmpty(words[0])) {// nothing schema
						IDBSchema schema = fDBRoot.getCurrentSchema();
						createTableProposal(assist,
								schema.startsWithResourceLists(words[1]), " ");

						createSchemaProposal(assist,
								fDBRoot.startsWithResourceLists(words[1]), ".");
					} else {
						IDBResource res = fDBRoot.getResource(words[0]);
						if (res != null && res instanceof IDBSchema) {
							createTableProposal(assist,
									res.startsWithResourceLists(words[1]), " ");
						}
					}
				}
				return;
			}
			fLogger.debug("ASTQueryTableExpressionClause Info="
					+ tableInfo.infomation());
			if (tableInfo.getSchemaString().length() > 0) {
				IDBSchema schema = (IDBSchema) fDBRoot.getResource(tableInfo
						.getSchemaString());
				if (schema != null) {
					createTableProposal(assist,
							schema.getResourceListStartsWith(tableInfo
									.getTableNameString()), " ");
				}
			} else {
				createTableProposal(
						assist,
						fDBRoot.getCurrentSchema().getResourceListStartsWith(
								tableInfo.getTableNameString()), " ");
				assist.addAll(fDBRoot.getSchemaListStartsWith(tableInfo
						.getTableNameString()));
			}
			fLogger.debug("assist.fProposalList=" + assist);
		}

	}

	/**
	 * @return
	 */
	public ASTProposalableAssist getASTProposalableAssist() {
		return (ASTProposalableAssist) getASTAssist();
	}

	private void createAliasProposal(ASTProposalableAssist assist,
			IDBTable table) {
		assist.add(table);

		String replacementString = fParser.getFirstTableAlias(table) + ".";

		assist.addProposal(fBackWord, replacementString,
				(Image) table.getImage(), table.toString());
		if (fLogger.isInfoEnabled()) {
			assist.dumpCnvText(fSQL_Statement, fBackWord, replacementString);
		}
	}

	private void createColumnProposal(ASTProposalableAssist assist,
			List<IDBResource> list, String sufix, boolean multi,
			boolean containDot) {
		assist.addAll(list);

		IDBResource column = null;
		Image image = null;
		for (int i = 0; i < list.size(); i++) {
			column = (IDBResource) list.get(i);
			String info = column.toString();
			String replacementString = column.getName() + sufix;
			if (column instanceof IDBColumn) {
				IDBColumn dColumn = (IDBColumn) column;
				image = (Image) dColumn.getImage();
				replacementString = dColumn.getProposalString() + sufix;
				if (multi) {
					IDBTable table = (IDBTable) column.getParent();
					info = column + "-" + table;
					if (!containDot) {
						String alias = fParser.getFirstTableAlias(table);
						if (alias.length() > 0) {
							replacementString = alias + "." + replacementString;
						}
					}
				}
			} else if (column instanceof IDBTable) {
			} else if (column instanceof IDBSchema) {
			} else {
				fLogger.error("unexpected class" + column);
				continue;
			}
			System.out.println("rep[" + replacementString + "]");
			assist.addProposal(fBackWord, replacementString, image, info);
			if (fLogger.isInfoEnabled()) {
				assist.dumpCnvText(fSQL_Statement, fBackWord, replacementString);
			}
		}
	}

	private void createTableProposal(ASTProposalableAssist assist,
			IDBTable table, String prefix) {
		assist.add(table);

		String replacementString = table.getName() + prefix;
		if (fParser.hasTableAliasSet(table.getName())) {
			replacementString = fParser.getFirstTableAlias(table) + prefix;
		}
		assist.addProposal(fBackWord, replacementString,
				(Image) table.getImage(), table.toString());
		if (fLogger.isInfoEnabled()) {
			assist.dumpCnvText(fSQL_Statement, fBackWord, replacementString);
		}
	}

	private void createSchemaProposal(ASTProposalableAssist assist,
			List<IDBResource> list, String prefix) {
		assist.addAll(list);

		IDBSchema schema = null;
		for (int i = 0; i < list.size(); i++) {
			IDBResource res = list.get(i);
			if (!(res instanceof IDBSchema)) {
				continue;
			}
			schema = (IDBSchema) res;
			// assist schema
			String replacementString = schema.getName() + prefix;

			assist.addProposal(fBackWord, replacementString,
					(Image) schema.getImage(), schema.toString());
			if (fLogger.isInfoEnabled()) {
				assist.dumpCnvText(fSQL_Statement, fBackWord, replacementString);
			}
		}
	}

	private void createTableProposal(ASTProposalableAssist assist,
			List<IDBResource> list, String prefix) {
		assist.addAll(list);

		IDBTable table = null;
		for (int i = 0; i < list.size(); i++) {
			IDBResource res = list.get(i);
			if (!(res instanceof IDBTable)) {
				continue;
			}
			table = (IDBTable) res;
			fLogger.debug("table[" + i + "][" + table + "]");
			if (table.isProcudeure() || table.isDictionary()) {
				continue;
			}
			// assist schema
			String replacementString = table.getName() + prefix;
			if (fParser.hasTableAliasSet(table.getName())) {
				replacementString = fParser.getFirstTableAlias(table) + prefix;
			}
			// fLogger.warning("replacementString[" + i + "][" +
			// replacementString + "]");
			try {
				// System.out.println("replacementString[" + replacementString +
				// "]");
				// System.out.println("replacementString.length[" +
				// replacementString.length() + "]");
				// System.out.println("fBackWord.getReplacementOffset[" +
				// fBackWord.getReplacementOffset() + "]");
				// System.out.println("fBackWord.getReplacementLength()[" +
				// fBackWord.getReplacementLength() + "]");

				assist.addProposal(fBackWord, replacementString,
						(Image) table.getImage(), table.toString());
				if (fLogger.isInfoEnabled()) {
					assist.dumpCnvText(fSQL_Statement, fBackWord,
							replacementString);
				}
			} catch (Throwable e) {
				e.printStackTrace();
				dump();
			}
		}
	}

	public List<IToken> getAllErrorTokens() {
		List<IToken> retList = new ArrayList<IToken>();
		if (!fParser.nodeCreated()) {
			Matcher m = TYPO_TRUNCATE_TABLE_PATTERN.matcher(getSQLStatement());
			if (m.find()) {
				Token token = new Token();
				token.image = m.group(1);
				// token.
				retList.add(token);
				System.out.println("fault:" + m.group(1));
			}
		}
		return retList;
	}

}