package GCUpdater;

###################################################
#
#  Copyright 2005 Tian
#  Copyright 2018-2022 Kerenoc
#
#  This file is part of GCstar.
#
#  GCstar 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; either version 2 of the License, or
#  (at your option) any later version.
#
#  GCstar 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 GCstar; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#
###################################################

use utf8;
use strict;

use LWP;
use Digest::MD5 qw(md5 md5_hex md5_base64);
use File::Find;

my $REPO_URL = 'http://gitlab.com/GCstar/GCstar/-/';
my $BASE_URL = $REPO_URL.'raw/main/gcstar/lib/gcstar/';
my $INDEX_URL = $REPO_URL.'jobs/artifacts/main/raw/list_file_digests.txt?job=digests';
my $INDEX_FILE = 'list_file_digests.txt';

{
    package GCRealUpdater;

    use File::Basename;
    use File::Path;
    use File::Copy;

    use GCUtils 'localName';

    sub new
    {
        my ($proto, $lang, $baseDir, $toBeUpdated, $version) = @_;
        my $class = ref($proto) || $proto;

        my $self  = {
            lang => $lang,
            baseUrl => $BASE_URL,
            indexUrl => $INDEX_URL,
            indexFile => $baseDir.'/'.$INDEX_FILE,
            baseInstallation => $baseDir.'/',
            toBeUpdated => $toBeUpdated
        };
        bless ($self, $class);

        $self->{filesList} = {};
        $self->{backupList} = {};
        $self->abort($self->{lang}->{UpdateNoPermission}.$baseDir) if (! -w localName($baseDir));
        $self->{next} = 0;
        $self->{total} = undef;

        return $self;
    }

    sub abort
    {
        my ($self, $msg) = @_;
        print "$msg\n";
        exit 1;
    }

    sub getNextFile
    {
        my $self = shift;

        return $self->{filesList}->[$self->{next}];
    }

    sub createBrowser
    {
        my ($self, $proxy) = @_;

        $self->{browser} = LWP::UserAgent->new( cookie_jar => {});
        $self->{browser}->proxy(['http'], $proxy) if $proxy;
    }

    sub checkFile
    {
        my ($self, $file) = @_;

        return 1 if $self->{toBeUpdated}->{all};
        foreach ('plugins', 'import', 'export', 'lang', 'models', 'extract')
        {
            return 1 if ($self->{toBeUpdated}->{$_}) && ($file =~ /$_/i);
        }
        return 0;
    }

    sub updateFiles
    {
        my $self = shift;

        # update file if sizes are different or digests are different
        while (my ($f, $d) = each(%{$self->{filesList}}))
        {
            my $fileName = localName($self->{baseInstallation}.$f);
            if (-r $fileName)
            {
                my $fileDigest = $self->{backupList}->{$f};
                if (! $fileDigest)
                {
                    open (my $fh, '<', $fileName);
                    $fileDigest = Digest::MD5->new->addfile($fh)->hexdigest;
                    close $fh;
                }
                # different digests => update
                $self->updateFile($fileName) if ($d ne $fileDigest);
            }
            else
            {
                $self->updateFile($fileName);
            }
        }
    }

    sub updateFile
    {
        my ($self, $fileName) = @_;

        print "Updating ",$fileName,"\n";
        mkpath(basename($fileName)) if ! -r $fileName;
        my $file = $fileName;
        $file =~ s|.*/lib/gcstar/||;
        my $response = $self->{browser}->get($self->{baseUrl}.$file, ':content_file' => $fileName);
        if (!$response->is_success)
        {
            print $response->message, "\n";
        }
        $self->{next}++;
    }

    sub createIndex
    {
        my $self = shift;

        if (-r $self->{indexFile})
        {
            copy($self->{indexFile}, $self->{indexFile}."_backup");
            $self->readIndex($self->{indexFile}."_backup",$self->{backupList});
        }
        # download a file containing all the digests for the source files from Gitlab CI
        print "Getting index file from Gitlab repository : $self->{indexUrl}\n";
        my $response = $self->{browser}->get($self->{indexUrl},
                                             ':content_file' => $self->{indexFile});
        if (!$response->is_success)
        {
            if (! -r $self->{indexFile})
            {
                $self->abort("!!! ".$self->{lang}->{UpdateNone});
                return;
            }
        }
        print "Using index file ".$self->{indexFile}."\n";
        $self->readIndex($self->{indexFile},$self->{filesList});
        $self->{total} = scalar %{$self->{filesList}};
    }

    sub readIndex
    {
        my ($self, $fileName, $index) = @_;

        open INDEX, localName($fileName);
        while (<INDEX>)
        {
            chomp;
            my ($f, $d) = split /\|/, $_;
            $index->{$f} = $d
                if (defined $f && $self->checkFile($f));
        }
        close INDEX;
    }

    sub total
    {
        my $self = shift;
        $self->createIndex if ( !defined $self->{total});
        return $self->{total};
    }
}

{
    package GCTextUpdater;

    sub new
    {
        my ($proto, $lang, $baseDir, $toBeUpdated, $noProxy, $version) = @_;
        my $class = ref($proto) || $proto;
        my $self  = {
            lang => $lang,
            noProxy => $noProxy,
            updater => GCRealUpdater->new($lang, $baseDir, $toBeUpdated, $version)
        };
        bless ($self, $class);

        return $self;
    }

    sub update
    {
        my $self = shift;

        my $proxy;
        if (!$self->{noProxy})
        {
            # use proxy from options, don't ask
            #$proxy = <STDIN>;
            #chomp $proxy;

            # get environment variables and configuration file
            # copied from the main gcstar program to avoid modify it

            #XDG stuff
            my $home = $ENV{'HOME'};
            $home = $ENV{'APPDATA'} if ($^O =~ /win32/i);
            $home =~ s/\\/\//g if ($^O =~ /win32/i);

            $ENV{XDG_CONFIG_HOME} = $home.'/gcstar/config' if ($^O =~ /win32/i);
            $ENV{XDG_CONFIG_HOME} = $home.'/.config' if ! exists $ENV{XDG_CONFIG_HOME};

            $ENV{XDG_DATA_HOME} = $home.'/gcstar' if ($^O =~ /win32/i);
            $ENV{XDG_DATA_HOME} = $home.'/.local/share' if ! exists $ENV{XDG_DATA_HOME};

            $ENV{GCS_CONFIG_HOME} = $ENV{XDG_CONFIG_HOME}.'/gcstar';
            $ENV{GCS_CONFIG_HOME} = $ENV{XDG_CONFIG_HOME} if ($^O =~ /win32/i);

            $ENV{GCS_CONFIG_FILE} = $ENV{GCS_CONFIG_HOME}.'/GCstar.conf';

            use GCOptions;
            my $self->{options} = new GCOptionLoader($ENV{GCS_CONFIG_FILE}, 1);
            $proxy = $self->{options}->proxy if ($self->{options}->proxy !~ m/^#/);
        }

        $self->{updater}->createBrowser($proxy);

        my $count = $self->{updater}->total;
        print $self->{lang}->{UpdateNone},"\n" if !$count;
        print "Checking ".$count." files\n";
        $self->{updater}->updateFiles();
        print "Updating ended\n";
    }
}

1;
