package hiro.yoshioka.sdh.diff;

import hiro.yoshioka.sdh.StringRecordDataHolder;
import hiro.yoshioka.sdh.pair.DifferenceStringData;
import hiro.yoshioka.sdh.pair.FlashStringData;
import hiro.yoshioka.util.Util;

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

public class DiffToResultSetDataHolder {
	protected Log fLogger = LogFactory.getLog(getClass());
	int[] fDifferenceKeys;

	StringRecordDataHolder fLeftTarget;

	StringRecordDataHolder fRightTarget;

	int sumDelete, sumInsert, sumUpdate;

	private REMARK_STAMP remark = REMARK_STAMP.VALUE;

	private boolean case_insensitive;

	public enum REMARK_STAMP {
		STRUCT, VALUE;

		public boolean isStruct() {
			return STRUCT.equals(this);
		}
	}

	public void diffConfigure(StringRecordDataHolder target) {
		fLeftTarget = target;
		fLeftTarget.setPairAll(new FlashStringData(DiffElementType.NO_CHANGE));
	}

	public StringRecordDataHolder getLeftTarget() {
		return fLeftTarget;
	}

	public StringRecordDataHolder getRightTarget() {
		return fRightTarget;
	}

	public void diffConfigure(StringRecordDataHolder targetLeft,
			StringRecordDataHolder targetRight, int[] matchKeys) {

		fRightTarget = targetRight;
		fLeftTarget = targetLeft;
		fDifferenceKeys = matchKeys;

		try {
			String[] leftHeader = fLeftTarget.getKey();
			String[] rightHeader = fRightTarget.getKey();
			for (int m : matchKeys) {
				fLogger.debug(" LEFT DIFF COLUMN[" + leftHeader[m] + "]");
				fLogger.debug(" RIGHT DIFF COLUMN[" + rightHeader[m] + "]");
			}
		} catch (Exception e) {
		}

		fLeftTarget.setPairAll(new DifferenceStringData(
				DiffElementType.ADDITION));
		fRightTarget.setPairAll(new DifferenceStringData(
				DiffElementType.ADDITION));
	}

	public void setCase_insensitive(boolean case_insensitive) {
		this.case_insensitive = case_insensitive;
	}

	private void diffEach(int row1, int row2) {
		try {
			int r1 = fLeftTarget.getIntDataDefaultZero(row1, "R");
			int r2 = fLeftTarget.getIntDataDefaultZero(row2, "R");
			if (r1 + 1 != r2) {
				return;
			}
		} catch (RuntimeException e) {
			return;
		}
		String[] keys = fLeftTarget.getKey();
		for (int i = 5; i < keys.length; i++) {
			if (!Util.matchCanNull(fLeftTarget.getRow(row1)[i],
					fLeftTarget.getRow(row2)[i])) {
				((DifferenceStringData) fLeftTarget.getPair(row1, i))
						.changeKind(fLeftTarget.getRow(row2)[i],
								DiffElementType.CHANGE);
				((DifferenceStringData) fLeftTarget.getPair(row2, i))
						.changeKind(fLeftTarget.getRow(row1)[i],
								DiffElementType.CHANGE);
			}
		}
	}

	private void init() {
		sumDelete = 0;
		sumInsert = 0;
		sumUpdate = 0;
	}

	// ROW, R, TIME, Operation, ...
	public void diff() {
		init();
		if (fRightTarget == null) {
			for (int i = 0; i < fLeftTarget.getRowCount(); i++) {
				String Operation = fLeftTarget.getStringData(i, "Operation");

				if ("I".equals(Operation)) {
					((DifferenceStringData) fLeftTarget.getPair(i, 3))
							.changeKind(DiffElementType.ADDITION);
					sumInsert++;
				} else if ("U".equals(Operation)) {
					((DifferenceStringData) fLeftTarget.getPair(i, 3))
							.changeKind(DiffElementType.CHANGE);
					if (i + 1 < fLeftTarget.getRowCount()) {
						diffEach(i, i + 1);
					}
					sumUpdate++;
				} else if ("D".equals(Operation)) {
					((DifferenceStringData) fLeftTarget.getPair(i, 3))
							.changeKind(DiffElementType.DELETION);
					sumDelete++;
				} else {
					System.out.println("bagu???????????? [" + Operation + "]");
				}
			}
		} else {
			for (int i = 0, ir = 0; i < fLeftTarget.getRowCount(); i++) {
				for (int j = ir; j < fRightTarget.getRowCount(); j++) {
					if (matchDifferenceKeys(i, j)) {
						match(fLeftTarget.getKey(), i, j);
					}
				}
			}
			for (int i = 0; i < fLeftTarget.getRowCount(); i++) {
				if (((DifferenceStringData) fLeftTarget.getPair(i, 0)).fDifferenceKind == DiffElementType.ADDITION) {
					sumDelete++;
				}
			}
			for (int i = 0; i < fRightTarget.getRowCount(); i++) {
				if (((DifferenceStringData) fRightTarget.getPair(i, 0)).fDifferenceKind == DiffElementType.ADDITION) {
					sumInsert++;
				}
			}
		}
	}

	public int getSumDelete() {
		return sumDelete;
	}

	public int getSumInsert() {
		return sumInsert;
	}

	public int getSumUpdate() {
		return sumUpdate;
	}

	public void setRemarks(REMARK_STAMP remark) {
		this.remark = remark;
	}

	public REMARK_STAMP getRemarks() {
		return remark;
	}

	public String getDiffInfomation() {
		if (sumDelete == 0 && sumInsert == 0 && sumUpdate == 0) {
			return "No Deifference";
		}
		return String.format("Update:%03d Insert:%03d Delete:%03d", sumUpdate,
				sumInsert, sumDelete);
	}

	private boolean match(String[] mkeys, int thisIndex, int targetIndex) {

		for (int i = 0; i < mkeys.length; i++) {
			if (i == 0
					|| Util.matchCanNull(fLeftTarget.getRow(thisIndex)[i],
							fRightTarget.getRow(targetIndex)[i],
							case_insensitive)) {
				((DifferenceStringData) fLeftTarget.getPair(thisIndex, i))
						.changeKind(DiffElementType.NO_CHANGE);
				((DifferenceStringData) fRightTarget.getPair(targetIndex, i))
						.changeKind(DiffElementType.NO_CHANGE);
			} else {
				((DifferenceStringData) fLeftTarget.getPair(thisIndex, i))
						.changeKind(fRightTarget.getRow(targetIndex)[i],
								DiffElementType.CHANGE);
				((DifferenceStringData) fRightTarget.getPair(targetIndex, i))
						.changeKind(fLeftTarget.getRow(thisIndex)[i],
								DiffElementType.CHANGE);
				sumUpdate++;
			}
		}
		return true;
	}

	// int getIndexByName(String name) {
	// for (int i = 0; i < key.length; i++) {
	// if (key[i].equals(name)) {
	// return i;
	// }
	// }
	// return 0;
	// }

	private boolean matchDifferenceKeys(int thisIndex, int targetIndex) {
		try {
			for (int i = 0; i < fDifferenceKeys.length; i++) {
				String left = fLeftTarget.getRow(thisIndex)[fDifferenceKeys[i]];
				String right = fRightTarget.getRow(targetIndex)[fDifferenceKeys[i]];
				boolean ret = Util.matchCanNull(left, right, case_insensitive);
				if (fLogger.isTraceEnabled()) {
					fLogger.trace(String.format(
							"  ROW[LEFT:%03d RIGHT:%03d][%s][%s][%b]",
							thisIndex, targetIndex, left, right, ret));
				}
				if (!ret) {
					return false;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return true;
	}

	@Override
	public String toString() {
		return getDiffInfomation();
	}
	// public DifferenceStringData[][] getDifferenceData() {
	// int cnt = getDataCount(key[0]);
	// DifferenceStringData[][] ret = new DifferenceStringData[cnt][key.length];
	// for (int irow = 0; irow < cnt; irow++) {
	// for (int icol = 0; icol < key.length; icol++) {
	// ret[irow][icol].fData = getData(key[icol], irow);
	// ret[irow][icol].fDifferenceKind = fDiffInfoRowCol[irow][icol];
	// }
	// }
	// return ret;
	// }
}