#!/usr/bin/perl

# ---------------------------------------------------------------
# Prontus CMS
# http://www.prontus.cl
# by Altavoz.net
#
# licensed under LGPL license.
# http://www.prontus.cl/license.html
# ---------------------------------------------------------------

# -------------------------------COMENTARIO GLOBAL---------------
# ---------------------------------------------------------------
# PROPOSITO.
# -----------
# Funciones para generar thumbnails
# El output de JPEGs usa la varglb NIVEL_OPTIMIZACION_JPG. El valor
# de esa variable (por defecto 85) está determinado por el usuario
# si la varglb REDUCIR_CALIDAD_JPEGS está seteada a SI,
# y está seteado a 100 de otro modo.
# ---------------------------------------------------------------

# ---------------------------------------------------------------
# DECLARACIONES GLOBALES.
# ------------------------

package lib_thumb;
use strict;
use utf8;
use glib_fildir_03;
require GD;
require prontus_varglb;

sub make_thumbnail {
    # Genera thumb de la imagen jpg o gif, basandose en el anchomax, altomax o en ambos.
    # Retorna: anchofinal, altofinal, buffer binario de la imagen.

    my $anchomax = $_[0];
    my $altomax = $_[1];
    my $lafoto = $_[2]; # full path
    my $cuadrar = $_[3];

    GD::Image->trueColor(1);

    my $proporcionmaxima; # Regla fija para mantener imagenes largas bajo control.
    my $ratio; # Razon de proporcionalidad para el resize.
    my ($ancho,$alto,$tipo,$anchofinal,$altofinal); # Ancho, alto y tipo (gif o jpg) de la foto.
    my $imagen; # Objeto GD que contiene la imagen original.
    my $thumb;  # Objeto GD que contiene el thumbnail.
    my ($offsetx, $offsety, $anchocrop, $altocrop);

    # Si no se especifica ni ancho ni alto maximo, no hace nada.
    if ( (($anchomax eq '')||($anchomax == 0))
    && (($altomax eq '')||($altomax == 0)) ){
        # print "Error: No hay indicacion de ancho ni alto maximo.\n"; # debug
        return ('','','');
    }

    # Determina si se trata de un GIF, un JPG u otro.
    $tipo = &get_imag_extension($lafoto);
    if (!$tipo) {
        print STDERR "Error: El tipo de archivo no es valido. Debe ser .jpg  .gif o .png\n";
        return ('','','');
    }

    # Lee la imagen de origen
    open(my $IN, '<', $lafoto) || return ('','','');
    if ($tipo eq 'jpg') {
        ref ($imagen = GD::Image->newFromJpeg(*$IN)) || return ('','','');
    } elsif ($tipo eq 'png') {
        ref ($imagen = GD::Image->newFromPng(*$IN)) || return ('','','');
    } elsif ($tipo eq 'gif') {
        ref ($imagen = GD::Image->newFromGif(*$IN)) || return ('','','');
    } else {
        close $IN;
        return ('','','');
    }
    close $IN;

    # Se obtienen las dimensiones de la imagen
    ($ancho,$alto) = $imagen->getBounds();

    # Determina el lado que se ajustara.
    if ( ($altomax eq '') || ($altomax == 0) ) {
        # Ajusta el ancho.
        $ratio = $ancho / $anchomax;
        $offsetx = 0;
        $offsety = 0;
        $anchocrop = $ancho;
        $altocrop = $alto;

        if ($ratio == 0) { $ratio = 1; };
        return ('','','') if ($ratio <= 1);
        # print STDERR "RATIO[$ratio]\n;";
        ($anchofinal, $altofinal) = (int $ancho/$ratio, int $alto/$ratio);

    } elsif ( ($anchomax eq '') || ($anchomax == 0) ) {
        # Ajusta el alto.
        $ratio = $alto / $altomax;
        $offsetx = 0;
        $offsety = 0;
        $anchocrop = $ancho;
        $altocrop = $alto;

        if ($ratio == 0) { $ratio = 1; };
        return ('','','') if ($ratio <= 1);
        # print STDERR "RATIO[$ratio]\n;";
        ($anchofinal, $altofinal) = (int $ancho/$ratio, int $alto/$ratio);

    } else {

        if ($cuadrar ne 'si') {
            # Ajusta el lado proporcionalmente mayor.
            $proporcionmaxima = $altomax/$anchomax;
            # Si la proporcion de ancho a alto es > proporcionmaxima entonces fija el alto en vez del ancho.
            if ($ancho * $proporcionmaxima < $alto) {
                $ratio = $alto / $altomax;
            }else{
                $ratio = $ancho / $anchomax;
            };
            $offsetx = 0;
            $offsety = 0;
            $anchocrop = $ancho;
            $altocrop = $alto;

            if ($ratio == 0) { $ratio = 1; };
            return ('','','') if ($ratio <= 1);
            # print STDERR "RATIO[$ratio]\n;";
            ($anchofinal, $altofinal) = (int $ancho/$ratio, int $alto/$ratio);

        } else {
            # Ajusta el lado proporcionalmente mayor.
            $proporcionmaxima = $anchomax / $altomax;
            $ratio = $ancho / $alto;
            if($ratio > $proporcionmaxima) {
                $anchocrop = $proporcionmaxima*$alto;
                $altocrop = $alto;
                $offsetx = ($ancho - $anchocrop)/2;
                $offsety = 0;

            } elsif($ratio < $proporcionmaxima) {
                $anchocrop = $ancho;
                $altocrop = $ancho/$proporcionmaxima;
                $offsetx = 0;
                $offsety = ($alto - $altocrop)/2;

            } else {
                $offsetx = 0;
                $offsety = 0;
                $anchocrop = $ancho;
                $altocrop = $alto;
            }
            $anchofinal = $anchomax;
            $altofinal = $altomax;
        }
    }

    if ($tipo eq 'jpg') {
        $thumb = new GD::Image($anchofinal,$altofinal);
    } else{
        # Si el formato es gif o png, usa paleta de 8 bits.
        # $thumb = new GD::Image($anchofinal,$altofinal,0);
        # CVI - Para evitar error con PNG muy complejos
        $thumb = new GD::Image($anchofinal,$altofinal,1);
        $thumb->alphaBlending(0);
        $thumb->saveAlpha(1)
    }

    # Crea el thumbnail.
    $thumb->copyResampled($imagen,0,0,$offsetx,$offsety,$anchofinal,$altofinal,$anchocrop,$altocrop);

    # No graba, sino que retorna el buffer binario de la foto para ser guardada por quien invoco a la funcion.
    my $bin_buffer;

    if ($tipo eq 'jpg') {
        $bin_buffer = $thumb->jpeg($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'});
    } else {
        $bin_buffer = $thumb->png();
    }

    return ($bin_buffer, $anchofinal, $altofinal);
}

# Revisa el tipo de imagen abriéndola para ver si coincide con su extensión.
# recibe el path al archivo de foto
# retorna 'gif', 'jpg', 'png', o ''.
sub get_tipo {
    my $lafoto = shift; # full path

    my $tipo = ''; # tipo (gif, png o jpg) de la foto.
    my $imagen; # Objeto GD que contiene la imagen original.

    # Determina si se trata de un GIF, un JPG u otro.
    $tipo = &get_imag_extension($lafoto);
    if (!$tipo) {
        print STDERR "Error: El tipo de archivo no es valido. Debe ser .jpg  .gif o .png\n";
        return '';
    }

    # Lee la imagen de origen
    open (my $IN, '<', $lafoto) || return '';
    if ($tipo eq 'jpg') {
        ref ($imagen = GD::Image->newFromJpeg(*$IN)) || return '';
    } elsif ($tipo eq 'png') {
        ref ($imagen = GD::Image->newFromPng(*$IN)) || return '';
    } elsif ($tipo eq 'gif') {
        ref ($imagen = GD::Image->newFromGif(*$IN)) || return '';
    } elsif ($tipo eq 'svg') {

    } else {
        $tipo = '';
    }
    close $IN;

    return $tipo;
}

# devuelve las propiedades de una imagen como lista (ancho, alto, ratio, tipo)
sub get_propiedades {
    my $lafoto = shift; # full path

    my ($ancho, $alto, $tipo, $ratio); # Ancho, alto y tipo (gif o jpg) de la foto.
    my $imagen; # Objeto GD que contiene la imagen original.

    # Determina si se trata de un GIF, un JPG u otro.
    $tipo = &get_imag_extension($lafoto);
    if (!$tipo) {
        print STDERR "Error: El tipo de archivo no es valido. Debe ser .jpg  .gif o .png\n";
        return ('', '', '', '');
    }

    # Lee la imagen de origen
    open(my $IN, '<', $lafoto) || return ('', '', '', '');
    if ($tipo eq 'jpg') {
        ref ($imagen = GD::Image->newFromJpeg(*$IN)) || return ('', '', '', '');
    }elsif ($tipo eq 'png') {
        ref ($imagen = GD::Image->newFromPng(*$IN)) || return ('', '', '', '');
    }elsif ($tipo eq 'gif') {
        ref ($imagen = GD::Image->newFromGif(*$IN)) || return ('', '', '', '');
    }else {
        close $IN;
        return ('', '', '', '');
    }
    close $IN;

    ($ancho, $alto) = $imagen->getBounds();
    $ratio = $ancho / $alto;
    return ($ancho, $alto, $ratio, $tipo);
}

# ---------------------------------------------------------------
# Genera thumb de la imagen jpg o gif, basandose en el anchomax, altomax o en ambos.
# Retorna: anchofinal, altofinal, buffer binario de la imagen.
sub make_resize {
    my $anchomax = $_[0];
    my $altomax = $_[1];
    my $lafoto = $_[2]; # full path

    GD::Image->trueColor(1);

    my $proporcionmaxima; # Regla fija para mantener imagenes largas bajo control.
    my $ratio; # Razon de proporcionalidad para el resize.
    my ($ancho,$alto,$tipo,$anchofinal,$altofinal); # Ancho, alto y tipo (gif o jpg) de la foto.
    my $imagen; # Objeto GD que contiene la imagen original.
    my $thumb;  # Objeto GD que contiene el thumbnail.

    # Si no se especifica ni ancho ni alto maximo, no hace nada.
    if ( (($anchomax eq '')||($anchomax == 0))
        && (($altomax eq '')||($altomax == 0)) ){
        # print "Error: No hay indicacion de ancho ni alto maximo.\n"; # debug
        return ('','','');
    }

    # Determina si se trata de un GIF, un JPG u otro.
    $tipo = &get_imag_extension($lafoto);
    if (!$tipo) {
        print STDERR "Error: El tipo de archivo no es valido. Debe ser .jpg  .gif o .png\n";
        return ('','','');
    }

    # Lee la imagen de origen
    open(my $IN, '<', $lafoto) || return ('','','');
    if ($tipo eq 'jpg') {
        ref ($imagen = GD::Image->newFromJpeg(*$IN)) || return ('','','');
    } elsif ($tipo eq 'png') {
        ref ($imagen = GD::Image->newFromPng(*$IN)) || return ('','','');
    } elsif ($tipo eq 'gif') {
        ref ($imagen = GD::Image->newFromGif(*$IN)) || return ('','','');
    } else {
        close $IN;
        return ('','','');
    }
    close $IN;

    ($ancho,$alto) = $imagen->getBounds();

    # print STDERR "RATIO[$ratio]\n;";
    ($anchofinal, $altofinal) = ($anchomax, $altomax);

    if ($tipo eq 'jpg') {
        $thumb = new GD::Image($anchofinal,$altofinal);
    }else{
        # Si el formato es gif o png, usa paleta de 8 bits.
        # $thumb = new GD::Image($anchofinal,$altofinal,0);
        # CVI - Para evitar error con PNG muy complejos
        $thumb = new GD::Image($anchofinal,$altofinal,1);
        $thumb->alphaBlending(0);
        $thumb->saveAlpha(1)
    };

    # Crea el thumbnail.
    $thumb->copyResampled($imagen,0,0,0,0,$anchofinal,$altofinal,$ancho,$alto);


    # No graba, sino que retorna el buffer binario de la foto para ser guardada por quien invoco a la funcion.
    my $bin_buffer;

    if ($tipo eq 'jpg') {
        $bin_buffer = $thumb->jpeg($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'});
    }else{
        $bin_buffer = $thumb->png();
    }

    return ($bin_buffer, $anchofinal, $altofinal);
};

# ---------------------------------------------------------------
sub make_crop {
    my ($srcX, $srcY, $width, $height, $path_foto) = @_;

    # Determina si se trata de un GIF, un JPG u otro.
    my $tipo = &get_imag_extension($path_foto);
    if (!$tipo) {
        my $error = "Error: El tipo de archivo no es valido. Debe ser .jpg  .gif o .png";
        print STDERR "$error\n";
        return ('','','', $error);
    };

    # valida archivo original
    if ((! -f $path_foto) || (! -s $path_foto)) {
        my $error = "Error: Archivo origen para el crop no es valido";
        print STDERR "$error\n";
        return ('','','', $error);
    }

    GD::Image->trueColor(1);

    # Lee la imagen de origen
    my $srcImage;
    open(my $IN, '<', $path_foto) || return ('','','');
    if ($tipo eq 'jpg') {
        ref ($srcImage = GD::Image->newFromJpeg(*$IN)) || return ('','','');
    }elsif ($tipo eq 'png') {
        ref ($srcImage = GD::Image->newFromPng(*$IN)) || return ('','','');
    }elsif ($tipo eq 'gif') {
        ref ($srcImage = GD::Image->newFromGif(*$IN)) || return ('','','');
    }else {
        close $IN;
        return ('','','', 'Tipo desconocido, no se puede recortar');
    }
    close $IN;

    # Dimensiones del arch origen
    my ($ancho, $alto) = $srcImage->getBounds();

    # valida dimensiones del crop solicitado
    if (($srcX !~ /^[0-9]+$/) || ($srcY !~ /^[0-9]+$/) || ($width !~ /^[0-9]+$/) || ($height !~ /^[0-9]+$/)) {
        my $error = "Error: Dimensiones del crop solicitado no son validas";
        print STDERR "$error\n";
        return ('','','', $error);
    }
    if ((($srcX + $width) > $ancho) || (($srcY + $height) > $alto)) {
        my $error = "Error: Dimensiones del crop solicitado se encuentran fuera de rango.";
        print STDERR "$error\n";
        return ('','','', $error);
    }

    # Crea Imagen destino
    my $dstImage;
    if ($tipo eq 'jpg') {
        $dstImage = new GD::Image($width, $height);
    } else {
        # Si el formato es gif o png, usa paleta de 8 bits.
        # $dstImage = new GD::Image($width, $height, 0);
        # CVI - Para evitar error con PNG muy complejos
        $dstImage = new GD::Image($width, $height,1);
        $dstImage->alphaBlending(0);
        $dstImage->saveAlpha(1)
    }

    # Crea el crop
    $dstImage->copy($srcImage, 0, 0, $srcX, $srcY, $width, $height);

    # No graba, sino que retorna el buffer binario de la foto para ser guardada por quien invoco a la funcion.
    my $bin_buffer;
    if ($tipo eq 'jpg') {
        $bin_buffer = $dstImage->jpeg($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'});
    } else {
        $bin_buffer = $dstImage->png();
    }

    return ($bin_buffer, $width, $height, '');
}

# ---------------------------------------------------------------
sub make_flip {
    my ($sentido, $path_foto) = @_;

    # Determina si se trata de un GIF, un JPG u otro.
    my $tipo = &get_imag_extension($path_foto);
    if (!$tipo) {
        print STDERR "Error: El tipo de archivo no es valido, path_foto[$path_foto]. Debe ser .jpg  .gif o .png\n";
        return '';
    }

    # valida archivo original
    if ((! -f $path_foto) || (! -s $path_foto)) {
        print STDERR "Error: Archivo origen para el flip no es valido[$path_foto]\n";
        return '';
    }

    GD::Image->trueColor(1);

    # Lee la imagen de origen
    my $srcImage;
    open(my $IN, '<', $path_foto) || return ('','','');
    if ($tipo eq 'jpg') {
        ref ($srcImage = GD::Image->newFromJpeg(*$IN)) || return '';
    }elsif ($tipo eq 'png') {
        ref ($srcImage = GD::Image->newFromPng(*$IN)) || return '';
    }elsif ($tipo eq 'gif') {
        ref ($srcImage = GD::Image->newFromGif(*$IN)) || return '';
    }else {
        close $IN;
        return '';
    }
    close $IN;


    # Aplica la transformacion
    $srcImage->flipHorizontal() if ($sentido eq 'horizontal');
    $srcImage->flipVertical()   if ($sentido eq 'vertical');

    # No graba, sino que retorna el buffer binario de la foto para ser guardada por quien invoco a la funcion.
    my $bin_buffer;
    if ($tipo eq 'jpg') {
        $bin_buffer = $srcImage->jpeg($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'});
    } else {
        $bin_buffer = $srcImage->png();
    }

    return $bin_buffer;
}

# ---------------------------------------------------------------
sub make_rotate {
    my ($degrees, $path_foto) = @_;

    # Determina si se trata de un GIF, un JPG u otro.
    my $tipo = &get_imag_extension($path_foto);
    if (!$tipo) {
        print STDERR "Error: El tipo de archivo no es valido, path_foto[$path_foto]. Debe ser .jpg  .gif o .png\n";
        return '';
    }

    # valida archivo original
    if ((! -f $path_foto) || (! -s $path_foto)) {
        print STDERR "Error: Archivo origen para el rotate no es valido[$path_foto]\n";
        return '';
    }

    GD::Image->trueColor(1);

    # Lee la imagen de origen
    my $srcImage;
    open(my $IN, '<', $path_foto) || return ('','','');
    if ($tipo eq 'jpg') {
        ref ($srcImage = GD::Image->newFromJpeg(*$IN)) || return '';
    } elsif ($tipo eq 'png') {
        ref ($srcImage = GD::Image->newFromPng(*$IN)) || return '';
    } elsif ($tipo eq 'gif') {
        ref ($srcImage = GD::Image->newFromGif(*$IN)) || return '';
    } else {
        close $IN;
        return '';
    }
    close $IN;

    # Crea Imagen destino (del mismo porte que la original)
    my ($width,$height) = $srcImage->getBounds();
    my $dstImage;

    # Aplica la transformacion
    $dstImage = $srcImage->copyRotate90() if ($degrees == 90);
    $dstImage = $srcImage->copyRotate180() if ($degrees == 180);
    $dstImage = $srcImage->copyRotate270() if ($degrees == 270);

    # No graba, sino que retorna el buffer binario de la foto para ser guardada por quien invoco a la funcion.
    if ($tipo eq 'jpg') {
        return $dstImage->jpeg($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'});
    } else {
        return $dstImage->png();
    }
}
# ---------------------------------------------------------------
# rota y refleja imagen de acuerdoa  valores de exif
# 1 = Horizontal (normal)
# 2 = Mirror horizontal
# 3 = Rotate 180
# 4 = Mirror vertical
# 5 = Mirror horizontal and rotate 270 CW
# 6 = Rotate 90 CW
# 7 = Mirror horizontal and rotate 90 CW
# 8 = Rotate 270 CW
sub apply_orientation_jpg {
    my ($path_foto, $orientation) = @_;

    # Determina si se trata de un GIF, un JPG u otro.
    my $tipo = &get_imag_extension($path_foto);

    GD::Image->trueColor(1);

    # Lee la imagen de origen
    my $image;
    open(my $IN, '<', $path_foto) || return ('','','');
    ref ($image = GD::Image->newFromJpeg(*$IN)) || return '';
    close $IN;

    # Aplica la transformacion
    if ($orientation == 1) {
        # 1 = Horizontal (normal), no se hace nada
        return $image->jpeg($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'});
    } elsif ($orientation == 2) {
        # 2 = Mirror horizontal
        $image->flipHorizontal();
        # retorna el buffer binario de la foto para ser guardada por quien invoco a la funcion.
        return $image->jpeg($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'});
    } elsif ($orientation == 3) {
        # 3 = Rotate 180
        $image->rotate180();
        # retorna el buffer binario de la foto para ser guardada por quien invoco a la funcion.
        return $image->jpeg($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'});
    } elsif ($orientation == 4) {
        # 4 = Mirror vertical
        $image->flipVertical();
        # retorna el buffer binario de la foto para ser guardada por quien invoco a la funcion.
        return $image->jpeg($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'});
    } elsif ($orientation == 5) {
        # 5 = Mirror horizontal and rotate 270 CW
        $image->flipHorizontal();
        my $copy_image =  $image->copyRotate270();
        # retorna el buffer binario de la foto para ser guardada por quien invoco a la funcion.
        return $copy_image->jpeg($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'});
    } elsif ($orientation == 6) {
        # 6 = Rotate 90 CW
        my $copy_image = $image->copyRotate90();
        # retorna el buffer binario de la foto para ser guardada por quien invoco a la funcion.
        return $copy_image->jpeg($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'});
    } elsif ($orientation == 7) {
        # 7 = Mirror horizontal and rotate 90 CW
        $image->flipHorizontal();
        my $copy_image = $image->copyRotate90();
        # retorna el buffer binario de la foto para ser guardada por quien invoco a la funcion.
        return $copy_image->jpeg($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'});
    } elsif ($orientation == 8) {
        # 8 = Rotate 270 CW
        my $copy_image =  $image->copyRotate270();
        # retorna el buffer binario de la foto para ser guardada por quien invoco a la funcion.
        return $copy_image->jpeg($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'});
    }
}
# ---------------------------------------------------------------
# Determina si la extension de la imagen original es valida
# para ser tratada con las funciones de esta lib.
sub get_imag_extension {
    my $lafoto = lc($_[0]);
    my $tipo = '';
    if ($lafoto =~ /\.jpe?g?$/) {
        $tipo = 'jpg';
    } elsif ($lafoto =~ /\.gif$/) {
        $tipo = 'gif';
    } elsif ($lafoto =~ /\.png$/) {
        $tipo = 'png';
    } elsif ($lafoto =~ /\.svg$/) {
        $tipo = 'svg';
    } elsif ($lafoto =~ /\.webp$/) {
        $tipo = 'webp';
    }
    return $tipo;
}

# ---------------------------------------------------------------
# Calcula las medidas proporcionales para redimensionar la imagen sin deformarla.
sub calcular_proporcion_img {
    my $wfoto  = $_[0];
    my $hfoto   = $_[1];
    my $wmax  = $_[2];
    my $hmax  = $_[3];
    my $percent;

    if ($wfoto > $wmax) {
        $percent = (($wmax * 100) / $wfoto);
        $wfoto = $wmax;
        $hfoto = ($hfoto * ($percent / 100));
    }

    if ($hfoto > $hmax) {
        $percent = (($hmax * 100) / $hfoto);
        $hfoto = $hmax;
        $wfoto = ($wfoto * ($percent / 100));
    }
    return ($wfoto, $hfoto);
}
# ---------------------------------------------------------------
# Centraliza la escritura de la imagen
sub write_image {
    my ($dst_path, $binfoto) = @_;
    return unless($binfoto);

    &glib_fildir_03::write_file_bin($dst_path, $binfoto);
}

1;
