# +-----------------------------------------------------------------------+
# | pLoader - a Perl photo uploader for Piwigo                            |
# +-----------------------------------------------------------------------+
# | Copyright(C) 2008-2010 Piwigo Team                  http://piwigo.org |
# +-----------------------------------------------------------------------+
# | This program is free software; you can redistribute it and/or modify  |
# | it under the terms of the GNU General Public License as published by  |
# | the Free Software Foundation                                          |
# |                                                                       |
# | This program is distributed in the hope that it will be useful, but   |
# | WITHOUT ANY WARRANTY; without even the implied warranty of            |
# | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
# | General Public License for more details.                              |
# |                                                                       |
# | You should have received a copy of the GNU General Public License     |
# | along with this program; if not, write to the Free Software           |
# | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
# | USA.                                                                  |
# +-----------------------------------------------------------------------+
package Uploader::ImageList;
use strict;
use Carp;
use base qw/Uploader::Object/;
use Image::ExifTool qw(:Public);
use Image::Magick;
use File::Spec;
use Uploader::Image;
use Data::Dumper;
use Storable;
use Digest::MD5::File qw/file_md5_hex md5_hex/;
use Wx::Locale qw/:default/;
use Wx qw/wxTheApp/;

# this class implements a collection of image files with associated data
$|=1;
__PACKAGE__->mk_accessors( 
    qw/
                thumb_size
                preview_ratio
                categories
                type
                filter
                blur
                quality
                resize_w
                resize_h
                hd_filter
                hd_blur
                hd_quality
                hd_w
                hd_h
                hd_interlace
                prefix
                author
                count
                new_files
                storable_file
                wx_thumb_size
                current_image
                images
                image_selection
                exif_metadata
                wx_thumb_imglist
                wx_thumb_dir
                site_resized_dir
                site_thumb_dir
                userdata_dir
                progress_msg
                last_error_msg
                default_caption
                default_caption_pattern
                SetNewFilesViewerRefreshCallback
                SetNewFilesProgressCallback
                SetNewFilesDisplayEndInfoCallback
                YieldCallback
                UploadImagesViewerCallback
                progress_thumbnail_refresh
                progress_msg_details_refresh
                progress_msg_refresh
                progressbar_refresh
                progress_endinfo_refresh
                ResizeCallback
                upload_rejects
                pwg
                upload_high
                upload_hd
                remove_uploaded_from_selection
                wx_quality
                th_quality
                auto_rotate
                interlace
                create_resized
                use_exif_preview
                image_sums
                upload_image_sums
                sums
                version
                imagelist_version
                uploaded_images
                watermark_activate
                watermark_activate_pwg_high
                watermark_text
                watermark_text_size
                watermark_position
                watermark_y
                watermark_x
                watermark_color
                gravity
                rgbcolor
                upload_msg
                upload_selection_count
                upload_uploaded_count
                upload_rejected_count
                upload_last_error
                upload_error_content
                upload_begin_time
                upload_end_time
                upload_duration
                upload_file
                upload_name
                ReuploadCallback
                reupload_action_files
                reupload_action_properties
                reupload_action_properties_m
                reupload_not_ask
                display_mode
                stop_processing
                image_selection_tags
                image_selection_privacy_level
                image_selection_name
                image_selection_comment
                image_selection_author
                image_selection_create_date
     /
);


sub Init {
    my ( $self ) = @_;
    
    $self->image_selection([]) if !defined $self->image_selection;
    $self->uploaded_images([]);
    $self->gravity(
        { 
            'Top'          => 'North',
            'Left'         => 'West',
            'Right'        => 'East',
            'Bottom'       => 'South',
            'Top left'     => 'NorthWest',
            'Top right'    => 'NorthEast',
            'Bottom left'  => 'SouthWest',
            'Bottom right' => 'SouthEast',
            'Center'       => 'Center',
       }   
    );    

    $self->rgbcolor(
        {
            "White" => '#FFFFFF',
            "Black" => '#000000',
        }    
    );
    
    $self->image_selection_tags(
        []
    ) unless defined $self->image_selection_tags;
}

sub _set_exif_tag {
    my ( $self, $file, $tag, $newValue ) = @_;    

  my $options = {};
  # Create a new Image::ExifTool object
  my $exifTool = new Image::ExifTool;

  # Extract meta information from an image
  $exifTool->ExtractInfo($file, $options);

  # Set a new value for a tag
  $exifTool->SetNewValue($tag, $newValue);

  # Write new meta information to a file
  $exifTool->WriteInfo($file);

}


sub _set_current_image_filepaths {
    my ( $self ) = @_;

    my $filename = sprintf(
        "%s.%s",
        $self->current_image->file_sum,
        $self->type,
    );


    $self->current_image->wx_thumb_file( 
        File::Spec->catfile(
            $self->wx_thumb_dir,
            $filename
        )
    );

    $self->current_image->site_thumb_file( 
        sprintf("%s.%s",
            File::Spec->catfile(
                $self->site_thumb_dir,
                'thumbnail'
            ),
            $self->type
        )
    );

}


sub SetCurrentImage {
    my ( $self, $indx ) = @_;    

    $self->current_image(
        $indx != -1 ? $self->GetImage($indx) : Uploader::Image->new()
    );
}


sub SetNewFiles {
    my ( $self, $files ) = @_;

    $self->stop_processing(0);

    $self->new_files( $files );

    # if some files have been previously selected 
    my $i = scalar @{$self->sums};
    #printf("SetNewFiles %s\n", $i);
    my $count = 0;
    $self->count($count);
    my $errors = 0;

    foreach my $file ( @{$files} ) {
        my $info = $self->_read_exif_metatdata($file->{ANSIPathName});
           my $is_new_image = $self->_add_image($file, $info, $i);    
        $self->SetCurrentImage($i);
        $self->_set_current_image_filepaths();

        if($is_new_image){
            #my $use_wx_resize = $self->_create_gui_preview($info);
            $self->_create_gui_thumbnail();

            # ok
            if(!$@){
                $self->progress_msg(gettext("Selection thumbnail created for %s"));
            }
            else {
                $self->progress_msg("An error has occured when processing %s\n$@");
                # remove from list
                splice @{$self->sums}, $i, 1;
                $errors++;
            }
        
            $self->SetNewFilesProgressCallback->();
        }
        $i++;
        $count++;
        $self->count($count);
        $self->SetNewFilesViewerRefreshCallback->();
        last if $self->stop_processing;
    }

    $self->SetNewFilesDisplayEndInfoCallback->(
        sprintf(
            "%s : %s\n\n%s : %s",
            gettext("photos added to the selection"),
            $self->count,
            gettext("errors"),
            $errors,
            
        ),
        $errors
    );
    
    $self->Store;
    
}

sub _read_exif_metatdata {
    my ( $self, $file ) = @_;
    
    # read exif metadata
    my $info;
    eval {
        $info = ImageInfo( $file );
    };
    $info = {} if($@);
    
    $info;
}

# key is file path
sub _add_image {
    my ( $self, $file, $info, $i ) = @_;    

    my $is_new_image;

    # for legacy imagelist that do not have image_sums property
    $self->image_sums(
        {}
    ) if !$self->image_sums;

    my $sum = file_md5_hex($file->{ANSIPathName});

    my $image;
    if ( !exists $self->image_sums->{$sum} ){
        #print "_add_image ", Dumper $file, "\n";
        # append to image list
        $image = Uploader::Image->new(
        {
                file              => $file->{ANSIPathName},
                file_sum          => $sum,
                site_name         => $self->init_default_caption($file->{PathName}, $info, $i),
                caption           => $self->default_caption,
                site_author       => $self->author,
                exif_metadata     => $self->_select_exif_data($info),
                add_rank          => $i,
                site_categories   => [],
                site_tags         => [],
                site_high_file    => $file->{ANSIPathName},
            }
        );

        $self->image_sums->{$sum} = $image ;
        $is_new_image = 1;
    } else {
        $image = $self->image_sums->{$sum};
    }

        
    $self->sums->[$i] = $sum ;

    $is_new_image;
}


sub init_default_caption {
    my ( $self, $file, $info, $i ) = @_;

    my $create_date = $info->{CreateDate};

    $self->init_caption_from_pattern(
        $file,
        $create_date,
        $i,
        $self->default_caption,
        $self->default_caption_pattern
    );
}


sub init_caption_from_pattern {
    my ( $self, $file, $create_date, $i, $caption, $pattern ) = @_;

    my ( $yyyy, $mm, $dd, $hh, $mi, $ss ) = split /[:\s]/, $create_date ;
  
    my $chrono = join('', $yyyy, $mm, $dd);

    my $caption_from_pattern;
    my $ext;
    my ( $vol, $path, $filename ) = File::Spec->splitpath($file);
    ( $filename, $ext ) = split /\.\w+$/, $filename;
    

    if('Caption' eq $pattern){
        $caption_from_pattern = $caption
    }
    elsif('File name' eq $pattern){
        $caption_from_pattern = $filename
    }
    elsif('File path and name' eq $pattern){
        $caption_from_pattern = sprintf(
            "%s", 
            File::Spec->catfile($path, $filename), 
        )        
    }    
    elsif('Caption + rank number' eq $pattern){
        $caption_from_pattern = sprintf(
            "%s %s", 
            $caption, 
            1+$i,
        )        
    }    
    elsif('Rank number + caption' eq $pattern){
        $caption_from_pattern = sprintf(
            "%s %s", 
            1+$i,
            $caption, 
        )        
    }    
    elsif('Caption + create date chrono' eq $pattern){
        $caption_from_pattern = sprintf(
            "%s %s", 
            $caption, 
            $chrono,
        )        
    }    
    elsif('Create date chrono + caption' eq $pattern){
        $caption_from_pattern = sprintf(
            "%s %s", 
            $chrono,
            $caption, 
        )        
    }
    elsif('Create date chrono + rank' eq $pattern){
        $caption_from_pattern = sprintf(
            "%s %s", 
            $chrono,
            1+$i, 
        )        
    }
    elsif('Rank + create date chrono' eq $pattern){
        $caption_from_pattern = sprintf(
            "%s %s", 
            1+$i, 
            $chrono,
        )        
    }

    $caption_from_pattern;
}


sub GetCurrentImageCaption {
    my ( $self, $index, $pattern ) = @_;

    $pattern = wxTheApp->eng_caption_patterns->{$pattern};

    $self->SetCurrentImage($index);

    my $img = $self->current_image;

    $self->init_caption_from_pattern(
        $img->file,
        $img->create_date,
        $index,
        $self->current_image->caption,
        $pattern
    );
}


sub _create_gui_thumbnail {
    my ( $self ) = @_;

     eval {
        if(!$self->CreateGUIThumbnail())
        {
            $self->ResizeCallback->(
                $self->current_image->file,
                $self->current_image->wx_thumb_file,
                $self->type,
                $self->wx_thumb_size,
                $self->wx_thumb_size,
                $self->wx_quality,
            );
            
        }
     };
}

sub RemoveImageSelection {
    my ( $self ) = @_;
    
    return if (! scalar @{$self->sums} );
    return if (! defined $self->image_selection );
    
    $self->_remove_image_list($self->image_selection);
    # clear image selection
    $self->image_selection([]);
}

sub _remove_image_list {
    my ( $self, $list ) = @_;
    # the list is sorted, ascendant
    # we reverse it to have
    # higher first, and keep same indexes during remove
    @$list = reverse @$list;     
    map {
        $self->DeleteImage($_);
        splice @{$self->sums}, $_, 1 ;
        $self->wx_thumb_imglist->Remove($_);
        shift @$list;
    }
    @$list;
}


# used for display in GUI. has to fit a square box ( wxImageList )
sub CreateGUIThumbnail {
    my ( $self ) = @_;

    my $rval = 0;
    my $image = new Image::Magick;

    my $size = $self->wx_thumb_size||100;

    my $status = $image->Set(size=>sprintf("%sx%s", 2*$size, 2*$size));
    warn "$status" if $status ;

    $status = $image->ReadImage(
        $self->current_image->file
    );
    warn "$status" if $status;
    return $rval if $status;

    $self->current_image->width(
        $image->Get('width')
    );
    $self->current_image->height(
        $image->Get('height')
    );


    # maximize size and keep aspect ratio
    $status = $image->Thumbnail(
        geometry=>sprintf("%s%s>", $size*$size, '@')
    );
    # to get adjusted to a square box
    #$status = $image->Thumbnail(
    #    geometry=>sprintf("%sx%s%s", $size, $size, '^')
    #);
    warn "$status" if $status;
    return $rval if $status;

#    causes strange behaviour with i18n -> yellow borders when local is other than EN
#    $status = $image->Set(background=>"white");
#    warn "$status" if $status ;

    $status = $image->Set(Gravity=>"Center");
    warn "$status" if $status ;

    $image->Extent(
        geometry=>sprintf("%sx%s", $size, $size),
        gravity=>'center',
    );
    
    $image->Set(
        quality=>$self->wx_quality||90
    );

    $status = $image->Strip();
    warn "$status" if $status ;

    # exif from original image
    my $orientation = $self->current_image->exif_metadata->{Orientation};
    
    # Valid for Rotate 180, Rotate 90 CW, Rotate 270 CW
    if( $orientation =~ m/Rotate (\d+)/ ){
        printf(
            "Rotate %s\n",
            $1
        );
    
        $image->Rotate( degrees=>$1 ) if $self->auto_rotate;    
    }
    

    $image->Write(
        sprintf(
            "%s:%s",
            $self->type,
            $self->current_image->wx_thumb_file,
        )
    );

    undef $image;
    
    $rval = 1;
    
    return $rval;
}




sub CreateResized {
    my ( $self ) = @_;
    
    my $rval = 1 ;


    my $image = new Image::Magick;

    my $status = $image->ReadImage(
        $self->current_image->file
    );
    warn "$status" if $status ;
    return 0 if $status;

    my $w = $image->Get('width');
    my $h = $image->Get('height');

    $status = $image->Set(Gravity=>"Center");
    warn "$status" if $status ;

    # exif from original image
    my $orientation = $self->current_image->exif_metadata->{Orientation};
    
    # Valid for Rotate 180, Rotate 90 CW, Rotate 270 CW
    if( $orientation =~ m/Rotate (\d+)/ ){
        printf(
            "Rotate %s\n",
            $1
        );
    
        $image->Rotate( degrees=>$1 ) if $self->auto_rotate;    
    }


    #printf("resize with blur value %s\n", $self->blur);
    $status = $image->Resize(
        geometry => sprintf("%sx%s>", $self->resize_w, $self->resize_h), 
        filter => sprintf("%s", $self->filter), 
        blur => $self->blur
    );
    warn "$status" if $status ;
    return 0 if $status;

    #printf("resize with quality value %s\n", $self->quality);
    $status = $image->Set(quality=>$self->quality);
    warn "$status" if $status ;

    $status = $image->Set(interlace=>$self->interlace);
    warn "$status" if $status ;

    $image->Write(
        sprintf(
            "%s:%s",
            $self->type,
            $self->current_image->site_resized_file,
        )
    );
    warn "$status" if $status ;
    return 0 if $status;
    
    undef $image;

   
    $rval = 0 if $status;

    return $rval;
}

sub CreateThumbnail {
    my ( $self ) = @_;
    
    #return 1 if( -e $self->current_image->site_thumb_file );
    
    my $rval = 1;

    my $image = new Image::Magick;

    my $status = $image->ReadImage(
        $self->current_image->site_resized_file
    );
    warn "$status" if $status ;

    
    $status = $image->Resize(
        geometry => sprintf(
                                "%sx%s>", 
                                $self->thumb_size, 
                                $self->thumb_size
                           ),
    );
    warn "$status" if $status ;

    $status = $image->Set(Gravity=>"Center");
    warn "$status" if $status ;

    $status = $image->Set(quality=>$self->th_quality);
    warn "$status" if $status ;

    $status = $image->Strip();
    warn "$status" if $status ;


    $image->Write(
        sprintf(
            "%s:%s",
            $self->type,
            $self->current_image->site_thumb_file,
        )
    );
    
    undef $image;


    $rval = 0 if $status;

    return $rval;
}



sub _select_exif_data {
    my ( $self, $exif ) = @_;

    return {
        map {
            $_ => $exif->{$_},
        }
        qw/
            CreateDate
            ImageWidth
            ImageHeight
            Orientation
            DateTimeOriginal
            ISO
            ExposureTime
            ApertureValue
            FocalLength
            Lens
            Exposure
            Make
            Model
        /
    };    
}

sub Store {
    my ( $self ) = @_;
    
    my $data = $self->get_storable(
        [ 
            qw/
                images
                thumb_size
                preview_ratio
                type
                filter
                blur
                quality
                wx_quality
                th_quality
                prefix
                author
                count
                resize_w
                resize_h
                hd_filter
                hd_blur
                hd_quality
                hd_w
                hd_h
                hd_interlace
                new_files
                storable_file
                wx_thumb_size
                current_image
                exif_metadata
                wx_thumb_dir
                site_resized_dir
                site_thumb_dir
                userdata_dir
                progress_msg
                default_caption
                default_caption_pattern
                upload_high
                upload_hd
                remove_uploaded_from_selection
                auto_rotate
                interlace
                create_resized
                use_exif_preview
                image_sums
                sums
                version
                imagelist_version
                watermark_activate
                watermark_activate_pwg_high
                watermark_text
                watermark_text_size
                watermark_position
                watermark_y
                watermark_x
                watermark_color
                reupload_action_files
                reupload_action_properties
                reupload_action_properties_m
                display_mode
                image_selection_tags
            /
        ] 
    
    );
    eval {
        store $data, $self->storable_file;
    };
    if($@){
        print $@, "\n";    
    }
}



sub UploadSelection {
    my ( $self ) = @_;

    $self->stop_processing(0);

    my $viewer_callback = $self->UploadImagesViewerCallback ;

    $self->image_selection([]) if !defined $self->image_selection;
    $self->upload_rejects(
        []
    );

    $self->count(
        1
    );

    $self->upload_uploaded_count(0);
    $self->upload_rejected_count(0);
    $self->upload_begin_time(time);
    $self->upload_selection_count(scalar @{$self->image_selection});
    # for re-upload management
    $self->upload_image_sums( [
            map { $self->GetImage($_)->file_sum }
            @{$self->image_selection}
        ] 
    );

    # check if already exist on server
    my $uploaded = $self->pwg->IsAlreadyUploaded($self->upload_image_sums);
    my @already_uploaded = grep { $_ } values %$uploaded ; 
    $self->ReuploadCallback->() if ( scalar @already_uploaded and !$self->reupload_not_ask );

    
    foreach(@{$self->image_selection}) {
    # current image object        
        $self->current_image(
            $self->GetImage($_)
        ); 
        # prepare resized, high, thumbnail
        # if not already uploaded
        $self->_set_site_resized_file();
        $self-> _set_site_high_file ();
        # photo metadata
        $self->_upload_selection_prepare() if (!$uploaded->{$self->current_image->file_sum} or $self->reupload_action_files);
        $self->_prepare_upload_properties();        

        # transfert resized, high, thumbnail to site
        my $status = $self->_upload_selection_transfert();
        # log operations
        $self->_upload_selection_log($status);
        $self->count(
            1+$self->count
        );
        # remove thumbnail, resized
        # to make sure everything is clean
        # keep site_high_file because it can be the original !!!
        $self->_remove_resized_from_cache;

        last if $self->stop_processing;
    }

    $self->stop_processing(0);

    if($self->remove_uploaded_from_selection){
        $self->_remove_image_list($self->uploaded_images);
        # clear thumbnail imagelistctrl
        $viewer_callback->();
    }

    $self->_upload_selection_final_log();

}


sub _remove_resized_from_cache {
    my ( $self ) = @_;

    unlink $self->current_image->site_thumb_file if -e $self->current_image->site_thumb_file;

    map {
        my $file = File::Spec->catfile(
            $self->site_resized_dir,
            sprintf(
                "%s.%s",
                 $_,
                $self->type,
            )
        );
        unlink $file if -e $file;
    } qw /resized high/;


}

sub _upload_selection_prepare {
    my ( $self ) = @_;

    $self->progress_thumbnail_refresh->();
    # PREPARE
    $self->_set_upload_msg(gettext("Preparing resized image for"));
    $self->_upload_progress();
    #printf("resized %s\n", $self->create_resized);
    if( $self->create_resized ){
        $self->_create_site_resized_file();
        $self->_set_upload_msg(gettext("Resized image done for"));
        $self->_upload_progress();
    }
    # the original is at the right size, no need to create a resize
    else {
        #printf("original no resized %s\n", $self->create_resized);
        $self->current_image->site_resized_file(
            $self->current_image->file,
        );
    }

    my $decode = {
        'No' => 0,
        'Yes, use HD resized of the original photo' => 'HD',
        'Yes, use a copy of the original photo' => 'ORIGINAL',
    };    
    #printf("upload HD %s\n", $self->upload_hd);
    $self->upload_high(
        $decode->{$self->upload_hd}
    );
    #printf("upload High %s\n", $self->upload_high);
    
    # if upload high, rotate a copy of original file
    if($self->upload_high){
        $self->CreateHigh();
        $self->_set_upload_msg(gettext("HD image done for"));
        $self->_upload_progress();
    }

    eval {
        $self->CreateThumbnail();
    };
    $self->_set_upload_msg(gettext("Thumbnail image done for"));
    $self->_upload_progress();


}

sub _upload_progress {
    my ( $self, $value ) = @_;

    eval {
        $self->progress_msg_refresh->(
            $self->upload_msg
        );
    };
    # user cancelled : dialog box is destroyed
    croak gettext("Upload cancelled"), " .", $@ if $@ ;


}

sub _set_upload_msg {
    my ( $self, $msg, $errmsg ) = @_;

    $self->upload_msg(
        sprintf(
            "%s : %s - %s\n\n%s %s %s %s\n%s",
            $msg,
            $self->upload_file,
            $self->upload_name,
            gettext("Photo"),
            $self->count,
            gettext("on"),
            $self->upload_selection_count,
            $errmsg
        )
    );
}

sub _upload_selection_transfert {
    my ( $self ) = @_;

    return if $self->stop_processing;

    $self->_set_upload_msg(gettext("Uploading"));
    $self->_upload_progress(0);

    # UPLOAD
    my ( $status, $status_msg, $content ) = $self->pwg->UploadImage(
        { 
            yield           => $self->YieldCallback, 
            bar             => $self->progressbar_refresh, 
            msg             => $self->progress_msg_refresh,
            msg_details     => $self->progress_msg_details_refresh,
            resized_msg     => gettext("Uploading resized"),
            thumbnail_msg   => gettext("Uploading thumbnail"),
            highdef_msg     => gettext("Uploading high definition"),
            checksum_msg    => gettext("Checksum for"),
            original_sum    => $self->current_image->file_sum,
            stop_processing => $self->stop_processing,
        } 
    );
    my $ok = 0;
    # HTTP REQUEST OK
    if ( $status ){
        # PIWIGO RESULT ( HTTP may be ok while Piwigo is not )
        $ok = 'fail' eq $content->{stat} ? 0 : 1;
    }
    else{
        Wx::LogMessage(
            "%s %s : %s",
            gettext("Communication error with"),
            $self->pwg->site_url,
            $status_msg,
        );
    }
    $self->upload_last_error(
        $status_msg
    );

    $self->upload_error_content(
        $content
    );

    $ok;
}

sub _upload_selection_log {
    my ( $self, $ok ) = @_;

    if($ok){
        $self->_set_upload_msg(gettext("Uploaded"));
        $self->_upload_progress(0);
        push @{$self->uploaded_images}, $_;
        $self->upload_uploaded_count(
            1+$self->upload_uploaded_count        
        );
    } else {
        $self->_set_upload_msg(gettext("An error has occured"), Dumper($self->upload_error_content));
        $self->upload_rejected_count(
            1+$self->upload_rejected_count
        );
    }    
        
}

sub _upload_selection_final_log {
    my ( $self ) = @_;

    $self->upload_end_time(time);
    $self->upload_duration(
        $self->upload_end_time - $self->upload_begin_time
    );

    $self->progress_endinfo_refresh->(
        sprintf(
            "%s : %s\n\n%s : %s\n\n%s : %s\n\n\n%s : %s %s",
            gettext("images processed"),
            $self->count - 1,
            gettext("images uploaded"),
            $self->upload_uploaded_count,
            gettext("images in errors and not uploaded"),
            $self->upload_rejected_count,
            gettext("Duration"),
            $self->upload_duration,
            gettext("seconds"),
        )
    );
}

sub _set_site_resized_file {
    my ( $self ) = @_;

    my ( $vol, $dir, $file ) = File::Spec->splitpath(
        $self->current_image->file
    );

    $self->upload_file(
        $file
    );
        
    $self->upload_name(
        $self->current_image->site_name
    );
    my $filename = $self->current_image->file_sum ;

    # lately defined to make sure we have the last global properties ( resize_w, resize_h )
    $self->current_image->site_resized_file( 
        File::Spec->catfile(
            $self->site_resized_dir,
            sprintf(
                "%s.%s",
                'resized',
                $self->type,
            )
        )
    );
    printf("_set_site_resized_file %s\n", $self->current_image->site_resized_file);
}

sub _create_site_resized_file {
    my ( $self ) = @_;

    eval {
        if(!$self->CreateResized()){
            $self->_create_resized_fallback();
        };

        $self->_set_exif_tag(
            $self->current_image->site_resized_file,
            'Orientation',
            'Horizontal (normal)',
        ) if $self->auto_rotate;

        $self->CreateWatermark(
            $self->watermark_text,
            $self->watermark_text_size,
            $self->watermark_position,
            $self->watermark_x,
            $self->watermark_y,
            $self->watermark_color,
            $self->current_image->site_resized_file
        ) if $self->watermark_activate;
    }
}

sub _create_resized_fallback {
    my ( $self ) = @_;
    # use wx builtin rescale if IM fails
    printf("CreateResized failed %s. Use ResizeCallback\n", $@);
    # use method provided by the caller
    # source, target, type, ratio, width, $height
    $self->ResizeCallback->(
        $self->current_image->file,
        $self->current_image->site_resized_file,
        $self->type,
        $self->resize_w,
        $self->resize_h,
        $self->quality,
    );
                
    $self->RotateImage(
        $self->current_image->site_resized_file,
    ) if $self->auto_rotate;
}

# if we need to rotate
sub CreateHigh {
    my ( $self ) = @_;

    #printf("CreateHigh %s\n", $self->upload_high);
    my $bModifyOriginal;
    my $bRotate;
    my $bAddWatermark;
    my $bResize;
    my $orientation = $self->current_image->exif_metadata->{Orientation};
    my $degrees;
    
    # Valid for Rotate 180, Rotate 90 CW, Rotate 270 CW
    if( $self->auto_rotate and $orientation =~ m/Rotate (\d+)/ ){
        $bModifyOriginal = 1;
        $bRotate = 1;
        $degrees = $1;
    }

    if( $self->watermark_activate_pwg_high ){
        $bModifyOriginal = 1;
        $bAddWatermark = 1;
    }
    
    # HD resize
    if('HD' eq $self->upload_high){
        $bModifyOriginal = 1;
        $bResize = 1;
    }

    if($bModifyOriginal){

        my $image = Image::Magick->new();
        # we read original
        my $status = $image->Read(
            $self->current_image->file
        );
        warn "$status ", $self->current_image->file, "\n" if $status ;
        return 0 if $status;

        if($bRotate){
            $image->Rotate( degrees=>$degrees );    
        }        
        $image->Write(
            $self->current_image->site_high_file
        );
        warn "$status ", $self->current_image->site_high_file, "\n" if $status ;
        return 0 if $status;

        if($bResize){
            $status = $image->Resize(
                geometry => sprintf("%sx%s>", $self->hd_w, $self->hd_h), 
                filter => sprintf("%s", $self->hd_filter), 
                blur => $self->hd_blur
            );
            warn "$status" if $status ;
            return 0 if $status;
        }
        
        #printf("resize with quality value %s\n", $self->quality);
        $status = $image->Set(quality=>$self->quality);
        warn "$status" if $status ;

        $status = $image->Set(interlace=>$self->interlace);
        warn "$status" if $status ;
        
        
        undef $image;

        if($bAddWatermark){
          my $file_out = $self->current_image->site_high_file;
          $self->CreateWatermark(
             $self->watermark_text,
                $self->watermark_text_size,
                $self->watermark_position,
                $self->watermark_x,
                $self->watermark_y,
                $self->watermark_color,
                $file_out
            );
        }


        $self->_set_exif_tag(
            $self->current_image->site_high_file,
            'Orientation',
            'Horizontal (normal)',
        );

        # Now all images that need to be rotated are done. Update exif
        $self->current_image->exif_metadata->{Orientation} = 'Horizontal (normal)';


    }
    else{
        # high file is the original file
        $self->current_image->site_high_file(
            $self->current_image->file
        );
        #printf("site high file %s\n", $self->current_image->site_high_file);
    }

    return 1;
}

# file name for original copy
# we do not need the original file name
sub _set_site_high_file {
        my ( $self ) = @_;

        my ( $vol, $dir, $file ) = File::Spec->splitpath(
            $self->current_image->file
        );
        
        $self->current_image->site_original_filename(
            $file
        );
        my ( $filename, $ext ) = split /\./, $file ;
    
        # high_file is a resized of original
        $self->current_image->site_high_file( 
            File::Spec->catfile(
                $self->site_resized_dir,
                sprintf(
                    "%s.%s",
                     'high',
                    $self->type,
                )
            )
        );
}



sub CreateWatermark {
    my ( $self, $text, $text_size, $position, $x, $y, $color, $file_out ) = @_;
    
    my $rval = 1 ;
    my $gravity = $self->gravity->{$position};
    my $fill = $self->rgbcolor->{$color};

    # debug
    #printf("Create watermark %s\n", $file_out);

    

    my $image = new Image::Magick;
    
    my $status = $image->ReadImage(
        $file_out
    );      

    my $ratio = $image->Get('height')/($self->resize_h||$image->Get('height'));
    $ratio||=1;
    $text ||="Your watermark";
    $image->Annotate(
        pointsize => $text_size*$ratio,
        fill => $fill,
        x => $x*$ratio,
        y => $y*$ratio,
        text => $text,
        gravity => $gravity,
    );
                      
    $image->Write(
        sprintf(
            "%s:%s",
            $self->type,
            $file_out,
        )
    );
}

sub _prepare_upload_properties {
    my ( $self ) = @_;

    # set default values only if not defined
    # || operator is not used because 0 is a choice value
    # and would replace a valid choice with a default value
    $self->reupload_action_files(1) 
        unless defined $self->reupload_action_files;

    $self->pwg->reupload_action_properties(1)
        unless defined $self->pwg->reupload_action_properties;

    $self->pwg->reupload_action_properties_m(1)
        unless defined $self->pwg->reupload_action_properties_m;

    $self->pwg->reupload_action_files(
        $self->reupload_action_files
    );
    $self->pwg->reupload_action_properties(
        $self->reupload_action_properties
    );

    $self->pwg->reupload_action_properties_m(
        $self->reupload_action_properties_m
    );
    
    $self->pwg->upload_high(
        $self->upload_high
    );

    $self->pwg->site_high_file(
        $self->current_image->site_high_file
    );

    $self->pwg->site_original_filename(
        $self->current_image->site_original_filename
    );


    $self->pwg->site_resized_file(
        $self->current_image->site_resized_file
    );

    $self->pwg->site_thumb_file(
        $self->current_image->site_thumb_file
    );

    $self->pwg->site_author(
        $self->current_image->site_author
    );

    $self->pwg->site_comment(
        $self->current_image->site_comment
    );

    $self->pwg->site_image_name(
        $self->current_image->site_name
    );

    $self->pwg->site_img_date_creation(
        substr($self->current_image->create_date, 0, 10)
    );

    $self->pwg->privacy_level(
        $self->current_image->privacy_level
    );
    
    $self->current_image->site_categories(
        $self->categories
    );

    $self->pwg->categories(
        sprintf(
            "%s",
            join(';', @{$self->categories})
        )
    );

    $self->pwg->site_tags(
        join(',', @{$self->current_image->site_tags})
    );

    
}

# read Orientation exif tag from original image
# apply rotation to $file image
sub RotateImage {
    my ( $self, $file ) = @_;
    
    # exif from original image
    my $orientation = $self->current_image->exif_metadata->{Orientation};

    # Valid for Rotate 180, Rotate 90 CW, Rotate 270 CW
    if( $orientation =~ m/Rotate (\d+)/ ){
        printf(
            "Rotate %s\n",
            $1
        );

        my $image = Image::Magick->new();
        
        # read resized file
        my $status = $image->Read(
            $file
        );
        warn "$status ", $file, "\n" if $status ;
        return 0 if $status;
    
        $image->Rotate( degrees=>$1 );    
        
        # write resized file
        $image->Write(
            $file
        );
        warn "$status ", $file, "\n" if $status ;
        return 0 if $status;
        
        undef $image;
    
    }    
    return 1;
}

sub GetImage {
    my ( $self, $indx ) = @_;
    
    my $sum = $self->sums->[$indx];

    $self->image_sums->{$sum};
}

sub DeleteImage {
    my ( $self, $indx ) = @_;
    
    my $sum = $self->sums->[$indx];

    delete $self->image_sums->{$sum};
}

sub multi_selection_mode {
    my ( $self ) = @_;

    scalar @{$self->image_selection} > 1;
}


sub SetImageSelectionTags {
    my ( $self, $tags ) = @_;

    $self->image_selection_tags($tags) if 'ARRAY' eq ref $tags;

    # append to each image
    # if multiple selection
    if($self->multi_selection_mode){
        map {
            # need to dedup
            my $tags = [
                @{$self->GetImage($_)->site_tags},
                @{$self->image_selection_tags},
            ];
            #deduplicate
            my $uniq = {
                 map { $_ => 1 } @$tags
            };
            @$tags = keys %$uniq;
            $self->GetImage($_)->site_tags(
                $tags
            );
        }@{$self->image_selection};
    }

    $self->image_selection_tags;
}


sub SetImageSelectionPrivacyLevel {
    my ( $self, $privacy_level ) = @_;

    # append to each image
    # if multiple selection
    if($self->multi_selection_mode){
        if(defined $privacy_level){
            $self->image_selection_privacy_level($privacy_level);
            map {
                $self->GetImage($_)->privacy_level(
                    $privacy_level
                ) ;
            }@{$self->image_selection}
        };
    }

    $self->image_selection_privacy_level;
}


sub SetImageSelectionName {
    my ( $self, $name, $param ) = @_;
    
    # works in single and multi selection mode
    if(defined $name){
        map {
            my $_name;
            if( 'CODE' eq ref $name ){
                $_name = $name->($_, $param);
            }
            else{
                $_name = $name;
                $self->GetImage($_)->caption(
                    $_name
                ) ;
            }

            $self->image_selection_name($_name);
            $self->GetImage($_)->site_name(
                $_name
            ) ;
        }@{$self->image_selection}
    }
}

sub SetImageSelectionAuthor {
    my ( $self, $author ) = @_;

    # append to each image
    # if multiple selection
    if($self->multi_selection_mode){
        if(defined $author){
            $self->image_selection_author($author);
            map {
                $self->GetImage($_)->site_author(
                    $author
                ) ;
            }@{$self->image_selection}
        };
    }

    $self->image_selection_author;
}

sub SetImageSelectionComment {
    my ( $self, $comment ) = @_;

    # append to each image
    # if multiple selection
    if($self->multi_selection_mode){
        if(defined $comment){
            $self->image_selection_comment($comment);
            map {
                $self->GetImage($_)->site_comment(
                    $comment
                ) ;
            }@{$self->image_selection}
        };
    }

    $self->image_selection_comment;
}


sub SetImageSelectionCreateDate {
    my ( $self, $date ) = @_;

    # append to each image
    # if multiple selection
    if($self->multi_selection_mode){
        if(defined $date){
            $self->image_selection_create_date($date);
            map {
                $self->GetImage($_)->create_date(
                    $date
                ) ;
            }@{$self->image_selection}
        };
    }

    $self->image_selection_create_date;
}

1;