# +-----------------------------------------------------------------------+
# | pLoader - a Perl photo uploader for Piwigo                            |
# +-----------------------------------------------------------------------+
# | Copyright(C) 2008      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::PWG::WebServices;
 
use strict;
use warnings;
use MIME::Base64 qw(encode_base64); 
use JSON;
use LWP::UserAgent;
use Data::Dumper;
use Digest::MD5::File qw/file_md5_hex md5_hex/;
use File::Slurp;
use File::Spec;
use POSIX qw(ceil floor);
use base qw/
           Uploader::Object
           Class::Accessor::Fast
           /;

__PACKAGE__->mk_accessors( 
    qw/
           uagent
           urlbase
           username
           password
           qry_list_categories
           qry_add_categories
           qry_list_tags
           items
           tags
           categories
           site_high_file
           site_resized_file
           site_thumb_file
           site_image_name
           site_tags
           rank
           site_author
           site_comment
           site_img_date_creation
           uagent_response
           login_result
           action_result
           upload_high
           chunk_size
           sum_high_file
           sum_resized_file
           sum_thumb_file
           sum_original_file
      / 
);

$|=1;

sub Init {
    my ( $self, $version ) = @_ ;
 
    $self->uagent(
        LWP::UserAgent->new(
            agent => sprintf("Mozilla/pLoader %s", $version)        
        )
    );
    
    $self->uagent->cookie_jar({});	

    $self->urlbase(
        $self->{site_url}
    );
    
    $self->username(
        $self->{site_username}
    );
    
    $self->password(
        $self->{site_password}
    );
    
    $self->chunk_size(
        $self->{chunk_size}
    );

    
    $self->uagent->default_headers->authorization_basic(
        $self->{http_username}||$self->username, 
        $self->{http_password}||$self->password
    );
    
    
    $self->qry_list_categories( sprintf
        "%s/ws.php?format=json&method=%s&recursive=%s",
        $self->urlbase,
#        'pwg.categories.getAdminList',
        'pwg.categories.getList',
        'true',
    );

    $self->qry_list_tags( sprintf
        "%s/ws.php?format=json&method=%s",
        $self->urlbase,
        'pwg.tags.getAdminList',
    );


    my $form = {
        method => 'pwg.session.login',
        username => $self->username,
        password => $self->password,
    };
 
    $self->uagent_response(
        $self->uagent->post(
            $self->urlbase.'/ws.php?format=json',
            $form
        )
    );
    
    my $hresult = {} ;

    if($self->uagent_response->is_success){
        eval {
            $hresult = from_json(
                $self->uagent_response->content
            );
        };
    }
    else{
        $hresult = {
            'message' => $self->uagent_response->message,
            'stat'    => 'fail',
        };
    }

    $self->login_result(
        $hresult
    );

}

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

    my $result;
    eval {
        $result = $self->uagent->get(
            $self->qry_list_categories
        );
    };

    if($@){
        printf("An error occured in query execution %s\n%s",
            $self->qry_list_categories,
            $@,
        );	
    }
    my $hresult;
    
    eval {
    	$hresult = from_json(
            $result->content
        );
    };
    
    $hresult ||= {};

    my $categories = $hresult->{result}{categories};

    $categories;	
}

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

    my $result;
    eval {
        $result = $self->uagent->get(
            $self->qry_list_tags
        );
    };

    if($@){
        printf("An error occured in query execution %s\n%s",
            $self->qry_list_tags,
            $@,
        );	
    }
    my $hresult;
    
    eval {
    	$hresult = from_json(
            $result->content
        );
    };
    
    $hresult ||= {};

    $hresult->{result}{tags};
}

sub AddTags {
    my ( $self, $name ) = @_;

    my $form = {
        method            => 'pwg.tags.add',
        name              => $name,
        
    };

    my $result = $self->uagent->post(
        $self->urlbase.'/ws.php?format=json',
        $form
    );

    my $content = {};
        eval {
            $content = from_json(
                $result->content
            );
        };

    return ( $result->is_success, $result->status_line, $content );
	
} 


sub UploadImage {
    my ( $self, $progress ) = @_;

    $self->sum_resized_file(
        $self->_checksum(
            $self->site_resized_file,
            $progress
        )
    );

    $self->sum_thumb_file(
        $self->_checksum(
            $self->site_thumb_file,
            $progress
        )
    );
 
    $self->sum_high_file(
        $self->_checksum(
            $self->site_high_file,
            $progress
        )
    );
    # is the same in pLoader but may be different 
    $self->sum_original_file(
        $self->sum_high_file
    );


    my $site_image_name = $self->site_image_name; 

    my $image_id = $self->_exists($self->sum_original_file);;
    my $status = 0;
    my $status_line = "An error has occured during upload.";
    my $content = {};
    my $doubleCheck;
    my $form;
    while(1){
        if(!defined($image_id)){
        	
            my $resized_params = $self->_send_chunks('file', $progress);
            $status_line = $resized_params->{message};
            last if !$resized_params->{ok};
          
            my $thumb_params = $self->_send_chunks('thumb', $progress);
            $status_line = $thumb_params->{message};
            last if !$thumb_params->{ok};
       	
            $form = {
                method            => 'pwg.images.add',
                original_sum      => $self->sum_original_file,
                file_sum          => $self->sum_resized_file,
                thumbnail_sum     => $self->sum_thumb_file,
                categories        => $self->categories,
                name              => $site_image_name,
                rank              => $self->rank,
                author            => $self->site_author,
                comment           => $self->site_comment,
                date_creation     => $self->site_img_date_creation,
                tag_ids           => $self->site_tags,
                
            };
    
            if($self->upload_high){
                my $high_params = $self->_send_chunks('high', $progress);
                $status_line = $high_params->{message};
                last if !$high_params->{ok};
				# high is a HD resized copy of original
				$form->{high_sum} = $self->sum_high_file;
            }
            $doubleCheck = 1;
        }
        else {
            $form = {
                method            => 'pwg.images.setInfo',
                image_id          => $image_id,
                categories        => $self->categories,
                name              => $site_image_name,
                rank              => $self->rank,
                author            => $self->site_author,
                comment           => $self->site_comment,
                date_creation     => $self->site_img_date_creation,
                tag_ids           => $self->site_tags,
            };
            $progress->{yield}->();
        }

        delete $form->{tag_ids} unless defined $self->site_tags;
        delete $form->{tag_ids} if '' eq $self->site_tags;

        $progress->{yield}->();
        my $result = $self->uagent->post(
            $self->urlbase.'/ws.php?format=json',
            $form
        );
        eval {
            $content = from_json(
                $result->content
            );
        };

    
        $status = $result->is_success;
        $status_line = $result->status_line; 

        if($doubleCheck){
            $content->{stat} = 'fail' if !$self->_exists($self->sum_original_file); 
        }

        last;
    }
    
    return ( $status,  $status_line, $content);
}

sub _exists {
    my ( $self, $md5_sum ) = @_;

    my $form = {
        method            => 'pwg.images.exist',
        md5sum_list       => $md5_sum,
    };

    my $result = $self->uagent->post(
        $self->urlbase.'/ws.php?format=json',
        $form
    );

    my $hresult = {};
    eval {
        $hresult = from_json(
            $result->{_content}
        );
    };
    my $id;
    $id = $hresult->{result}{$self->sum_original_file} if 'ok' eq $hresult->{stat};
    
    $id;
} 

sub _checksum {
    my ( $self, $file, $progress ) = @_;
    my $file_sum;

    my $yield = $progress->{yield};

    $yield->();
    $progress->{msg_details}->(
        sprintf(
            "%s : %s", 
            $progress->{checksum_msg}, 
            $file
        )
    );
    eval {
        $file_sum = file_md5_hex(
            $file
        );
    };
    $yield->();

    $file_sum;
}

sub _send_chunks {
    my ( $self, $type, $progress ) = @_;

    my $msg = {
        thumb=>'thumbnail_msg',
        file=>'resized_msg',
        high=>'highdef_msg',
    };

    my $filepath = {
        thumb=>$self->site_thumb_file,
        file=>$self->site_resized_file,
        high=>$self->site_high_file,
    };

    $progress->{current_msg} = $progress->{$msg->{$type}};
    $progress->{yield}->();

    my $params = {
         filepath      => $filepath->{$type},
         type          => $type,
         original_sum  => $self->sum_original_file,
    };

    $self->send_chunks(
       $params,
       $progress,
    );
    $progress->{yield}->();

    $params;
}

sub AddCategories{
    my ( $self, $name, $parentid ) = @_;

    my $form = {
        method            => 'pwg.categories.add',
        name              => $name,
        parent            => $parentid,
        
    };

    my $result = $self->uagent->post(
        $self->urlbase.'/ws.php?format=json',
        $form
    );

    my $content = {};
        eval {
            $content = from_json(
                $result->content
            );
        };

    return ( $result->is_success, $result->status_line, $content );
	
}

sub SetInfoCategories{
    my ( $self, $name, $comment, $parentid ) = @_;

    my $form = {
        method            => 'pwg.categories.setInfo',
        name              => $name,
        comment           => $comment,
        category_id       => $parentid,
        
    };

    my $result = $self->uagent->post(
        $self->urlbase.'/ws.php?format=json',
        $form
    );

    my $content = {};
        eval {
            $content = from_json(
                $result->content
            );
        };

    return ( $result->is_success, $result->status_line, $content );
	
}


sub send_chunks {
    my ( $self, $params, $progress ) = @_;

    my $yield = $progress->{yield};
    my ( $vol, $dir, $filename ) = File::Spec->splitpath($params->{filepath});

    $yield->();
    $progress->{bar}->(0);
    $yield->();
    $progress->{msg_details}->(
        sprintf(
            "%s : %s", 
            $progress->{current_msg}, 
            $filename
        )
    );


    $yield->();
    my $content = read_file(
        $params->{filepath},
        binmode => ':raw',
    );
    $yield->();

    my $content_length = length($content);
    my $nb_chunks = ceil($content_length / $self->chunk_size);

    my $chunk_pos = 0;
    my $chunk_id = 1;


    while ($chunk_pos < $content_length) {

        my $chunk = substr(
            $content,
            $chunk_pos,
            $self->chunk_size
        );
        $chunk_pos += $self->chunk_size;
        my $response = $self->uagent->post(
            $self->urlbase.'/ws.php?format=json',
            {
                method       => 'pwg.images.addChunk',
                data         => encode_base64($chunk),
                original_sum => $params->{original_sum},
                position     => $chunk_id,
                type         => $params->{type},
            }
        );
        $yield->();
        $progress->{bar}->(100*($chunk_pos/$content_length));
        $progress->{msg_details}->(
            sprintf(
                "%s : %s", 
                $progress->{current_msg}, 
                $filename
            )
        );
        $params->{ok} = 1;
        if ($response->code != 200) {
            printf("response code    : %u\n", $response->code);
            printf("response message : %s\n", $response->message);
            $params->{ok} = 0;
            $params->{message} = $response->message;
            $params->{code} = $response->code;
            last;
        }

        $chunk_id++;
    }
}

 
1;
    