#
# (c) Jan Gehring <jan.gehring@gmail.com>
#
# vim: set ts=2 sw=2 tw=0:
# vim: set expandtab:

package Rex::Pkg::Gentoo;

use strict;
use warnings;

our $VERSION = '1.7.0'; # VERSION

use Rex::Commands::Run;
use Rex::Helper::Run;
use Rex::Commands::File;
use Rex::Pkg::Base;
use base qw(Rex::Pkg::Base);

sub new {
  my $that  = shift;
  my $proto = ref($that) || $that;
  my $self  = $proto->SUPER::new(@_);

  bless( $self, $proto );

  $self->{commands} = {
    install         => 'emerge --update --changed-use %s',
    install_version => 'emerge --update --changed-use =%s-%s',
    reinstall_check =>
      'emerge --pretend --update --changed-use --nodeps --quiet --verbose =%s',
    update_system => 'emerge --update --deep --with-bdeps=y --newuse world',
    dist_update_system =>
      'emerge --update --deep --with-bdeps=y --newuse world',
    remove            => 'emerge -C %s',
    update_package_db => 'emerge --sync',
  };

  return $self;
}

sub bulk_install {
  my ( $self, $packages_aref, $option ) = @_;

  delete $option->{version}; # makes no sense to specify the same version for several packages

  $self->update( "@{$packages_aref}", $option );

  return 1;
}

sub is_installed {

  my ( $self, $pkg, $option ) = @_;
  my $slot;
  my $version = $option->{version};

  # Determine slot.
  my $slot_idx = index( $pkg, ':' );
  if ( $slot_idx != -1 ) {
    die
      "Illegal package spec. `$pkg-$version': Both package and version has SLOT"
      if $version && index( $version, ':' ) != -1;
    $slot = substr( $pkg, $slot_idx + 1 );
    substr( $pkg, $slot_idx ) = '';
  }
  elsif ($version) {
    $slot_idx = index( $version, ':' );
    if ( $slot_idx != -1 ) {
      $slot = substr( $version, $slot_idx + 1 );
      substr( $version, $slot_idx ) = '';
    }
  }

  $self->{short} = 0;
  Rex::Logger::debug( "Checking if $pkg"
      . ( $version ? "-$version" : "" )
      . ( $slot    ? ":$slot"    : '' )
      . " is installed" );

  my @pkg_info = grep { $_->{name} eq $pkg } $self->get_installed();
  @pkg_info = grep { $_->{version} eq $version } @pkg_info if defined $version;

  unless (@pkg_info) {
    Rex::Logger::debug(
      "Couldn't find package by category/packagename, trying with packagename only"
    );
    $self->{short} = 1;
    @pkg_info = grep { $_->{name} eq $pkg } $self->get_installed();
    @pkg_info = grep { $_->{version} eq $version } @pkg_info
      if defined $version;
  }

  unless (@pkg_info) {
    Rex::Logger::debug( "$pkg"
        . ( $version ? "-$version" : "" )
        . ( $slot    ? ":$slot"    : '' )
        . " is NOT installed." );
    return 0;
  }

  # Check for requested SLOT.
  my $pkg_atom;

  if ( defined $slot ) {
    my $slot_ok;

    for my $info (@pkg_info) {
      $pkg_atom =
        "$info->{name}-$info->{version}$info->{suffix}$info->{release}";

      my $fh = file_read("/var/db/pkg/$pkg_atom/SLOT");
      chomp( my $slot_installed = $fh->read_all );
      $fh->close;

      if ( $slot eq $slot_installed ) {
        $pkg_atom .= ":$slot";
        $slot_ok = 1;
        last;
      }
    }

    unless ($slot_ok) {
      Rex::Logger::debug( "$pkg"
          . ( $version ? "-$version" : "" )
          . ( $slot    ? ":$slot"    : '' )
          . " is NOT installed." );
      return 0;
    }
  }
  else {
    $pkg_atom =
      "$pkg_info[0]->{name}-$pkg_info[0]->{version}$pkg_info[0]->{suffix}$pkg_info[0]->{release}";
  }

  # Check for any USE flag changes.
  my $rchk_cmd = sprintf( $self->{commands}->{reinstall_check}, $pkg_atom );
  my @rchk_out = i_run $rchk_cmd;

  for my $line (@rchk_out) {
    next unless $line =~ /\s*\[ebuild[^]]+\]\s+$pkg_info[0]->{name}/;

    Rex::Logger::debug( "$pkg"
        . ( $version ? "-$version" : "" )
        . ( $slot    ? ":$slot"    : '' )
        . " is installed but USE flags have changed." );
    return 0;
  }

  Rex::Logger::debug( "$pkg"
      . ( $version ? "-$version" : "" )
      . ( $slot    ? ":$slot"    : '' )
      . " is installed." );
  return 1;

}

sub get_installed {
  my ($self) = @_;
  my $cut_cmd;
  if ( $self->{short} ) {
    $cut_cmd = "cut -d '/' -f6-";
  }
  else {
    $cut_cmd = "cut -d '/' -f5-";
  }

  # ,,stolen'' from epm
  my $pkgregex = '(.+?)' .                  # name
    '-(\d+(?:\.\d+)*\w*)' .                 # version, eg 1.23.4a
    '((?:(?:_alpha|_beta|_pre|_rc)\d*)?)' . # special suffix
    '((?:-r\d+)?)$';                        # revision, eg r12

  my @ret;

  for my $line ( i_run("ls -d /var/db/pkg/*/* | $cut_cmd") ) {
    my $r = qr{$pkgregex};
    my ( $name, $version, $suffix, $revision ) = ( $line =~ $r );
    push(
      @ret,
      {
        name    => $name,
        version => $version,
        suffix  => $suffix,
        release => $revision,
      }
    );
  }

  return @ret;
}

sub add_repository {
  my ( $self, %data ) = @_;

  my $name  = $data{"name"};
  my $readd = $data{"readd"} // 0;

  if ( can_run("layman") ) {
    my $op;

    i_run "layman -lqnN | grep -q '$name'", fail_ok => 1;
    if ( $? == 0 ) {
      if ($readd) {
        $op = 'r'; # --readd
      }
      else {
        Rex::Logger::info(
          "Repository $name is present, use `readd' option to re-add from scratch."
        );
      }
    }
    else {
      $op = 'a';   # --add
    }
    i_run "layman -$op $name" if defined $op;
  }
  else {
    Rex::Logger::debug("You have to install layman, git and subversion.");
    die("Please install layman, git and subversion");
  }
}

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

  if ( can_run("layman") ) {
    i_run "layman -d $name";
  }
  else {
    Rex::Logger::debug("You have to install layman, git and subversion.");
    die("Please install layman, git and subversion");
  }
}

1;