package edu.uprm.admg.nettraveler.sched;

import edu.uprm.admg.nettraveler.plan.ExecutionSite;
import edu.uprm.admg.nettraveler.plan.Plan;
import edu.uprm.admg.nettraveler.plan.VirtualSite;
import edu.uprm.admg.nettraveler.type.MIInteger;
import edu.uprm.admg.util.hash.TableEntry;

public class PerformanceSched extends AbstractScheduler {
	/**
	 * 
	 */
	private static final long serialVersionUID = 7636614333304697586L;
	private VirtualSite[] vSites = null;

	public PerformanceSched(int numPartitions) {
		super(numPartitions);
	}

	@Override
	protected boolean localExist(ExecutionSite site) {
		return this.stable.hasEntryForSite(site);
	}

	@Override
	protected Plan localGetPlan(ExecutionSite site) {
		return this.stable.getEntry(site).getPlan();
	}

	@Override
	protected void localOTSched() throws SchedulerException {
		Plan plan = null;
		TableEntry entry = null;
		// first set allocation factor for each site according to the plan
		for (int i = 0; i < this.vSites.length; i++) {
			this.vSites[i].evaluatePlans(this.schedPlan);
		}

		// second compare each vSite with each other
		for (int i = 0; i < this.vSites.length; i++) {
			this.vSites[i].setVirtualValue(this
					.assignVirtualValues(this.vSites[i]));
		}

		// third decide real values
		// combining allocation factor and virtual Value
		int temp = 0;
		for (int i = 0; i < this.vSites.length; i++) {
			if (this.vSites[i].getAllocationFactor() == 0)
				this.vSites[i].setRealValue(0);
			else {
				for (int j = 0; j < this.vSites.length; j++) {
					if (i != j) {
						if (this.vSites[j].getVirtualValue() != 0) {
							temp = this.getVirtual(this.vSites[i]
									.getVirtualValue(), this.vSites[j]
									.getVirtualValue());
							if (this.vSites[i].getRealValue() < temp)
								this.vSites[i].setRealValue(temp);
						}
					}
				}
			}
		}
		int totalValues = 0;
		for (int i = 0; i < this.vSites.length; i++) {
			totalValues += this.vSites[i].getRealValue();
		}

		// fourth assign buckets;
		for (int i = 0; i < this.vSites.length; i++) {
			if (this.vSites[i].getRealValue() != 0) {
				plan = this.assignBuckets(this.vSites[i].getSite(), this.vSites[i]
								.getRealValue()
								* ((Double) (Math.ceil(new Double(
										this.numPartitions)
										/ new Double(totalValues)))).intValue());
				entry = new TableEntry(this.vSites[i].getSite(), plan);
				this.stable.addEntry(entry);
			}
		}
	}

	private int assignVirtualValues(VirtualSite vSite) {
		int virtualValue = 0;
		for (int i = 0; i < this.vSites.length; i++) {
			if (vSite.getSite().getInfo().getId() != this.vSites[i].getSite()
					.getInfo().getId())
				virtualValue += vSite.compareWith(this.vSites[i]);
		}
		if (virtualValue < 1)
			virtualValue = 1;
		return virtualValue;
	}

	@Override
	protected void localRTSched() throws SchedulerException {
		throw new SchedulerException("Runtime scheduling still unsupported");
	}

	@Override
	protected void localInit() throws SchedulerException {
		this.vSites = new VirtualSite[this.sites.length];
		for (int i = 0; i < this.vSites.length; i++) {
			this.vSites[i] = new VirtualSite(this.sites[i], this.sites[i]
					.getService());
		}
	}

	@Override
	protected Plan assignBuckets(ExecutionSite site, int numBuckets)
			throws SchedulerException {
		int bucketSize = (numBuckets < this.stable.bucketsRemains()) ? numBuckets
				: this.stable.bucketsRemains();
		MIInteger[] buckets = new MIInteger[bucketSize];
		for (int i = 0; i < bucketSize; i++) {
			buckets[i] = this.stable.removeFirst();
		}
		return this.setBuckets(site, buckets);
	}

	private int getVirtual(float num, float den) {
		if (num > den && den != 0) {
			return new Double(Math.rint(num / den)).intValue();
		} else {
			return 1;
		}
	}
}
