# This module is part of debbugs, and
# is released under the terms of the GPL version 2, or any later
# version (at your option). See the file README and COPYING for more
# information.
# Copyright 2018 by Don Armstrong <don@donarmstrong.com>.

package Debbugs::Collection;

=head1 NAME

Debbugs::Collection -- Collection base class which can generate lots of objects

=head1 SYNOPSIS


=head1 DESCRIPTION



=cut

use Mouse;
use strictures 2;
use namespace::autoclean;

extends 'Debbugs::OOBase';

has 'members' => (is => 'bare',
		  isa => 'ArrayRef',
		  traits => ['Array'],
		  default => sub {[]},
                  writer => '_set_members',
		  handles => {_add => 'push',
			      members => 'elements',
			      count => 'count',
			      _get_member => 'get',
                              grep => 'grep',
                              apply => 'apply',
                              sort => 'sort',
			     },
		 );

has 'member_hash' => (traits => ['Hash'],
		      is => 'ro',
		      isa => 'HashRef[Int]',
		      lazy => 1,
		      reader => '_member_hash',
		      builder => '_build_member_hash',
                      clearer => '_clear_member_hash',
		      predicate => '_has_member_hash',
		      handles => {_add_member_hash => 'set',
				  _member_key_exists => 'exists',
				  _get_member_hash => 'get',
				 },
		     );

has 'universe' => (is => 'ro',
                   isa => 'Debbugs::Collection',
                   required => 1,
                   builder => '_build_universe',
                   writer => '_set_universe',
                   predicate => 'has_universe',
                  );

sub _build_universe {
    # By default, the universe is myself
    return $_[0];
}

sub limit {
    my $self = shift;
    my $limit = $self->clone();
    # Set the universe to whatever my universe is (potentially myself)
    $limit->_set_universe($self->universe);
    $limit->_set_members();
    $limit->_clear_member_hash();
    $limit->add($self->universe->get_or_create(@_));
    return $limit;
}

sub get_or_create {
    my $self = shift;
    my @return;
    my @exists;
    my @need_to_add;
    for my $i (0..$#_) {
        # we assume that if it's already a blessed reference, that it's the right
        if (blessed($_[$i])) {
            $return[$i] =
                $_[$i];
        }
        elsif ($self->_member_key_exists($_[$i])) {
            push @exists,$i;
        } else {
            push @need_to_add,$i;
        }
    }
    # create and add by key
    @return[@need_to_add] =
        $self->add_by_key(@_[@need_to_add]);
    @return[@exists] =
        $self->get(@_[@exists]);
    return @return;
}

has 'constructor_args' => (is => 'rw',
			   isa => 'ArrayRef',
			   default => sub {[]},
			  );

sub add_by_key {
    my $self = shift;
    # we'll assume that add does the right thing. around this in subclasses
    return $self->add(@_);
}

sub add {
    my $self = shift;
    my @members_to_add;
    for my $member (@_) {
	if ($self->exists($member)) {
	    next;
	}
	$self->_add($member);
	$self->_add_member_hash($self->member_key($member),
				$self->count(),
			       );
    }
    $self->_add(@members_to_add);
    return @members_to_add;
}

sub get {
    my $self = shift;
    return $self->_get_member($self->_get_member_hash(@_));
}


sub member_key {
    return $_[1];
}

sub exists {
    my $self = shift;
    return $self->_member_key_exists($self->member_key($_[0]));
}

sub _build_member_hash {
    my $self = shift;
    my $hash = {};
    my $i = 0;
    for my $member ($self->members) {
	$hash->{$self->member_key($member)} =
	    $i++;
    }
    return $hash;
}

__PACKAGE__->meta->make_immutable;

1;

__END__
# Local Variables:
# indent-tabs-mode: nil
# cperl-indent-level: 4
# End:
