package CPANPLUS::Dist::Gentoo::Atom;

use strict;
use warnings;

our $VERSION = '0.08';

use Carp         ();
use Scalar::Util ();

use overload (
 '<=>' => \&cmp,
 '""'  => \&as_string,
);

use CPANPLUS::Dist::Gentoo::Version;

my $version_rx = $CPANPLUS::Dist::Gentoo::Version::version_rx;

sub new {
 my $class = shift;
 $class = ref($class) || $class;

 my %args = @_;

 my ($category, $name, $version);
 if (defined $args{name}) {
  ($category, $name, $version) = @args{qw/category name version/};
  Carp::confess('Category unspecified') unless defined $category;
  /[^\w-]/ and Carp::confess('Invalid argument') for $name, $category;
 } elsif (defined $args{atom}) {
  my $atom = $args{atom};
  $atom =~ m{^([\w-]+)/([\w-]+)-v?($version_rx)$}
                                               or Carp::confess('Invalid atom');
  ($category, $name, $version) = ($1, $2, $3);
 } elsif (defined $args{ebuild}) {
  my $ebuild = $args{ebuild};
  $ebuild =~ m{/([\w-]+)/([\w-]+)/\2-v?($version_rx)\.ebuild$}
                                             or Carp::confess('Invalid ebuild');
  ($category, $name, $version) = ($1, $2, $3);
 } else {
  Carp::confess('Not enough information for building an atom object');
 }

 if (defined $version) {
  unless (Scalar::Util::blessed($version)
                              and $_->isa('CPANPLUS::Dist::Gentoo::Version')) {
   $version = CPANPLUS::Dist::Gentoo::Version->new($version);
  }
 }

 Carp::confess('Minimum atoms require a valid version') if  not defined $version
                                                        and $args{minimum};

 bless {
  category => $category,
  name     => $name,
  version  => $version,
  minimum  => $args{minimum},
  ebuild   => $args{ebuild},
 }, $class;
}

BEGIN {
 eval "sub $_ { \$_[0]->{$_} }" for qw/category name version minimum ebuild/;
}

sub cmp {
 my ($a1, $a2, $r) = @_;

 my $v1 = $a1->version;

 my $v2;
 if (Scalar::Util::blessed($a2) and $a2->isa(__PACKAGE__)) {
  Carp::confess('Can\'t compare atoms of different packages')
                    if $a1->category ne $a2->category or $a1->name ne $a2->name;
  $v2 = $a2->version;
 } else {
  $v2 = $a2;
 }

 ($v1, $v2) = ($v2, $v1) if $r;

 return $v1 <=> $v2;
}

sub as_string {
 my ($a) = @_;

 my $atom = $a->category . '/' . $a->name;

 my $version = $a->version;
 if (defined $version) {
  $atom = "=$atom-$version";
  $atom = ">$atom" if $a->minimum;
 }

 return $atom;
}

sub fold {
 shift unless length ref $_[0];

 my %seen;
 for my $atom (@_) {
  my ($category, $name, $version) = map $atom->$_, qw/category name version/;
  my $key = join '/', $category, $name;
  my $cur = $seen{$key};

  unless (defined $cur) {
   $seen{$key} = $atom;
   next;
  }

  next unless defined $version;

  if (not defined $cur->version) {
   $seen{$key} = $atom;
   next;
  }

  if ($atom->minimum) {
   if ($cur->minimum) {
    $seen{$key} = $atom < $cur ? $cur : $atom;
   } else {
    Carp::confess('Version mismatch') if $atom > $cur;
   }
  } elsif ($cur->minimum) {
   Carp::confess('Version mismatch') if $cur > $atom;
  }
 }

 return values %seen;
}

1;
