package DX::Role::Op::FindIsh;

use DX::Op::FromCode;
use DX::Op::FindAllCollect;
use DX::Var;
use DX::OrderedSet;
use DX::Op::Return;
use Moo::Role;

with 'DX::Role::Op';

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

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

requires 'make_result_handler';

sub run {
  my ($self, $state) = @_;
  my $values = [];
  my $coll = DX::OrderedSet->new(values => $values);
  my $collect = DX::Op::FindAllCollect->new(
    var_name => $self->var_name,
    into => $values
  );
  my $do_body = DX::Op::FromCode->new(
    code => sub {
      my ($self, $state) = @_;
      $state->push_return_then($collect, $self->next);
    },
    next => $self->body
  );
  my $var = DX::Var->new(id => "rule:findall")
                   ->with_stream(DX::ArrayStream->from_array(
                       $do_body, DX::Op::Return->new
                     ));
  my $invoke = DX::Op::FromCode->new(
    code => sub {
      my ($self, $state) = @_;
      my $op = $state->resolve_value($var);
      $state->then($op);
    }
  );
  my $allow = $state->allow_actions;
  my $ret = DX::Op::SetScope->new(
    scope => $state->scope,
    next => DX::Op::FromCode->new(
      code => sub {
        my ($self, $state) = @_;
        $state->but(allow_actions => $allow)
              ->then($self->next);
      },
      next => $self->make_result_handler($coll),
    )
  );
  $state->assign_vars($self->var_name => {})
        ->but(allow_actions => 0)
        ->push_return_then($ret, $invoke)->mark_choice($var);
}

1;
