package Uploader::Images;
use strict;
use base qw/Uploader::Object/;
use Uploader::Image;
use Digest::MD5::File qw/file_md5_hex md5_hex/;
require Win32 if($^O =~ /MSWin32/);
use Storable;
use Data::Dumper;

$|=1;

my @cbk_properties = qw/
    add_images_cbk
    delete_images_cbk
    create_wx_thumbnail_cbk
    default_caption_cbk
    default_caption_pattern_cbk
/;

my @storable_properties = qw/
    ordered_image_ids
    selection
    _images
    current_image
    selection_tags
    selection_privacy_level
    selection_name
    selection_comment
    selection_author
    selection_create_date
    author
    type
/;

my @app_properties = qw/
    eng_caption_patterns
/;

__PACKAGE__->mk_accessors(@cbk_properties);
__PACKAGE__->mk_accessors(@storable_properties);
__PACKAGE__->mk_accessors(@app_properties);

my $_global_rank = -1;

sub Init {
    my ( $self ) = @_;

    $self->_images({}) if !defined $self->_images;
    $self->ordered_image_ids([]) if !defined $self->ordered_image_ids;
    $self->selection([]) if !defined $self->selection;
}


sub add_images {
    my ( $self, $file_paths ) = @_;

    my $files = [
        map {
            # to make sure that unicode chars in filenames are supported
            {
                ANSIPathName => $^O =~ /MSWin32/ ? Win32::GetANSIPathName($_) : $_,
                PathName => $_,
            },
        }@$file_paths
    ];

    @$files = sort { $a->{PathName} cmp $b->{PathName} } @$files;    



    foreach my $file ( @{$files} ) {
        # to append at the end of list
        $self->add_image_from_file($file);
    }
    
    $self->Store;
    
}


sub add_image_from_file {
    my ( $self, $file ) = @_;    

    # pLoader image id
    my $image_id = $self->new_image_id(
        $file->{ANSIPathName}
    );

    my $i = $self->image_count;
    $self->ordered_image_ids->[$i] = $image_id ;


    my $image = $self->_images->{
        $image_id
    };
    if ( !defined $image ){
        $image = Uploader::Image->new(
            {
                file              => $file->{ANSIPathName},
                image_id          => $image_id,
                caption           => $self->default_caption_cbk->(),
                site_author       => $self->author,
                add_rank          => $i,
                site_tags         => [],
                site_high_file    => $file->{ANSIPathName},
                global_rank       => ++$_global_rank,
            }
        );

        $image->site_name(
            $self->init_caption_from_pattern(
                $image->file,
                $image->create_date,
                $image->add_rank,
                $self->default_caption_cbk->(),
                $self->default_caption_pattern_cbk->()
            )
        );

        $self->create_wx_thumbnail_cbk->($image)
            if 'CODE' eq ref $self->create_wx_thumbnail_cbk;

        $self->_images->{$image_id} = $image ;
    }

    $self->add_images_cbk->($image->wx_thumb_file)
       if 'CODE' eq ref $self->add_images_cbk;

    $image;
}


sub new_image_id {
    my ( $self, $filename ) = @_;

    my $image_id = file_md5_hex(
        $filename
    );

    $image_id;
}


sub Store {
    my ( $self ) = @_;
    
    my $data = $self->get_storable(
        [ 
            qw/
                version
                storable_file
            /,
            @storable_properties
        ] 
    
    );
    eval {
        #print Dumper $data;
        store $data, $self->storable_file if defined $self->storable_file;
    };
    if($@){
        print $@, "\n";    
    }
}


sub set_current_image {
    my ( $self, $indx ) = @_;
    $self->current_image(
        $indx != -1 ? $self->get_image($indx) : Uploader::Image->new()
    );
}


sub get_image {
    my ( $self, $indx ) = @_;
    
    my $id = $self->ordered_image_ids->[$indx];

    $self->_images->{$id};
}


sub Image {
    my ( $self, $id ) = @_;
    
    $self->_images->{$id};
}


sub is_empty {
    my ( $self ) = @_;

    !$self->image_count;
}


sub image_count {
    my ( $self ) = @_;

    scalar @{$self->ordered_image_ids};
}


sub selection_count {
    my ( $self ) = @_;

    scalar @{$self->selection};
}


sub remove_selection {
    my ( $self ) = @_;
    
    return if ( $self->is_empty );
    return if (! defined $self->selection );
    
    $self->_remove_selection($self->selection);
    # clear image selection
    $self->selection([]);
}


sub _remove_selection {
    my ( $self, $list ) = @_;
    # the list is sorted, ascendant
    # we reverse it to have
    # higher first, and keep same indexes while deleting
    @$list = reverse @$list;     
    map {
        $self->delete_image($_);
        $self->delete_images_cbk->($_);
        splice @{$self->ordered_image_ids}, $_, 1 ;
        shift @$list;
    }
    @$list;

}

sub delete_image {
    my ( $self, $indx ) = @_;
    
    my $id = $self->ordered_image_ids->[$indx];

    # have to check if that id is used more than once
    my $count = grep { $id eq $_} @{$self->ordered_image_ids};

    delete $self->_images->{$id} unless $count > 1;
}


sub multi_selection_mode {
    my ( $self ) = @_;

    scalar @{$self->selection} > 1;
}


sub set_image_selection_tags {
    my ( $self, $tags ) = @_;

    $self->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->get_image($_)->site_tags},
                @{$self->selection_tags},
            ];
            #deduplicate
            my $uniq = {
                 map { $_ => 1 } @$tags
            };
            @$tags = keys %$uniq;
            $self->get_image($_)->site_tags(
                $tags
            );
        }@{$self->selection};
    }

    $self->selection_tags;
}


sub set_image_selection_privacy_level {
    my ( $self, $privacy_level ) = @_;

    # append to each image
    # if multiple selection
    if($self->multi_selection_mode){
        if(defined $privacy_level){
            $self->selection_privacy_level($privacy_level);
            map {
                $self->get_image($_)->privacy_level(
                    $privacy_level
                ) ;
            }@{$self->selection}
        };
    }

    $self->selection_privacy_level;
}


sub set_image_selection_name {
    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->get_image($_)->caption(
                    $_name
                ) ;
            }

            $self->selection_name($_name);
            $self->get_image($_)->site_name(
                $_name
            ) ;
        }@{$self->selection}
    }
}


sub set_image_selection_author {
    my ( $self, $author ) = @_;

    # append to each image
    # if multiple selection
    if($self->multi_selection_mode){
        if(defined $author){
            $self->selection_author($author);
            map {
                $self->get_image($_)->site_author(
                    $author
                ) ;
            }@{$self->selection}
        };
    }

    $self->selection_author;
}

sub set_image_selection_comment {
    my ( $self, $comment ) = @_;

    # append to each image
    # if multiple selection
    if($self->multi_selection_mode){
        if(defined $comment){
            $self->selection_comment($comment);
            map {
                $self->get_image($_)->site_comment(
                    $comment
                ) ;
            }@{$self->selection}
        };
    }

    $self->selection_comment;
}


sub set_image_selection_create_date {
    my ( $self, $date ) = @_;

    # append to each image
    # if multiple selection
    if($self->multi_selection_mode){
        if(defined $date){
            $self->selection_create_date($date);
            map {
                $self->get_image($_)->create_date(
                    $date
                ) ;
            }@{$self->selection}
        };
    }

    $self->selection_create_date;
}


sub get_current_image_caption {
    my ( $self, $index, $pattern ) = @_;

    $pattern = $self->eng_caption_patterns->{$pattern};

    $self->set_current_image($index);

    my $img = $self->current_image;

    $self->init_caption_from_pattern(
        $img->file,
        $img->create_date,
        $index,
        $self->current_image->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;
}


# we cannot send objects in other threads
# and thread use data as they were when the thread is created
sub get_images {
    my ( $self, $preferences, $destination_category, $all ) = @_;

    my $image_id_list = $all ?
        $self->ordered_image_ids :
        [
            map {
                $self->ordered_image_ids->[$_]
            }@{$self->selection}
        ];

    $self->get_images_data(
        $image_id_list,
        $preferences,
        $destination_category
    );
}


my $add_rank = 0;
# copy data from image objects
sub get_images_data {
    my ( $self, $image_id_list, $preferences, $destination_category ) = @_;


    return [
        map{
            $self->Image($_)->get_data($preferences, $destination_category, $add_rank++)
        }@$image_id_list
    ]
}


sub reset_add_rank {
    my ( $self ) = @_;

    $add_rank = 0;
}
1;
