#include "BasicStrategy.h"

BasicStrategy::BasicStrategy(int nproc){
    this->nproc = nproc;
}

int BasicStrategy::get_optimal_allocation(Job *j, Job *runq){
  
    Job *jptr = runq;
    // Number of CPU's in use by migratable jobs.
    int n_migrate_proc = 0;
    // Minimum number needed by migratable jobs.
    int min_used_proc = 0;
    int n_migrate_jobs = 0;
    int opt_alloc = 0;
    int max_alloc = 0;

    printf("in get_optimal_allocation\n");

    // Otherwise, let's find out the lower bound on number of used CPU's.
    while(jptr != NULL){
	if(jptr->type == MCHARM){
	    min_used_proc += jptr->min_proc;
	    n_migrate_proc += jptr->num_allocated_proc;
	    n_migrate_jobs ++;
	}
	else if(jptr->type == MPI)
	    min_used_proc += jptr->num_allocated_proc;
	jptr = jptr->next;
    }

    max_alloc = nproc - min_used_proc ; //!!!!
    opt_alloc = (n_migrate_proc + num_free_proc) / (n_migrate_jobs + 1);

    if(opt_alloc > max_alloc)
        opt_alloc = max_alloc;
    
    if(opt_alloc > j->max_proc)
	opt_alloc = j->max_proc;

    // Can we make available enough CPU's for this job ?
    if((opt_alloc < j->min_proc) && (j->min_proc <= max_alloc))
	opt_alloc = j->min_proc;

    return opt_alloc;
}

// returns -1 for failure, 1 for success.
// can we accept this job ?
int BasicStrategy::is_available(Job *j, Job *waitq, Job *runq){
    
    Job *jptr = runq;

    int opt_alloc = get_optimal_allocation(j, runq);
    
    if(opt_alloc >= j->min_proc)
	return 1;

    else return -1;
}

void BasicStrategy::opt_allocate(Job *j, Job *runq, int nalloc){
    
    Job *jptr = runq;
    int temp_proc = 0;
    int num_allocated = 0;

    printf("In opt_allocate \n");

    // First, let's give the job the free CPU's, if any are available.
    if(num_free_proc > 0){
	for(int i=0; i < nproc; i++)
	    if((num_allocated < j->max_proc) && free_proc_vector[i]){
		j->add_proc(i);
		free_proc_vector[i] = 0;
		num_free_proc --;
		num_allocated++;
	    }
	if(num_allocated == j->max_proc)
	    return;
    }

    // If the job still needs CPU's, let's reallocate them away from
    // the migratable jobs.
    //
    // The outermost loop is because we might have to go through the
    // Job List several times.  We are trying to be fair by taking
    // away only one CPU from each running job during each iteration.
    while(num_allocated < j->min_proc){
        // Let's go through the list of jobs.
	jptr = runq;
	while((jptr != NULL) && (num_allocated < j->min_proc)){
           // Let's find a migratable job with potentially available
           // CPU's.  We'll reallocate only one CPU for now.
	    if((jptr->type == MCHARM) &&
	       (jptr->num_allocated_proc > jptr->min_proc)){
		temp_proc = jptr->delete_proc();
		j->add_proc(temp_proc);
		num_allocated++;
	    }
	    jptr = jptr->next;
	}
    }

    // We've given the new job its min CPU's.  To be fair, we'll now
    // try and give it the average number of CPU's given to migratable
    // jobs.  We'll take away CPU's from migratable jobs which have
    // more CPU's than we do, without breaking their MIN CPU
    // constraint, of course.
    int alloc_flag = 0;
    int itr = 0;
    while(num_allocated < nalloc){
	
	if(jptr == NULL)
	    jptr = runq;
	while((jptr != NULL) && (num_allocated < nalloc)){
	    if((jptr->type == MCHARM) &&
	       (jptr->num_allocated_proc > jptr->min_proc)
	       &&(jptr->num_allocated_proc > j->num_allocated_proc + 1)){
		temp_proc = jptr->delete_proc();
		j->add_proc(temp_proc);
		num_allocated++;
		alloc_flag = 1;
	    }
	    jptr = jptr->next;
	}
	if((!alloc_flag) && (itr > 0))
	    break;
	alloc_flag = 0;
	jptr = runq;
	itr ++;
    }
}

// This function does the bulk of the work in allocating CPU's to a
// new job.  Although this function calculates the new bitmap for
// every migratable job, it does not tell the job to update itself.
void BasicStrategy::allocate_processors(Job *waitq, Job * runq){

    Job *jptr = waitq;
    int opt_alloc;

    printf("In allocate Proc %d\n", runq);

    while(jptr != NULL){
	opt_alloc = get_optimal_allocation(jptr, runq);
    
	if(opt_alloc >= jptr->min_proc)
	    opt_allocate(jptr, runq, opt_alloc);
	
	jptr = jptr->next;
    }	
    
    if(runq)
	expand_running_jobs(runq);
    return;
}

void BasicStrategy::expand_running_jobs(Job *runq){
    Job *jptr;
    int status;

    printf("In expand running jobs\n");

    if(num_free_proc == 0)
	return;

    jptr = runq;
    while((jptr->type != MCHARM) && (jptr != NULL) &&
	  (jptr->num_allocated_proc >= jptr->max_proc))
	jptr = jptr->next;

    if(jptr == NULL)
	return;

    int min_pes = jptr->num_allocated_proc;
    Job * min_proc_job = jptr;
    Job * startptr = jptr;

    for(int i=0; i<nproc; i++){
	if(!free_proc_vector[i])
	    continue;
	while(jptr != NULL){
	    if((jptr->type == MCHARM) && (jptr->num_allocated_proc < min_pes)
	       && (jptr->num_allocated_proc < jptr->max_proc)){
		min_proc_job = jptr;
		min_pes = jptr->num_allocated_proc;
	    }
	    jptr = jptr->next;
	}

	if(min_proc_job == NULL)
	    break;
	printf("adding processor %d to %d\n", i, min_proc_job->dbid);
	min_proc_job->add_proc(i);
	free_proc_vector[i] = 0;
	num_free_proc --;
	min_pes = nproc + 1;
	min_proc_job = NULL;
	jptr = startptr;
    }
}


