#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------

package MediaServe::Image::Converter;
use strict;
use warnings;

use Image::Magick;
use File::Basename;

use lib '/app/tools/common/lib';
use Common::RSApp;
use Common::Util;
use Common::Assert;

use lib '/app/tools/mediaserve/lib';
use MediaServe::DB::Item::AlbumFile;
use MediaServe::Converter;

use base 'MediaServe::Converter';

# ----> How to use this class <----
#
# Convert images into different RS standard formats.
#
# my $converter = MediaServe::Image::Converter->new
# (
#   filename => "myimage.gif",
#   allow_upscaling => 0
# );
#
# $converter->write_web_thumbnail( "/tmp" );
# $converter->write_web_image( "/tmp", "override.jpg" );
# $converter->write_distribution_image( "/tmp" );
#

use constant kImageFormat => (
    geometry       => '600x600',
    dpi            => '72',
    shape_variance => '10',                # percentage not square
    formats        => [ 'jpeg', 'tiff' ]
);

#
# resize filters: Point, Box, Triangle, Hermite, Hanning, Hamming, Blackman,
#                 Gaussian, Quadratic, Cubic, Catrom, Mitchell, Lanczos,
#                 Bessel, Sinc
#
use constant kResizeSettings => (

    web_thumbnail => {
        geometry    => '100x100',
        filter      => undef,
        blur        => undef,
        density     => 72,
        format      => 'jpeg',
        extension   => 'Th',
        compression => undef
    },

    web_image => {
        geometry    => '600x600',
        filter      => undef,
        blur        => undef,
        density     => 72,
        extension   => 'L',
        compression => 'None',
        format      => 'jpeg'
    },

    distribution_image => {
        geometry    => undef,
        filter      => undef,
        blur        => undef,
        density     => 300,
        extension   => 'D',
        compression => 'None',
        format      => 'tiff'
    },
);

sub validate {
    my $self         = shift;
    my $filename     = shift;
    my %valid_format = (kImageFormat);
    my @format       = @{ $valid_format{formats} };
    my ( $min_height, $min_width ) = split( /x/, $valid_format{geometry} );
    my ( $height,     $width );
    my ( $xres,       $yres );

    assert($filename) unless ( $self->{_image} );
    assert( !$self->{_image} ) if ($filename);

    $self->read($filename) unless ( $self->{_image} );
    my $image = $self->{_image};

    #
    # Validate the type
    #
    my $colorspace = $image->Get("type");
    Common::Log::Debug("Colorspace: $colorspace");
    if ($colorspace) {
        $self->{_error} = "Image must be RGB format"
          unless ( $colorspace eq "TrueColor" || $colorspace eq "Palette" || $colorspace eq 'Grayscale' );
    } else {
        $self->{_error} = "Could not determine colorspace";
    }
    return undef if ( $self->{_error} );

    #
    # Validate the type
    #
    my $type = $image->Get("mime");
    if ($type) {
        $type =~ s/(.*\/)//;
        $self->{_error} = "Unsupported image format: $type"
          unless ( grep /^$type$/, @format );
    } else {
        $self->{_error} = "Unrecognized file type";
    }
    return undef if ( $self->{_error} );

    #
    # Validate resolution
    #
    ( $xres, $yres ) = $image->Get( "x-resolution", "y-resolution" );
    if ( $xres < $valid_format{dpi} || $yres < $valid_format{dpi} ) {
        $self->{_error} = "Image resolution must be at least $valid_format{dpi} dpi";
        return undef;
    }

    #
    # Validate size
    #
    ( $height, $width ) = $image->Get( "rows", "columns" );
    unless ( $self->_validateShape( $height, $width ) ) {
        $self->{_error} = "Image must be square";
        return undef;
    }

    if ( $height < $min_height && !$self->allow_upscaling ) {
        $self->{_error} = "Image height must be at least $min_height pixels";
        return undef;
    }

    if ( $width < $min_width && !$self->allow_upscaling ) {
        $self->{_error} = "Image width must be at least $min_width pixels";
        return undef;
    }

    return 1;
}

sub _validateShape {
    my $self = shift;
    my ( $height, $width ) = @_;
    my %valid_format = (kImageFormat);
    my ( $min_height, $min_width ) = split( /x/, $valid_format{geometry} );
    my $variance = $valid_format{shape_variance};

    my ( $xres, $yres );

    assert( defined($height) && defined($width) );

    my $shape_difference = $height - $width;

    my $shape_variance = $shape_difference / $min_width * 100;
    $shape_variance *= -1 if ( $shape_variance < 0 );

    Common::Log::Debug("Test Shape: Height $height Width $width Difference $shape_difference Variance $shape_variance ");
    return $shape_variance < $variance;
}

sub read {
    my $self     = shift;
    my $filename = shift;
    my $image    = new Image::Magick;
    my $error;

    assert($self);
    assert($filename);

    $self->{_image}    = undef;
    $self->{_filename} = $filename;

    $error = $image->Read($filename);

    if (   $error
        && $error !~ /Exception 350/ ) {
        $self->{_error} = $error;
        return undef;
    }

    $self->{_image} = $image;

    return 1;
}

sub write_web_thumbnail {
    my $self    = shift;
    my %formats = (kResizeSettings);
    assert( $formats{web_thumbnail} );

    return $self->_write_image( $formats{web_thumbnail}, @_ );
}

sub write_web_image {
    my $self    = shift;
    my %formats = (kResizeSettings);
    assert( $formats{web_image} );

    return $self->_write_image( $formats{web_image}, @_ );
}

sub write_distribution_image {
    my $self    = shift;
    my %formats = (kResizeSettings);
    assert( $formats{distribution_image} );

    return $self->_write_image( $formats{distribution_image}, @_ );
}

sub write_original {
    my $self = shift;
    return $self->_write_image( undef, @_ );
}

sub write {
    my $self = shift;
    my ( $format, $dir, $file ) = @_;

    assert($format);
    assert($dir);
    assert($file);

    return $self->_write_image(@_);
}

sub _write_image {
    my $self            = shift;
    my $format          = shift || {};
    my $output_dir      = shift;
    my $output_filename = shift || $self->{_filename};
    my $image           = $self->{_image};
    my $output;
    my @resize_args;
    my @write_args;

    assert($self);
    assert($output_dir);
    assert($output_filename);
    assert($image);

    # Don't muck with the original
    $image = $image->Clone();

    unless ( ref($image) ) {
        $self->{_error} = $image;
        return undef;
    }

    # Setup the output file
    $output = basename($output_filename);
    $output = "$output_dir/$output";
    if ( $format->{format} ) {
        $output =~ s/(.*)\..*/$1/;
        $output .= "-$format->{extension}.";
        $output .= $format->{format};
        $output = "$format->{format}:$output";
    }

    my ( $width, $height ) = split( /x/i, $format->{geometry} ) if ( $format->{geometry} );

    #Common::Log::Debug( "Writing image to $output" );

    #push( @resize_args, geometry => $format->{geometry} ) if( $format->{geometry} );
    push( @resize_args, height => $height )           if ($height);
    push( @resize_args, width  => $width )            if ($width);
    push( @resize_args, filter => $format->{filter} ) if ( $format->{filter} );
    push( @resize_args, blur   => $format->{blur} )   if ( $format->{blur} );

    push( @write_args, compression => $format->{compression} ) if ( $format->{compression} );
    push( @write_args, density     => $format->{density} )     if ( $format->{density} );

    $self->{_error} = $image->Set( Type => "TrueColor" );

    unless ( $self->{_error} ) {
        Common::Log::Debug("  - Resize image: @resize_args");
        $self->{_error} = $image->Resize(@resize_args) if (@resize_args);
    }

    unless ( $self->{_error} ) {
        Common::Log::Debug("Writing $output");
        $self->{_error} = $image->Write( filename => "$output", @write_args );
    }

    return $self->error() ? undef : 1;
}

sub outputFilename {
    my $self     = shift;
    my $filename = shift;
    my $type     = shift;
    my %formats  = (kResizeSettings);
    my $format;

    assert($self);
    assert($filename);
    assert($type);

    Common::Log::Debug("Converting filename: $filename");
    if ( $type eq MediaServe::DB::Item::AlbumFile::kFileTypeOriginal ) {
        return $filename;
    }

    if ( $type eq MediaServe::DB::Item::AlbumFile::kFileTypeThumbnail ) {
        $format = "web_thumbnail";
    } elsif ( $type eq MediaServe::DB::Item::AlbumFile::kFileTypeLarge ) {
        $format = "web_image";
    } elsif ( $type eq MediaServe::DB::Item::AlbumFile::kFileTypeDistribution ) {
        $format = "distribution_image";
    }

    assert( $format, "Unknown image format" );

    my $base = basename($filename);
    $base =~ s/(.*)\..*/$1/;
    $base = $filename unless ($base);
    my $out = $base . "-" . $formats{$format}->{extension} . "." . $formats{$format}->{format};

    Common::Log::Debug("Converted filename: $out");

    return $out;
}

sub getFileTypes {
    return ( MediaServe::DB::Item::AlbumFile::kFileTypeOriginal, MediaServe::DB::Item::AlbumFile::kFileTypeThumbnail,
        MediaServe::DB::Item::AlbumFile::kFileTypeLarge, MediaServe::DB::Item::AlbumFile::kFileTypeDistribution
    );

}

1;
