#!/usr/bin/perl

use 5.038;
$|++;

my $N = 500;           # número de moviemientos a simular por año 
my @db;
my $ipos = 0.03 / 360;
my $ineg = -0.06 / 360;
my $anno = 2024;

my $saldoant = 0;
my ($sumoppos, $sumopneg);
for (1 .. 2){
# METODO HAMBURGUES DE CALCULO DE INTERESES
    annocompleto();   # operaciones 
    report();         # listado
    hamburgues();     # cálculo de inetereses pasivos y activos
    $anno++;
}
    
exit 2;

sub annocompleto {
    @db=();
    $db[0][0] = 0;
    $db[0][1] = 0;
    $db[0][2] = 0;
    $db[0][3] = $saldoant;
    ($sumoppos, $sumopneg) = (0,0);
    for my $c  (1 .. $N){
	my ($fechmov, $money) = simmov($anno); 
	$db[$c][0] = $fechmov;
	if ($money >= 0){
	    $db[$c][1] = $money;
	    $db[$c][2] = 0;
	    $sumoppos += $money;
	}else{
	    $db[$c][2] = abs($money);
	    $db[$c][1] = 0;
	    $sumopneg += abs($money);
	}
    }
    @db = sort { $a->[0] <=> $b->[0] } @db;
    for my $c (1 .. $N) {
	$db[$c][3] = sprintf "%.02f", ($db[$c-1][3] + $db[$c][1] - $db[$c][2]); 
    }
}


sub hamburgues {
    my ($comerpos, $comerneg) = (0,0);
    for my $i ( 1 .. $N ){
	if ($db[$i][3] > 0  && $i < $N ){  
	    $comerpos += $db[$i][3] * delta( $db[$i][0], $db[$i+1][0] );
	}elsif ($db[$i][3] <0 && $i < $N ) {
	    $comerneg += $db[$i][3] * delta( $db[$i][0], $db[$i+1][0] );
	}
    }
#    $comerneg = abs($comerneg); # si no, mal, - x - = +
    my $tiempo = 1;   # marco temporal fijo, año
    my $deveng = ($comerpos * $ipos * $tiempo) + ($comerneg * $ineg * $tiempo);
    $db[$N+1][3] = $db[$N][3] + $deveng; 
    $db[$N+1][0] = $db[$N][0];
    $db[$N+1][1]= ($deveng >= 0) ? $deveng : 0;
    $db[$N+1][2]= ($deveng < 0) ? abs($deveng) : 0;
    say "";
    say "Suma de operaciones de abono = ", $sumoppos;
    say "Suma de operaciones de cargo = ", $sumopneg;
    say "";
    say "Números comerciales acreedores = ", $comerpos;
    say "Números comerciales deudores = " , $comerneg;
    say "";
    say "Intereses devengados = ", sprintf ("%.02f" , $deveng);
    say "Saldo final = ", sprintf ("%.02f", $db[$N+1][3]);
    $saldoant = $db[$N+1][3];
}

sub report {
    say "\nAÑO $anno\n";
    say "Fecha                         DEBE                          HABER                           SALDO";
    say " " x 30, " " x 30, " " x 30, sprintf ("%.02f", $saldoant);
    for my $i (1 .. $N){
	print $db[$i][0], " "x (30 - length($db[$i][0]));
	print $db[$i][2], " "x (30 - length($db[$i][2]));         # la contabilidad del banco es inversa a una normal
	print $db[$i][1], " "x (30 - length($db[$i][1]));         # 0j0 con no mezclar los subíndices
	say $db[$i][3];
    }
}

sub simmov {
    my $importe = sprintf "%.2f", (rand()-0.48)*1000;         # desbalanceado para que salga positivo
    my $fecha = 0;
#    do{
#	$fecha = sprintf "%04d", int (1994+rand()*100);
	$fecha = shift;     # para simplificar, se toma un solo año pero puede ser cualquiera
	$fecha .= sprintf "%02d", int (1+rand()*12);
	$fecha .= sprintf "%02d", int (1+rand()*28);
#    }until (validfecha($fecha));
    
    return ($fecha, $importe);   # la fecha, cualquiera pero válida. Después se ordena 
}

sub infofecha{
    #    my $i = shift;
    my $i = scalar localtime();
    my @datetime = split " ", $i;
    
    my $anyo = $datetime[4];
    #    if ($anyo<66) { $anyo += 2000; }
    #    elsif ($anyo<100) { $anyo += 1900; }
    my %mes = (
	    Jan => "01",
	    Feb => "02",
	    Mar => "03",
	    Apr => "04",
	    May => "05",
	    Jun => "06",
	    Jul => "07",
	    Aug => "08",
	    Sep => "09",
	    Oct => "10",
	    Nov => "11",
	    Dec => "12",
	);
    
    my $nmes = $mes{ $datetime[1] };
    my $j = $datetime[2];
    #    $j-- if ($AYER);         # BUG GRATUITO INCLUIDO: No funciona el día 1 de cada mes
    my $dia = sprintf "%02d", $j;
    return $anyo  . $nmes   . $dia;
}

sub febrero {
    my $y = shift;
    if ($y % 400 == 0){   return 29;
    }elsif ( $y % 100 == 0){  return 28;
    }elsif ($y % 4 == 0){  return 29;
    }else{      return 28;  }
}

sub daysinmonth {
    my ($m, $y) = @_;   # el mes empieza en 1
    if ($m==1 || $m==3 || $m==5 || $m==7 || $m==8 || $m==10 || $m==12){ return 31; }
    if ($m==4 || $m==6 || $m==9 || $m==11){ return 30; }
    if ($m==2){ return ( febrero($y)); }
    #    if ($m==0){ return 0; }
    else{ return -1; }
}

sub delta {                     # delta_days casi
    my ($z, $x) = @_;             # todas fechas ISO, pueden ir desordenadas
    if ($z < $x){
	# correcto
    }else{
	($z, $x) = ($x, $z);    # $z menor o igual a $x
    }
    $z =~ /(\d{4})(\d{2})(\d{2})/;
    my ($za, $zm, $zd) = ($1, $2, $3);
    $x =~ /(\d{4})(\d{2})(\d{2})/;
    my ($xa, $xm, $xd) = ($1, $2, $3);
    for ($za,$zm,$zd,$xa,$xm,$xd){
	return 0 unless defined $_;
    }
    
#    my $flag = 0;   # no ok
    my $dias = 0;
    if ($xa >= $za){
	$dias += ($xa-$za)*360;
    }
    if ($xm >= $zm){
	$dias += ($xm-$zm)*30;
    }
    if ($xd >= $zd){
	$dias += ($xd-$zd);
    }
    return $dias;
}    

sub validfecha {
    my $z = shift;
    # ahora se trata de que $z sea correcta
    my ($za,$zm,$zd);
    if ($z =~ /(\d{4})(\d{2})(\d{2})/){
	($za, $zm, $zd) = ($1, $2, $3);
    }else{
	return 0;
    }
    
    if ($zd > daysinmonth( $zm, $za )){
	return 0;
    }
    if ($zm > 12){
	return 0;
    }
    if ($za > 2500 || $za < -50000){
	return 0;
    }
#    $zm = sprintf "%02d", $zm;
#    $zd = sprintf "%02d", $zd;
    return $za.$zm.$zd;
}
