// CS 349 Undo Demo

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Field;
import java.util.Observable;

import javax.swing.undo.*;

// A simple model that is undoable
public class Model extends Observable {

	// Undo manager
	private UndoManager undoManager;

	// Model data
	private int value;
	private int min;
	private int max;

	Model(int value, int min, int max) {
		undoManager = new UndoManager();
		this.value = value;
		this.min = min;
		this.max = max;
	}

	public void updateViews() {
		setChanged();
		notifyObservers();
	}

	public int getValue() {
		return value;
	}

	public void setValue(int v) {
		System.out.println("Model: set value to " + v);

		// create undoable edit
		UndoableEdit undoableEdit = new AbstractUndoableEdit() {

			// capture variables for closure
			final int oldValue = value;
			final int newValue = v;

			// Method that is called when we must redo the undone action
			public void redo() throws CannotRedoException {
				super.redo();
				value = newValue;
				System.out.println("Model: redo value to " + value);
				setChanged();
				notifyObservers();
			}

			public void undo() throws CannotUndoException {
				super.undo();
				value = oldValue;
				System.out.println("Model: undo value to " + value);
				setChanged();
				notifyObservers();
			}
		};

		// Add this undoable edit to the undo manager
		undoManager.addEdit(undoableEdit);

		// finally, set the value and notify views
		value = v;
		setChanged();
		notifyObservers();
	}

	public void incrementValue() {
		System.out.println("Model: increment value ");

		// constrain value to valid range
		if (value + 1 > max) return;

		// create undoable edit
		UndoableEdit undoableEdit = new AbstractUndoableEdit() {

			// Method that is called when we must redo the undone action
			public void redo() throws CannotRedoException {
				super.redo();
				value = value + 1;
				System.out.println("Model: redo value to " + value);
				setChanged();
				notifyObservers();
			}

			public void undo() throws CannotUndoException {
				super.undo();
				value = value - 1;
				System.out.println("Model: undo value to " + value);
				setChanged();
				notifyObservers();
			}
		};

		// Add this undoable edit to the undo manager
		undoManager.addEdit(undoableEdit);

		value = value + 1;
		setChanged();
		notifyObservers();
	}

	public void decrementValue() {
		System.out.println("Model: decrement value ");

		// constrain value to valid range
		if (value - 1 < min) return;

		// create undoable edit
		UndoableEdit undoableEdit = new AbstractUndoableEdit() {

			// Method that is called when we must redo the undone action
			public void redo() throws CannotRedoException {
				super.redo();
				value = value - 1;
				System.out.println("Model: redo value to " + value);
				setChanged();
				notifyObservers();
			}

			public void undo() throws CannotUndoException {
				super.undo();
				value = value + 1;
				System.out.println("Model: undo value to " + value);
				setChanged();
				notifyObservers();
			}
		};

		// Add this undoable edit to the undo manager
		undoManager.addEdit(undoableEdit);

		value = value - 1;
		setChanged();
		notifyObservers();
	}

	// could make these settable and undoable too
	public int getMin() { return min; }
	public int getMax() { return max; }


	// undo and redo methods
	// - - - - - - - - - - - - - -

	public void undo() {
		if (canUndo())
			undoManager.undo();
	}

	public void redo() {
		if (canRedo())
			undoManager.redo();
	}

	public boolean canUndo() {
		return undoManager.canUndo();
	}

	public boolean canRedo() {
		return undoManager.canRedo();
	}

}