package FC_Solve::DepthMultiQueuePrototype;

use strict;
use warnings;
use FC_Solve::QueueInC;

use parent 'Games::Solitaire::Verify::Base';

__PACKAGE__->mk_acc_ref([qw(
        _num_items_per_page
        _offload_dir_path
        _num_inserted
        _num_items_in_queue
        _num_extracted
        _min_depth
        _max_depth
        _queues_by_depth
        _next_queue_id
        )]);

sub _init
{
    my $self = shift;
    my $args = shift;

    my $num_items_per_page = int($args->{num_items_per_page});

    if (not $num_items_per_page >= 1)
    {
        die "num_items_per_page must be greater or equal to 1.";
    }
    $self->_num_items_per_page($num_items_per_page);

    my $offload_dir_path = $args->{offload_dir_path};

    if (! -d $offload_dir_path)
    {
        die "offload_directory_path does not exist";
    }
    $self->_offload_dir_path($offload_dir_path);

    $self->_num_inserted(0);
    $self->_num_items_in_queue(0);
    $self->_num_extracted(0);

    my $first_depth = $args->{first_depth};
    my $first_item = $args->{first_item};

    $self->_next_queue_id(0);

    $self->_min_depth($first_depth);

    $self->_queues_by_depth(
        [
            $self->_new_queue(),
        ]
    );

    $self->_max_depth($first_depth);

    $self->insert($first_depth, $first_item);

    return;
}

sub _get_next_queue_id
{
    my ($self) = @_;

    my $ret = $self->_next_queue_id();

    $self->_next_queue_id($ret + 1);

    return $ret;
}

sub _new_queue
{
    my ($self) = @_;

    return FC_Solve::QueueInC->new(
        {
            num_items_per_page => $self->_num_items_per_page,
            offload_dir_path => $self->_offload_dir_path,
            queue_id => $self->_get_next_queue_id(),
        }
    );
}

sub insert
{
    my ($self, $depth, $item) = @_;

    while ($depth > $self->_max_depth())
    {
        push @{$self->_queues_by_depth()}, $self->_new_queue();
        $self->_max_depth($self->_max_depth + 1);
    }

    $self->_queues_by_depth->[$depth-$self->_min_depth()]->insert($item);

    $self->_num_inserted($self->_num_inserted() + 1);
    $self->_num_items_in_queue($self->_num_items_in_queue() + 1);

    return;
}

sub get_num_inserted
{
    my $self = shift;

    return $self->_num_inserted;
}

sub get_num_items_in_queue
{
    my $self = shift;

    return $self->_num_items_in_queue();
}

sub get_num_extracted
{
    my $self = shift;

    return $self->_num_extracted();
}

sub extract
{
    my $self = shift;

    if ($self->_num_items_in_queue() == 0)
    {
        return;
    }

    while ($self->_queues_by_depth->[0]->get_num_items_in_queue() == 0)
    {
        my $save_queue = shift(@{ $self->_queues_by_depth });
        push @{$self->_queues_by_depth()}, $save_queue;
        $self->_min_depth($self->_min_depth + 1);
        $self->_max_depth($self->_max_depth + 1);
    }

    $self->_num_items_in_queue($self->_num_items_in_queue() - 1);
    $self->_num_extracted($self->_num_extracted() + 1);

    return ($self->_min_depth, $self->_queues_by_depth->[0]->extract());
}

1;

