package at.ac.tuwien.lsdc.sched;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import at.ac.tuwien.lsdc.types.Application;
import at.ac.tuwien.lsdc.types.PhysicalMachine;
import at.ac.tuwien.lsdc.types.ScenarioType;
import at.ac.tuwien.lsdc.types.SchedulerEvent;
import at.ac.tuwien.lsdc.types.SchedulerEvent.EventType;
import at.ac.tuwien.lsdc.types.VirtualMachine;

public abstract class AbstractSchedulerWithMigration extends AbstractScheduler {

	public AbstractSchedulerWithMigration(int numPMs, int numCloudPartners, File schedulerLog,
			ScenarioType scenario) throws IOException {
		super(numPMs, numCloudPartners, schedulerLog, scenario);
	}

	@Override
	protected void handleEvents(HashMap<EventType, LinkedList<SchedulerEvent>> events) {
		super.handleEvents(events);
		runMigration();
	}

	/**
	 * Check if we can free up a VM to shut it down.
	 */
	protected void runMigration() {
		List<PhysicalMachine> pms = manager.getSortedPMs();
		// iterate through all the PMs (except the one with the highest utilization)
		for (int i = 0; i < (pms.size() - 1); i++) {
			PhysicalMachine currentPM = pms.get(i);
			if (currentPM.isRunning() && (currentPM.countCurrentlyRunningVMs() > 0)) {
				VirtualMachine currentVM = currentPM.getVirtualMachines().values().iterator()
						.next();
				for (Application app : currentVM.getApplications()) {
					
					// try to fit app on most utilized machine to get maximum utilization
					for (int j = pms.size()-1; j > i; j--) {
						PhysicalMachine nextPM = pms.get(j);
						if (nextPM.isRunning() && (nextPM.countCurrentlyRunningVMs() > 0)) {
							VirtualMachine nextVM = nextPM.getVirtualMachines().values().iterator()
									.next();
							if (deployApp(nextVM, app)) {
								currentVM.stopApplication(app);
//								currentVM.resizeDown(app);
								break;
							}
						}
					}
				}
				if (currentVM.getApplications().size() == 0) {
					currentPM.stopVirtualMachine(currentVM);
					if (currentPM.countCurrentlyRunningVMs() == 0) {
						manager.stopPhysicalMachine(currentPM.getId());
					}
				}
			}
		}
	}

	protected abstract boolean deployApp(VirtualMachine vm, Application app);

}
