package hiro.yoshioka.sql.engine;

import hiro.yoshioka.ast.sql.util.BindInfo;
import hiro.yoshioka.sql.params.ConnectionProperties;
import hiro.yoshioka.sql.resource.DBRoot;
import hiro.yoshioka.sql.resource.IDBSchema;
import hiro.yoshioka.sql.resource.IDBTable;
import hiro.yoshioka.sql.resource.INameFamily;
import hiro.yoshioka.sql.resource.xml.DBTableValue;
import hiro.yoshioka.sql.util.CommentAnalyzer;
import hiro.yoshioka.sql.util.CommentInfo;
import hiro.yoshioka.util.StringUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.script.Invocable;
import javax.script.ScriptEngine;

public class TransactionRequest extends Request {
	private CommentInfo commentInfo;
	protected List<String> fSQLStatementList;

	Object[] fBindObjects;

	String fTitle;

	int timeOut;

	int maxRownum;

	long resultCount;

	protected IDBSchema schema;

	protected DBTableValue tableValue;

	protected List<INameFamily> tableList;

	protected List<String> selectColumnList;

	private List<Map<String, Object>> paramsList;

	public CommentInfo getCommentInfo() {
		return commentInfo;
	}

	public int getMaxRownum() {
		return maxRownum;
	}

	public void setMaxRownum(int maxRownum) {
		this.maxRownum = maxRownum;
	}

	public TransactionRequest(SQLOperationType operation,
			ConnectionProperties properties, INameFamily table) {
		this(operation, properties, null, null, table);
	}

	public TransactionRequest(SQLOperationType operation,
			ConnectionProperties properties, String sql_statement,
			Object[] binds, INameFamily table) {
		super(operation, properties);
		setSqlStatement(sql_statement);
		this.fBindObjects = binds;
		setTable(table);
		this.commentInfo = CommentAnalyzer.parse(sql_statement);
	}

	public Map<String, Object> getFirstParams() {
		if (this.paramsList == null || this.paramsList.size() == 0) {
			return Collections.EMPTY_MAP;
		}
		return paramsList.get(0);
	}

	public List<Map<String, Object>> getParamsList() {
		if (this.paramsList == null || this.paramsList.size() == 0) {
			return Collections.EMPTY_LIST;
		}
		return paramsList;
	}

	public void addParams(Map<String, Object> map) {
		if (this.paramsList == null || this.paramsList.size() == 0) {
			paramsList = new ArrayList<Map<String, Object>>();
		}
		paramsList.add(map);
	}

	private void setSqlStatement(String sql_statement) {
		if (this.fSQLStatementList == null) {
			this.fSQLStatementList = new ArrayList<String>();
		} else {
			this.fSQLStatementList.clear();
		}
		if (!StringUtil.isEmpty(sql_statement)) {
			this.fSQLStatementList.add(sql_statement);
		}
	}

	public void addSqlStatement(String sql_statement) {
		if (this.fSQLStatementList == null) {
			this.fSQLStatementList = new ArrayList<String>();
		}
		if (!StringUtil.isEmpty(sql_statement)) {
			this.fSQLStatementList.add(sql_statement);
		}
	}

	public void setSchema(IDBSchema schema) {
		this.schema = schema;
	}

	public IDBSchema getSchema() {
		if (schema == null) {
			return (IDBSchema) getIDBTable().getParent();
		}
		return schema;
	}

	private void setTable(INameFamily table) {
		if (this.tableList == null) {
			this.tableList = new ArrayList<INameFamily>();
		} else {
			this.tableList.clear();
		}
		this.tableList.add(table);
	}

	public void setTable(IDBTable table) {
		if (this.tableList == null) {
			this.tableList = new ArrayList<INameFamily>();
		} else {
			this.tableList.clear();
		}
		this.tableList.add(table);
	}

	private void addTable(IDBTable table) {
		if (this.tableList == null) {
			this.tableList = new ArrayList<INameFamily>();
		}
		this.tableList.add(table);
	}

	public TransactionRequest(SQLOperationType operation,
			ConnectionProperties properties, String sql_statement) {
		super(operation, properties);
		setSqlStatement(sql_statement);
		this.commentInfo = CommentAnalyzer.parse(sql_statement);
	}

	public TransactionRequest(SQLOperationType operation,
			ConnectionProperties properties, String[] sql_statements) {
		super(operation, properties);
		for (String sql : sql_statements) {
			addSqlStatement(sql);
		}
		if (sql_statements.length > 0) {
			this.commentInfo = CommentAnalyzer.parse(sql_statements[0]);
		}
	}

	public TransactionRequest(SQLOperationType operation,
			ConnectionProperties properties, IDBTable[] tables) {
		super(operation, properties);
		for (IDBTable table : tables) {
			addTable(table);
		}

	}

	public String getTitle() {
		return fTitle;
	}

	public int getTimeOut() {
		return timeOut;
	}

	public void setTimeOut(int timeOut) {
		this.timeOut = timeOut;
	}

	public String getSQLStatement() {
		if (this.fSQLStatementList == null
				|| this.fSQLStatementList.size() == 0) {
			return StringUtil.EMPTY_STRING;
		}
		return this.fSQLStatementList.get(0);
	}

	public INameFamily getNameFamily() {
		if (getIDBTable() == null) {
			return tableValue;
		}
		return getIDBTable();
	}

	public IDBTable getIDBTable() {
		if (tableList == null) {
			return null;
		}
		INameFamily f = tableList.get(0);
		if (f instanceof IDBTable) {
			return (IDBTable) f;
		}
		return null;
	}

	public String[] getSQLStatements() {
		if (this.fSQLStatementList == null
				|| this.fSQLStatementList.size() == 0) {
			return StringUtil.EMPTY_STRING_ARRAY;
		}
		return this.fSQLStatementList.toArray(new String[this.fSQLStatementList
				.size()]);
	}

	public void setPartName(String title) {
		fTitle = title;
	}

	public Object[] getBindObjects() {
		if (fBindObjects == null) {
			return new Object[0];
		}
		return fBindObjects;
	}

	public boolean hasBlob() {
		if (fBindObjects == null) {
			return false;
		}
		for (int i = 0; i < fBindObjects.length; i++) {
			if (fBindObjects[i] instanceof BindInfo) {
				if (((BindInfo) fBindObjects[i]).isBlob()) {
					return true;
				}
			}
		}
		return false;
	}

	@Override
	protected void before() {
		for (ScriptEngine engine : this.engines) {
			try {
				Invocable inv = (Invocable) engine;
				String[] strs = getSQLStatements();
				String[] immutableStatements = new String[strs.length];
				for (int i = 0; i < immutableStatements.length; i++) {
					immutableStatements[i] = strs[i];
				}
				engine.put("CI", getCommentInfo());
				inv.invokeFunction("before", immutableStatements, fBindObjects);
			} catch (NoSuchMethodException e) {
			} catch (Exception e) {
				log.fatal(e);
			}
		}
	}

	@Override
	protected void after() {
		for (ScriptEngine engine : this.engines) {
			try {
				Invocable inv = (Invocable) engine;
				engine.put("CI", getCommentInfo());
				inv.invokeFunction("after", fRdh, getIDBTable());
			} catch (NoSuchMethodException e) {
			} catch (Exception e) {
				log.fatal(e);
			}
		}
	}

	@Override
	public void done() {
		super.done();
		if (getIDBTable() != null) {
			DBRoot root = this.properties.getDBRootResource();
			if (root != null) {
				root.addRecentryUsedTable(getIDBTable());
			}
		}
	}

	public String[] getBindStrings() {
		if (fBindObjects == null) {
			return new String[0];
		}
		String[] ret = new String[fBindObjects.length];
		for (int i = 0; i < fBindObjects.length; i++) {
			if (fBindObjects[i] instanceof BindInfo) {
				ret[i] = StringUtil.nvl(((BindInfo) fBindObjects[i])
						.getStringValue());
			} else {
				ret[i] = StringUtil.EMPTY_STRING + fBindObjects[i];
			}
		}
		return ret;
	}

	public long getResultCount() {
		return resultCount;
	}

	public void setResultCount(long resultCount) {
		this.resultCount = resultCount;
	}

	public void setScription(String[] externalJars, File scriptFolder) {
		if (externalJars != null && externalJars.length > 0) {
			for (String jar : externalJars) {
				addExternalJar(new File(jar));
			}
		}
		if (commentInfo == null) {
			return;
		}
		for (String fileName : commentInfo.getInterceptorCommentList()) {
			File file = new File(scriptFolder, fileName);
			if (file.exists() && file.isFile()) {
				addInterceptor(file);
			}
		}
	}

	@Override
	protected Object getDoneArgObject() {
		switch (operation) {
		case SELECT_ALL:
		case EXECUTE_BAT:
		case PREPARED_EXECUTE_QUERY:
		case CREATE_TRIG_FNC_PROC:
		case PREPARED_EXECUTE:
		case UNID_EXECUTE_QUERY:
		case COUNTS:
			// if (getRdh() == null) {
			// log.info("rdh  is null");
			// } else {
			// log.info("rdh#count=" + getRdh().getRowCount());
			// }
			return getRdh();
		case COUNT:
			return resultCount;
		}
		return super.getDoneArgObject();
	}

	public IDBTable[] getIDBTables() {
		if (tableList == null) {
			return IDBTable.EMPTY_ARRAYS;
		}
		return tableList.toArray(new IDBTable[tableList.size()]);
	}

	public long getReturnedRowCount() {
		switch (operation) {
		case SELECT_ALL:
		case EXECUTE_BAT:
		case PREPARED_EXECUTE_QUERY:
		case CREATE_TRIG_FNC_PROC:
		case PREPARED_EXECUTE:
		case COUNTS:
			if (getRdh() == null) {
				log.info("rdh  is null");
				return -1;
			} else {
				log.info("rdh#count=" + getRdh().getRowCount());
				return getRdh().getRowCount();
			}
		case COUNT:
			return resultCount;
		}
		return -1;
	}

	public List<String> getSelectColumnList() {
		if (this.selectColumnList == null) {
			return Collections.EMPTY_LIST;
		}
		return this.selectColumnList;
	}

	public void setSelectColumnList(List<String> selectColumnList) {
		this.selectColumnList = selectColumnList;
	}

}