package SPVM::Sort : precompile {
  use SPVM::Comparator;
  use SPVM::Util(new_object_array_proto);

  native sub sortb : void ($nums : byte[]);
  native sub sorts : void ($nums : short[]);
  native sub sorti : void ($nums : int[]);
  native sub sortl : void ($nums : long[]);
  native sub sortf : void ($nums : float[]);
  native sub sortd : void ($nums : double[]);

  sub _merge : void($a : oarray, $b : oarray, $left : int, $mid : int, $right : int, $n : int, $comparator : SPVM::Comparator) {
      my $i = $left;
      my $j = $mid + 1;
      my $k = 0;
      while ($i <= $mid && $j <= $right) {
          $i = $left;
          $j = $mid + 1;
          $k = 0;
          while ($i <= $mid && $j <= $right) {
              if ($comparator->($a->[$i], $a->[$j]) <= 0) {
                  $b->[$k] = $a->[$i];
                  $i++;
              } else {
                  $b->[$k] = $a->[$j];
                  $j++;
              }
              $k++;
          }
          if ($i == $mid + 1) {
              while ($j <= $right) {
                  $b->[$k] = $a->[$j]; 
                  $j++;
                  $k++;
              }
          } else {
              while($i <= $mid) {
                  $b->[$k] = $a->[$i];
                  $i++;
                  $k++;
              }
          }
      }
      for ($i = 0; $i < $k; $i++) {
        $a->[$i + $left] = $b->[$i];
      }
  }

  sub _merge_sort : void($a : oarray, $b : oarray, $left : int, $right : int, $n : int, $comparator : SPVM::Comparator){
      if ($left >= $right) {
        return;
      }

      my $mid = ($left + $right) / 2;

      _merge_sort($a, $b, $left, $mid, $n, $comparator);
      _merge_sort($a, $b, $mid + 1, $right, $n, $comparator);
      _merge($a, $b, $left, $mid, $right, $n, $comparator);
  }
  
  sub sorto : void ($objs : oarray, $comparator : SPVM::Comparator) {
    if ($objs == undef) {
      die "Array must be defined";
    }
    my $length = @$objs;
    my $b = new_object_array_proto($objs, $length);
    _merge_sort($objs, $b, 0, $length - 1, $length, $comparator);
  }

  sub sortstr : void ($strs : string[]) {
    sorto($strs, sub : int ($self : self, $a : object, $b : object) {
      if (!$a || !$b) {
        die "Can't compare undef value";
      }
      
      if ((string)$a le (string)$b) {
        return -1;
      }
      elsif ((string)$a ge (string)$b) {
        return 1;
      }
      else {
        return 0;
      }
    });
  }
}