import java.util.LinkedList;
import java.util.List;

import org.mozilla.interfaces.MediumVariant;
import org.virtualbox_4_1.AccessMode;
import org.virtualbox_4_1.DeviceType;
import org.virtualbox_4_1.IMedium;
import org.virtualbox_4_1.IProgress;
import org.virtualbox_4_1.IVirtualBox;
import org.virtualbox_4_1.IVirtualBoxErrorInfo;
import org.virtualbox_4_1.VirtualBoxManager;

public class DiffImageProblem {

	private static VirtualBoxManager manager;
	private static IVirtualBox virtualBox;
	private String imagePath;

	public DiffImageProblem(String vBoxPath, String imagePath) {
		if (System.getProperty("vbox.home") == null) {
			System.setProperty("vbox.home", vBoxPath);
		}
		manager = VirtualBoxManager.createInstance(null);
		virtualBox = manager.getVBox();
		this.imagePath = imagePath;
	}

	/**
	 * Create a new base storage
	 * 
	 * @param baseName
	 *            name of the base image
	 * @param hddSize
	 *            size in MB
	 */
	public void createBaseStorage(String baseName, long hddSize) {
		IMedium hdd = virtualBox.createHardDisk("vdi", imagePath + baseName
				+ ".vdi");
		IProgress createHdd = hdd.createBaseStorage(hddSize * 1024 * 1024,
				MediumVariant.Standard);
		createHdd.waitForCompletion(-1);
		if (createHdd.getCompleted()) {
			IVirtualBoxErrorInfo errorInfo = createHdd.getErrorInfo();
			while (errorInfo != null) {
				System.out.println((createHdd.getErrorInfo().getText()));
				errorInfo = errorInfo.getNext();
			}
		} else {
			System.out.println(("hdd.createBaseStorage was not completed."));
		}
	}

	/**
	 * Creates a new differential image, based on the given existing image chain
	 * 
	 * @param priorChain
	 *            already existing image chain, as List of Strings
	 * @param diffName
	 *            name for the new differential image
	 */
	public void createDiffImage(List<String> priorChain, String diffName) {
		IMedium hdd = null;
		for (String existingMedium : priorChain) {
			hdd = virtualBox.openMedium(imagePath + existingMedium + ".vdi",
					DeviceType.HardDisk, AccessMode.ReadWrite, false);
		}
		IMedium newMedium = virtualBox.createHardDisk("vdi",
				"/usr/share/tomcat6/VirtualBox VMs/" + diffName + ".vdi");
		IProgress progress = hdd.createDiffStorage(newMedium,
				MediumVariant.Diff);
		progress.waitForCompletion(-1);
	}

	/**
	 * Given a linear chain, this method tries to merge the last
	 * differentialImage and the base image. Afterwards should only exist one
	 * base image.
	 * 
	 * @param linearChain
	 *            existing image chain, as List of Strings
	 */
	public void mergeLinearChain(List<String> linearChain) {
		int numOfElements = linearChain.size();
		if (numOfElements > 1) {
			IMedium base = virtualBox.openMedium(imagePath + linearChain.get(0)
					+ ".vdi", DeviceType.HardDisk, AccessMode.ReadWrite, false);
			System.out.println("Medium 0: Type:" + base.getType());
			for (int i = 1; i < numOfElements - 1; i++) {
				IMedium med = virtualBox.openMedium(
						imagePath + linearChain.get(i) + ".vdi",
						DeviceType.HardDisk, AccessMode.ReadWrite, false);
				System.out.println("Medium " + i + ": Type:" + base.getType());

			}
			IMedium last = virtualBox.openMedium(
					imagePath + linearChain.get(numOfElements - 1) + ".vdi",
					DeviceType.HardDisk, AccessMode.ReadWrite, false);
			System.out.println("Medium " + (numOfElements - 1) + ": Type:"
					+ base.getType());

			IProgress progress = last.mergeTo(base);
			progress.waitForCompletion(-1);
		} else {
			System.out
					.println("Merging not possible. Reason: To few medium in this chain");
		}
	}

	/**
	 * Deletes all images of a given linear image chain
	 * 
	 * @param linearChain
	 *            existing image chain, as List of Strings
	 */
	public void deleteAllImages(List<String> linearChain) {
		for (String medium : linearChain) {
			IMedium hdd = virtualBox.openMedium(imagePath + medium + ".vdi",
					DeviceType.HardDisk, AccessMode.ReadWrite, false);
		}
		for (int i = linearChain.size() - 1; i >= 0; i--) {
			IMedium hdd = virtualBox.findMedium(imagePath + linearChain.get(i)
					+ ".vdi", DeviceType.HardDisk);
			IProgress progress = hdd.deleteStorage();
			progress.waitForCompletion(-1);
		}
	}

	public static void main(String[] args) {
		// NOTE: Set vBox and image folder path
		String vBoxPath = "/usr/lib/virtualbox";
		String imagePath = "/usr/share/tomcat6/VirtualBox VMs/";

		LinkedList<String> priorChain = new LinkedList<String>();
		DiffImageProblem dut = new DiffImageProblem(vBoxPath, imagePath);
		dut.createBaseStorage("someBase", 3L);
		// add someBase to imageList
		priorChain.add("someBase");
		dut.createDiffImage(priorChain, "someDiff");
		// add someDiff to imageList
		priorChain.add("someDiff");
		// trying to merge image someBase and someDiff results in an error
		try {
			dut.mergeLinearChain(priorChain);
		} catch (Exception e) {
			e.printStackTrace();
			// Delete images, that where needed for testing
			dut.deleteAllImages(priorChain);
		}
	}

}
