# Copyright (c) 2016 SUSE LLC
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 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 (see the file COPYING); if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#
package BSSrcServer::Product;

use strict;
use warnings;

use BSConfiguration;
use BSSrcServer::Link;
use BSUtil;
use BSRevision;
use BSSrcrep;
use BSXML;
use BSVerify;

my $projectsdir = "$BSConfig::bsdir/projects";
my $srcrep = "$BSConfig::bsdir/sources";
my $uploaddir = "$srcrep/:upload";

our $notify_repservers = sub {
};

# run the productconverter on _product to create/update/delete
# all _product:xxx packages
#
sub expandproduct {
  my ($projid, $packid, $rev, $files, $user, $fail) = @_;

  if (!$files) {
    # gone!
    my @packages = grep {/^\Q${packid}:\E/} BSRevision::lspackages_local($projid);
    for my $opid (@packages) {
      unlink("$projectsdir/$projid.pkg/$opid.upload-MD5SUMS");
      unlink("$projectsdir/$projid.pkg/$opid.rev");
      unlink("$projectsdir/$projid.pkg/$opid.mrev");
      unlink("$projectsdir/$projid.pkg/$opid.xml");
      $notify_repservers->('package', $projid, $opid);
    }
    return 1;
  }
  if ($files->{'_link'}) {
    eval {
      $files = BSSrcServer::Link::handlelinks({ %$rev, 'project' => $projid, 'package' => $packid }, $files);
      die("$files\n") unless ref $files;
    };
    if ($@) {
      die($@) if $fail;
      return undef;
    }
  }
  my $dir = "$uploaddir/expandproduct_$$";
  BSUtil::cleandir($dir);
  mkdir_p($dir);
  for my $file (sort keys %$files) {
    BSSrcrep::copyonefile_tmp($projid, $packid, $file, $files->{$file}, "$dir/$file");
  }
  my @prods = grep {/.product$/}  sort keys %$files;
  my %pids;
  for my $prod (@prods) {
    print "converting product $prod\n";
    my $odir = "$dir/$prod.out";
    my $olog = "$dir/$prod.logfile";
    system('rm', '-rf', $odir) if -d $odir;
    unlink($olog) if -e $olog;
    mkdir_p($odir);
    # run product converter and read stdout
    my $pid;
    if (!($pid = xfork())) {
      delete $SIG{'__DIE__'};
      open(STDOUT, '>>', $olog) || die("$olog: $!\n");
      open(STDERR, '>&STDOUT');
      $| = 1;
      exec("./bs_productconvert", "$dir/$prod", $odir, $projid);
      die("500 bs_productconvert: $!\n");
    }
    waitpid($pid, 0) == $pid || die("500 waitpid $pid: $!\n");
    if ($?) {
      my $s = readstr($olog);
      $s =~ s/^\n+//s;
      $s =~ s/\n+$//s;
      warn("bs_productconvert failed: $?\n");
      BSUtil::cleandir($dir);
      rmdir($dir);
      die("$s\n") if $fail;
      return undef;
    }
    my @out = sort(ls($odir));
    if (!@out) {
      warn("bs_productconvert produced nothing\n");
      BSUtil::cleandir($dir);
      rmdir($dir);
      return undef;
    }
    for my $p (@out) {
      my $pdir = "$odir/$p";
      my $pid = $p;
      $pid =~ s/^_product[_:]//;
      $pid =~ s/[:\000-\037]/_/sg;
      $pid = "$packid:$pid";
      $pids{$pid} = 1;
      my %pfiles;
      for my $pfile (sort(ls($pdir))) {
        next if $pfile eq '_meta';
	$pfiles{$pfile} = BSSrcrep::addfile($projid, $pid, "$pdir/$pfile", $pfile);
      }
      my $srcmd5 = BSSrcrep::addmeta($projid, $pid, \%pfiles);
      my $oldrev = BSRevision::getrev_local($projid, $pid);
      if ($oldrev && $oldrev->{'srcmd5'} eq $srcmd5 && $oldrev->{'rev'}) {
	# we're lucky, no change
	next;
      }
      my $prev = {'srcmd5' => $srcmd5, 'time' => time(), 'user' => $user, 'comment' => 'autogenerated', 'version' => '1', 'vrev' => '1'};
      BSRevision::addrev_local({'vrev' => 1, 'nolinkinfodb' => 1}, $projid, $pid, $prev);
      if (! -e "$projectsdir/$projid.pkg/$pid.xml") {
	my $pidpack = readxml("$pdir/_meta", $BSXML::pack, 1);
	if ($pidpack) {
	  eval {
	    $pidpack->{'name'} = $pid unless defined $pidpack->{'name'};
	    BSVerify::verify_pack($pidpack, $pid);
	  };
	  if ($@) {
	    warn($@);
	    undef $pidpack;
	  }
	}
        $pidpack ||= {
         'name' => $pid,
         'title' => $pid,
         'description' => "autogenerated from $packid by source server",
        };
	# XXX no mrev files for product packages?
	writexml("$projectsdir/$projid.pkg/.$pid.xml", "$projectsdir/$projid.pkg/$pid.xml", $pidpack, $BSXML::pack);
      }
      rmdir($pdir);
      $notify_repservers->('package', $projid, $pid);
    }
    rmdir($odir);
  }
  BSUtil::cleandir($dir);
  rmdir($dir);
  # now do away with the old packages
  my @packages = grep {/^\Q${packid}:\E/} BSRevision::lspackages_local($projid);
  @packages = grep {!$pids{$_}} @packages;
  for my $opid (@packages) {
    unlink("$projectsdir/$projid.pkg/$opid.upload-MD5SUMS");
    unlink("$projectsdir/$projid.pkg/$opid.rev");
    unlink("$projectsdir/$projid.pkg/$opid.mrev");
    unlink("$projectsdir/$projid.pkg/$opid.xml");
    $notify_repservers->('package', $projid, $opid);
  }
  return 1;
}

1;
