package DX::SearchState;

use Types::Standard qw(Maybe);
use DX::Step::Backtrack;
use DX::Step::ConsiderProposition;
use DX::Step::MarkAsSolution;
use DX::Class;

has current_hypothesis => (is => 'ro', isa => Hypothesis, required => 1);

has next_step => (is => 'ro', isa => Maybe[Step], required => 1);

has propositions => (is => 'ro', isa => PropositionSequence, required => 1);

has adjustments_made => (is => 'ro', isa => AdjustmentList, required => 1);

has is_solution_state => (is => 'ro', required => 1);

has on_exhaustion_step => (is => 'ro', isa => Maybe[Step], required => 1);

has on_solution_step => (is => 'ro', isa => Maybe[Step], required => 1);

sub next_proposition {
  my ($self) = @_;
  my $hyp = $self->current_hypothesis;
  $self->propositions->members->[
    $hyp->resolved_propositions->resolved_count + 1
  ];
}

sub new_for {
  my ($class, $hyp, $props) = @_;
  $class->new(
    current_hypothesis => $hyp,
    adjustments_made => [],
    propositions => $props,
    (@{$props->members}
      ? (
          next_step => DX::Step::ConsiderProposition->new(
            proposition => $props->members->[0],
          ),
          is_solution_state => 0,
        )
      : ( next_step => undef, is_solution_state => 1 )
    ),
    on_exhaustion_step => undef,
    on_solution_step => DX::Step::MarkAsSolution->new,
  );
}

sub with_one_step {
  my ($self) = @_;
  return undef unless my $step = $self->next_step;
  trace step => $step;
  return $step->apply_to($self);
}

sub force_backtrack {
  my ($self) = @_;
  my ($first_alt, @rest_alt) = @{$self->adjustments_made};
  return undef unless $first_alt;
  return $self->but(
    next_step => DX::Step::Backtrack->new,
  )->with_one_step;
}

1;
