#!/usr/bin/perl -W

use strict;
use 5.030;

my $DEBUG = 1;

my $nlifts = 10;
my $nfloors = 50;

say "$nlifts LIFTS, $nfloors FLOORS";

my $delay = 0.3;    # segundos, tiempo en cruzar un piso 
my $stop = 5;      # segundos en una parada

my $timetick = 12;               # ESTO DETERMINA CADA CUANTO LLEGA UNA DEMANDA (segundos) 

my $PERIOD = 16 * 60 * 60;       # número de segundos en 16 horas

my @serie = 1 .. $nlifts;

for my $policy ("A","B","C","D"){
    say "";
    say "POLICY $policy";
    my @floor = (0) x ($nlifts+1);  # por ascensor, piso origen o destino
    my @timer = (0) x ($nlifts+1);  # por ascensor, tiempo para llegada 
    my @busy = (0) x ($nlifts+1);
    my $destiny;
    my $count = 0;      # el contador de iteraciones es por segundos
    my @queue = ();
    my $up = 0;
    my $down = 0;
    my $downup = 0;
    my $available = 0;
    my $success = 0;
    my $fail = 0; 
    my %hash = ();
    my $new = 0;           # personas que llegan al sistema

    while ($count < $PERIOD){   # PERIOD
#    order $this;
	if (@queue){
	    $destiny = shift @queue;
	    $hash{$destiny} = 0;
	}else{             # if ($new <= $count / $timetick){
	    $destiny = int rand()*($nfloors * 2);
	    if ($destiny > $nfloors){ $destiny = 0; }
	    else { $new++; }
	}
	$count += 1;
	print "\r$count" if $DEBUG;
	$success = 0;
	for my $i ( jlm(@serie) ){	
	    if ($busy[$i]==0 && $timer[$i]==0){          # disoccupied + no movement
		if ($floor[$i]==0){
		    $floor[$i] = $destiny;
		    $timer[$i] = $destiny * $delay + $stop;
		    $up++;
		    $success=1;
		}elsif ($floor[$i] > $destiny){
		    $timer[$i] = $delay * ($floor[$i]-$destiny) + $stop;
		    $floor[$i] = $destiny;
		    $down++;
		    $success=1;
		}elsif ($floor[$i] < $destiny){
		    $timer[$i] = $delay * $floor[$i] + $delay * $destiny + $stop;
		    $floor[$i] = $destiny;
		    $downup++;
		    $success=1;
		}elsif ($floor[$i] == $destiny){
		    # nothing, no lift trip
#		    last;
		}
		if ($success == 1){
		    $busy[$i]= 1;
		    last;
		}	
	    }
	}
	if ($success==0){    # NO HUBO EXITO Y HAY QUE ESPERAR
	    $hash{$destiny}++;
	    if ($hash{$destiny} == 1){
		push @queue, $destiny;
		$fail++;
	    }
	}
	for my $i (1 .. $nlifts){ # no aleatorizado por llevar un cierto orden de preferencia
	    $timer[$i] -= 1;                      # la unidad de tiempo de la simulación es el segundo
	    if ($timer[$i] <= 0){ 
		$timer[$i] = 0;
		$busy[$i] = 0;
#		$hash{$i} = 0;

		if ( typeoflift($i) ){              # implementación de qué hacer con los ascensores parados
		    if ($policy eq "A"){
			$available++;
			# do nothing policy
		    }elsif ($policy eq "B"){
			if ($floor[$i] == 0){
			    $available++;
			}elsif ($i > $nlifts/2){
			    # go to zero floor
#			    push @queue, 0;
			    $timer[$i] += $delay * $floor[$i];
			    $busy[$i] = 1;
			    $floor[$i] = 0;          # or a floor F($i)
			}
		    }elsif ($policy eq "C"){
			if ($floor[$i] == 0){
			    $available++;
			}elsif ($i > 4* $nlifts /5){  # heuristics by hand
			    $timer[$i] += $delay * $floor[$i];
			    $busy[$i] = 1;
			    $floor[$i] = 0;
			}
		    }elsif ($policy eq "D"){
			if ($floor[$i] == 0){
			    $available++;
			}elsif ($i == $nlifts){
			    $timer[$i] += $delay * $floor[$i];
			    $busy[$i] = 1;
			    $floor[$i] = 0;
			}
		    }
		}
	    }
	}
    }

    print "\n$up up and $downup trips downup y $down downs, total = ", $up+$downup+$down, "\n";
    say "of $new arraivals and $count requirements.";
    say "There were $fail faliures (none disponsable) and $available availables.";
}

exit 1;



sub typeoflift{
    return 1; # intelligent
    if (0){
	my $c = <<EOT;
	Necesita mayor reescritura. Los datos pueden ser generados on the fly
	  y lo que está claro es que el resultado tiene que ser de 4 o 5 ascensores
	  con orden de bajar al bajo y el resto sobrante repartidos casi uniformemente,
	  con refuerzo de la zona media. 
	Otra política alternativa o complementaria: si hay más de 2 ascensores abajo,
	  subirlos a donde proceda.

EOT
	  ;	
    }
}

sub jlm {
    my @deck = @_;
    my @deck2;
    my $n = scalar(@deck);
    while ($n){
	my $t = int $n*rand;
	push @deck2, splice(@deck,$t,1);
	--$n;
    }
    return @deck2;
}



