package org.reldb.dbrowser.ui.content.cmd;

import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import org.eclipse.swt.custom.ExtendedModifyEvent;
import org.eclipse.swt.custom.ExtendedModifyListener;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Display;

/** Provide undo/redo support for a StyledText. */
public class UndoRedo {

	private static class Capture {
		private String content;
		private int caretOffset;
		private int topIndex;

		public Capture(StyledText text) {
			this.content = text.getText();
			this.caretOffset = text.getCaretOffset();
			this.topIndex = text.getTopIndex();
		}

		public void restore(StyledText text) {
			text.setText(content);
			text.setCaretOffset(caretOffset);
			text.setTopIndex(topIndex);
		}
		
		public String toString() {
			return content;
		}
	}

	private StyledText text;
	private Vector<Capture> buffer = new Vector<Capture>();
	private int currentUndoPoint = -1;

	private Timer quietTimer = new Timer();

	private boolean captured = false;
	private boolean ignore = false;

	private void capture() {
		if (!(buffer.size() > 0 && text.getText().equals(buffer.get(buffer.size() - 1).toString()))) {
			buffer.add(new Capture(text));
			if (buffer.size() > 100)
				buffer.remove(0);
		}
		currentUndoPoint = -1;
		captured = true;
	}

	private void restore() {
		ignore = true;
		buffer.get(currentUndoPoint).restore(text);
		ignore = false;
	}

	private ExtendedModifyListener extendedModifyListener = new ExtendedModifyListener() {
		@Override
		public void modifyText(ExtendedModifyEvent event) {
			if (ignore)
				return;
			captured = false;
			currentUndoPoint = -1;
			quietTimer.cancel();
			quietTimer = new Timer();
			quietTimer.schedule(new TimerTask() {
				@Override
				public void run() {
					quietTimer.cancel();
					Display.getDefault().syncExec(() -> capture());
				}
			}, 250);
		}
	};

	public UndoRedo(StyledText text) {
		this.text = text;
		text.addExtendedModifyListener(extendedModifyListener);
	}

	public void undo() {
		quietTimer.cancel();
		if (!captured)
			capture();
		if (currentUndoPoint == 0)
			return;
		if (currentUndoPoint < 0)
			currentUndoPoint = buffer.size() - 1;
		currentUndoPoint--;
		if (currentUndoPoint < 0)
			return;
		restore();
	}

	public void redo() {
		quietTimer.cancel();
		if (currentUndoPoint == -1 || currentUndoPoint >= buffer.size() - 1)
			return;
		currentUndoPoint++;
		restore();
	}

	public void dispose() {
		text.removeExtendedModifyListener(extendedModifyListener);
	}
}
