package edu.uprm.admg.nettraveler.sched;

import java.util.Random;

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 RandomPerformanceSched extends AbstractScheduler {
	/**
	 * 
	 */
	private static final long serialVersionUID = -6974619569338311724L;
	private VirtualSite[] vSites = null;
	private Random rand = null;

	public RandomPerformanceSched(int numPartitions) {
		super(numPartitions);
		this.rand = new Random();
	}

	@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;
		for (int i = 0; i < this.vSites.length; i++) {
			this.vSites[i].evaluatePlans(this.schedPlan);
		}
		//first set priority 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
		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);
						}
					}
				}
			}
		}
		
		float allocFactor = 0f;
		for(int i = 0;i<this.vSites.length;i++){
			allocFactor+=this.vSites[i].getAllocationFactor();
		}
		
		int totalValues = 0;
		for(int i = 0;i<this.vSites.length;i++){
			totalValues+=this.vSites[i].getRealValue();
		}
		
		//decidir este dato despues de observarlo
		if(allocFactor>0.5){
			//fourth assign buckets;
			for(int i=0;i<this.vSites.length;i++){
				this.assignBuckets(this.vSites[i].getSite(), this.vSites[i].getRealValue()*(this.numPartitions/totalValues));
			}
		} else {
			int bucketsRemains = this.numPartitions;
			int buckets;
			for(int i = 0; i<this.sites.length&&bucketsRemains>0; i++){
				buckets = rand.nextInt(bucketsRemains);
				plan = this.assignBuckets(this.sites[i], buckets);
				entry = new TableEntry(this.sites[i],plan);
				this.stable.addEntry(entry);
				bucketsRemains-=buckets;
			}
			if(bucketsRemains>0){
				plan = this.assignBuckets(this.sites[this.sites.length-1], bucketsRemains);
				entry = new TableEntry(this.sites[this.sites.length-1],plan);
				this.stable.addEntry(entry);
			} 
		}
	}

	@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 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;
	}
	
	private int getVirtual(float num, float den){
		if (num > den && den != 0) {
			return new Double(Math.rint(num / den)).intValue();
		} else {
			return 1;
		}
	}

}
