package hiro.yoshioka.sql.util;

import hiro.yoshioka.ast.sql.util.BindInfo;
import hiro.yoshioka.sql.engine.TransactionRequest;
import hiro.yoshioka.sql.resource.IDBTable;
import hiro.yoshioka.util.StringUtil;

import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class SQLHistroyManager {
	Log fLogger = LogFactory.getLog(getClass());
	String configFilename = "history.xml";

	private static SQLHistroyManager fSingle;

	List<SQLHistoryData> fHistoyList = new ArrayList<SQLHistoryData>();

	int fPointer = 0;

	public static SQLHistroyManager getInstance() {
		if (fSingle == null) {
			fSingle = new SQLHistroyManager();
		}
		return fSingle;
	}

	private SQLHistroyManager() {
		load();
	}

	private void resetPointer() {
		fPointer = 0;
	}

	public List<SQLHistoryData> getHistory() {
		return fHistoyList;
	}

	/**
	 * 
	 */
	public String go() {
		if (fHistoyList.size() == 0) {
			return StringUtil.EMPTY_STRING;
		}
		fPointer++;
		if (fPointer > fHistoyList.size() - 1) {
			resetPointer();
		}
		return fHistoyList.get(fPointer).getSQLStatement();
	}

	public String back() {
		if (fHistoyList.size() == 0) {
			return StringUtil.EMPTY_STRING;
		}
		fPointer--;
		if (fPointer < 0) {
			fPointer = fHistoyList.size() - 1;
		}
		return fHistoyList.get(fPointer).getSQLStatement();
	}

	public void addStatement(TransactionRequest req) {
		try {
			if (StringUtil.isEmpty(req.getSQLStatement())) {
				return;
			}
			int same = checkSame(req.getConnectionProperties()
					.getDisplayString(), req.getSQLStatement());
			StringBuffer buf = new StringBuffer();
			String[] binds = req.getBindStrings();
			for (int i = 0; binds != null && i < binds.length; i++) {
				buf.append(binds[i]).append(",");
			}
			if (buf.length() > 0) {
				buf.setLength(buf.length() - 1);
			}
			if (same >= 0) {
				SQLHistoryData data = fHistoyList.get(same);
				fHistoyList.remove(same);
				data.resetDate();
				data.fBindData = buf.toString();
				data.countUp();
				data.updatable = req.requestForUpdatable;
				fHistoyList.add(data);
				resetPointer();
				return;
			}
			IDBTable table = req.getIDBTable();
			SQLHistoryData record = new SQLHistoryData(req
					.getConnectionProperties().getDisplayString(),
					req.getSQLStatement(), buf.toString(),
					table == null ? StringUtil.EMPTY_STRING : table.getName());
			record.updatable = req.requestForUpdatable;
			record.setReturnedRows(req.getReturnedRowCount());
			fHistoyList.add(record);
			resetPointer();

		} catch (RuntimeException e) {
			e.printStackTrace();
		}
	}

	private int checkSame(String displayString, String statement) {
		String key = displayString + ":" + statement;

		for (int i = 0; i < fHistoyList.size(); i++) {

			String target = fHistoyList.get(i).getDisplayString() + ":"
					+ fHistoyList.get(i).getSQLStatement();

			if (whiteMatch(key, target)) {
				return i;
			}
		}

		return -1;
	}

	public boolean save() {
		XMLEncoder encoder = null;
		try {
			encoder = new XMLEncoder(new BufferedOutputStream(
					new FileOutputStream(getHistoryFile())));
			encoder.writeObject(fHistoyList);
			encoder.close();
			return true;
		} catch (Throwable e) {
			fLogger.error(e);
		}

		return false;
	}

	public void clear() {
		fHistoyList.clear();
		resetPointer();
	}

	public File getHistoryFile() {
		return new File(configFilename);
	}

	public boolean load() {
		XMLDecoder decoder = null;
		try {
			decoder = new XMLDecoder(new BufferedInputStream(
					new FileInputStream(getHistoryFile())));
			fHistoyList = (ArrayList<SQLHistoryData>) decoder.readObject();
			return true;
		} catch (FileNotFoundException e) {
			fLogger.error(e);
			clear();
		} finally {
			if (decoder != null) {
				decoder.close();
			}
		}

		return false;
	}

	private boolean whiteMatch(String s1, String s2) {
		return s1.replaceAll("\\W+", StringUtil.EMPTY_STRING).equals(
				s2.replaceAll("\\W+", StringUtil.EMPTY_STRING));
	}

	public String[] getBindValue(String target) {
		for (int i = 0; i < fHistoyList.size(); i++) {
			if (whiteMatch(target, fHistoyList.get(i).getSQLStatement())) {
				return fHistoyList.get(i).fBindData.split(",");
			}
		}
		return new String[0];
	}

	public void setBindValue(String target, BindInfo[] binds) {
		for (int i = 0; i < fHistoyList.size(); i++) {
			if (whiteMatch(target, fHistoyList.get(i).getSQLStatement())) {
				String[] b = fHistoyList.get(i).fBindData.split(",");
				for (int j = 0; binds != null && j < binds.length
						&& j < b.length; j++) {
					binds[j].setValue(b[j]);
				}
			}
		}
	}

	public List<SQLHistoryData> getHistory(String text) {
		if (text == null) {
			return fHistoyList;
		}
		ArrayList<SQLHistoryData> retList = new ArrayList<SQLHistoryData>();
		for (int i = 0; i < fHistoyList.size(); i++) {
			if (fHistoyList.get(i).getSQLStatement().toUpperCase()
					.indexOf(text.toUpperCase()) >= 0) {
				retList.add(fHistoyList.get(i));
			}
		}
		return retList;
	}

	public void remove(SQLHistoryData next) {
		fHistoyList.remove(next);
	}

}