#  Base model sceduler, which tries to run
#  tasks, which fits to free processors
#  while there are free processors
#
#  It stops on FIRST "large" task
#
#  Also it tries to run "small" tasks, which
#  will be finished before next "large" task
#  will be planned to run.
#

use vars qw($cleo $thresh $addtime);

$cleo=1.02;


#
#  Values to pass 'small' tasks first
#  If 'small' task won't be finished later when all needed processors for 'big'
#   task will be awailable, then it will be passed.
#  If you specify theese variables, then 'small' task timelimit for calculations
#  would be grown by maximum of 2% (1.02) and 15 minutes (15*60 seconds).
#
$thresh=1.02;     # 2%
$addtime=15*60;   # 15 minutes

sub do_scedule(){
  my ($tasks,$res,@free);
  my ($i,$j,$nfree);

  my ($future_kills,$time,$need,$runmode);
  my ($lim1,$lim2);

  $time=time;

  # get arguments
  $tasks=shift;
  $res=shift;
  push @free, @_;

  # count free processors (do not forget reserved!)
  $nfree=scalar(@free)-$res;

  # can we run own tasks?
  $runmode = get_mode() & MODE_RUN_ALLOW;

  cleo_log("tasks: ".scalar(@$tasks));
  cleo_log("NFree: $nfree");

  # No work this time...
  return 0 if $nfree<1;

  #
  #  First main loop: run tasks while here are free processors
  #
  cleo_log("first main loop");
  for($i=0;$i<@$tasks;++$i){

    # ignore own tasks if run is disallowed by mode
    next if(!$runmode and $tasks->[$i]->{is_own});

    #!!!!!!!!!!!!!!!!!!!!  IMPORTANT  !!!!!!!!!!!!!!!!
    #
    # check if this task running wont violate any rules in queue
    # if it does (violates), then BLOCK it automatically (second arg=1)
    #
    next if(violates($tasks->[$i]->{id},1));

    # ignore blocked
    if(get_task_info($tasks->[$i]->{id},'blocked')){
      delete $tasks->[$i]->{id}; # exclude this task
      next;
    }

    # just log
    cleo_log("task $i: $tasks->[$i]->{id} np=$tasks->[$i]->{np}");


    # first TOO LARGE task
    last if(($tasks->[$i]->{np}>$nfree) and $tasks->[$i]->{is_own});

    # it fits! Run it!
    run($tasks->[$i]->{id});

    # correct number of free processors
    $nfree-=$tasks->[$i]->{np};

    # do not take it in account later
    delete $tasks->[$i]->{id};
  }

  cleo_log("end first main loop");

  # have we ran all tasks?
  return 0 unless exists $tasks->[$i];

  # count needed processors
  $need=$tasks->[$i]->{np}-$nfree;

  # start with next task
  ++$i;
  $future_kills=list_future();
  cleo_log("Future kills: ".join(";",keys(%$future_kills)));


  cleo_log("second main loop (try to run small tasks)");
  # for every planned finish of task
  foreach my $t (sort {$a->{time}<=>$b->{time}} keys(%$future_kills)){

    cleo_log("second: future timestamp $future_kills->{$t}->{id}; need $need");

    # first 'BIG' task will be runned here, so break
    last if($need<1);

    # test all tasks...
    for($j=$i;$j<@$tasks;++$j){
      next unless defined $tasks->[$j]->{id};

      # ignore own tasks if run is disallowed by mode
      next if(!$runmode and $tasks->[$i]->{is_own});

      # ignore blocked
      if(get_task_info($tasks->[$i]->{id},'blocked')){
        delete $tasks->[$i]->{id}; # exclude this task
        next;
      }
      cleo_log("second pass: task $tasks->[$j]->{id} np=$tasks->[$j]->{np} ($j) free: $nfree");

      #do not fit
      next if($tasks->[$j]->{np}>$nfree and $tasks->[$j]->{is_own});

      cleo_log("Fits!");

      # times when task will be ended if runned now
      $lim1=$time+$tasks->[$j]->{timelimit}*$thresh;
      $lim2=$time+$tasks->[$j]->{timelimit}+$addtime;

      cleo_log("new-cur ".($lim1-$time)." - ".($future_kills->{$t}->{time}-$time));
      cleo_log("new-cur ".($lim2-$time)." - ".($future_kills->{$t}->{time}-$time));

      # check, is this task 'SMALL'? (will be it finished in time?)
      if(($lim1<$future_kills->{$t}->{time}) and
         ($lim2<$future_kills->{$t}->{time})){

        # bad task (cannot be runned)
        next if(violates($tasks->[$j]->{id}));

        run($tasks->[$j]->{id});
        $nfree-=$tasks->[$j]->{np};
        delete $tasks->[$j]->{id};
      }
    }
    # correct need processors count
    $need-=$future_kills->{$t}->{np};
  }

  return 0;
}

