import java.util.*;

public class CloneFun 
{
	public static void main(String[] args) 
	{
		// Here we create two Shallow objects, the second being a shallow copy (clone) of the first.
		Shallow s = new Shallow(1, 2);
		Shallow sCopy = (Shallow) s.clone();
		sCopy.i = 3; // This change doesn't affect s.i.
		sCopy.a[0] = 4; // Since sCopy and s refer to the same array a, this changes both.
		System.out.println("s:\t" + s); // Note the change from 2 to 4 in both shared arrays a.
		System.out.println("sCopy:\t" + sCopy);

		// Here we create two Deep objects, the second being a deep copy (clone) of the first.
		Deep d = new Deep(1, 2);
		Deep dCopy = (Deep) d.clone();
		dCopy.i = 3; // This change doesn't affect d.i
		dCopy.a[0] = 4; // Since dCopy.a refers to a different array than d.a, this changes only dCopy.a
		System.out.println("d:\t" + d); 
		System.out.println("dCopy:\t" + dCopy); 
		
		// Alternative copy constructor approach:
		Deep2 d2 = new Deep2(1, 2);
		Deep2 d2Copy = (Deep2) d2.clone();
		d2Copy.i = 3; // This change doesn't affect d.i
		d2Copy.a[0] = 4; // Since dCopy.a refers to a different array than d.a, this changes only dCopy.a
		System.out.println("d2:\t" + d2); 
		System.out.println("d2Copy:\t" + d2Copy); 
		
		// This is an illustration of an undo stack making use of a deep clone to save copies for undo operations.
		System.out.println();
		System.out.println("Commands:");
		System.out.println("i <number> - assign <number> to i");
		System.out.println("a <number> - assign <number> to a[0]");
		System.out.println("undo - undo the previous assignment");
		System.out.println("quit - end the program");
		Stack<Deep> stack = new Stack<Deep>();
		boolean done = false;
		Scanner in = new Scanner(System.in);
		while (!done) {
			System.out.println("Current d:");
			System.out.println(d);
			System.out.print("? ");
			String command = in.nextLine();
			if (command.equals("quit"))
				done = true;
			else if (command.equals("undo")) {
				if (stack.isEmpty()) 
					System.out.println("Cannot undo");
				else
					d = stack.pop();
			}
			else {
				Scanner lineIn = new Scanner(command); // create a new Scanner to re-read the command line
				String varName = lineIn.next();
				int value = lineIn.nextInt();
				if (varName.equals("i")) {
					stack.push((Deep) d.clone()); // push a copy before making the change
					d.i = value;
				}
				else if (varName.equals("a")) {
					stack.push((Deep) d.clone()); // push a copy before making the change
					d.a[0] = value;
				}
				else {
					System.out.println("Illegal variable name");
				}
				lineIn.close();
			}
		}
		in.close();

	}
}

/*
 * This example class Shallow illustrates a shallow clone (copy) of an object.
 * Field i is a primitive (fundamental) type int, so i contains its value.
 * Field a is an instance (Object) type int[], so a contains a reference (location) of its value.  Arrays are objects.
 * The default clone method ("super.clone()") makes a shallow copy where:
 * - The copy's i is assigned i of this object.  The copy has it's own unique copy of i.
 * - The copy's a is assigned a of this object.  The copy has only a copy of this objects reference.
 * Therefore, both the copy and the original refer to the _same array object in memory_.
 * Whenever a change is made within the array of Shallow field a, that change is seen by all
 * Shallow objects that share that reference. 
 */

class Shallow implements Cloneable
{
	public int i;
	public int[] a;

	public Shallow(int i1, int i2) 
	{
		i = i1;
		a = new int[1];
		a[0] = i2;
	}

	public Object clone()
	{
		try {    
			return super.clone();
		}
		catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}

	public String toString() 
	{
		return "i:" + i +" a[0]:" + a[0];
	}
}

/*
 * In contrast, this example class Deep illustrates a deep clone (copy) of an object.
 * As before, the default clone method ("super.clone()") makes a shallow copy.
 * However, we then make a new array a for the "newDeep" copy, and we copy the single value from
 * this object's array a to the newDeep array a.
 * This ensures that both have independent arrays, holding different references to different memory locations.
 * Whenever a change is made within the array of Deep field a, that change is not seen beyond that object.
 * Deep cloned objects never share references to mutable objects. 
 */

class Deep implements Cloneable
{
	public int i;
	public int[] a;

	public Deep(int i1, int i2) 
	{
		i = i1;
		a = new int[1];
		a[0] = i2;
	}

	public Object clone()
	{
		try {  
			// Get a shallow clone...
			Deep newDeep = (Deep) super.clone();
			// ... and make it deeper as necessary.
			newDeep.a = new int[1];
			newDeep.a[0] = a[0];
			return newDeep;
		}
		catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}

	public String toString() 
	{
		return "i:" + i +" a[0]:" + a[0];
	}
}

/* 
 * Alternative copy contructor approach: Create a copy constructor.
 * The above example Deep class illustrates the original official way to perform deep cloning.
 * Increasingly, programmers are taking a simpler approach via the use of a
 * copy constructor that manually performs copying or deep copying of fields as necessary.
 * Upside: No try/catch exception handling is necessary.  It's conceptually simpler.
 * Downside: If one creates a new primitive (fundamental) field, one needs to remember to add 
 * corresponding code to the copy constructor.
 */

class Deep2 implements Cloneable
{
	public int i;
	public int[] a;

	public Deep2(int i1, int i2) 
	{
		i = i1;
		a = new int[1];
		a[0] = i2;
	}
	
	public Deep2(Deep2 original) 
	{
		i = original.i;
		a = new int[1];
		a[0] = original.a[0];
	}

	public Object clone()
	{
		return new Deep2(this);
	}

	public String toString() 
	{
		return "i:" + i +" a[0]:" + a[0];
	}
}
