
package Text::Wigwam::Engine::Totem::Token::Directive;

    use strict;
    use warnings;
    no warnings q/recursion/;
    our $VERSION = '0.01';

    use base q/Text::Wigwam::Cast/;

    use Text::Wigwam::Const(
       qw/
        BLOCK
        ARRAY
        HASH
        TERM
        STRING
        VAR
        ANY
        VOID
        RUNONCE
      /
    );

    use Text::Wigwam::Engine::Totem::Const(
      qw/
        TEXT_BLOCK
        CODE_BLOCK
        FAKED
      /
    );

sub new {
    my ($class, @params) = @_;
    my %self;
    @self{qw/
        DATA
        DRAW
        MATE
        TYPE
        ONCE
    /} = @params;
    return bless \%self, ref($class) || $class;
}

sub dcache { $_[0]->{DRIP} = $_[1] }
sub data { $_[0]->{DATA} }
sub type { $_[0]->{TYPE} || 0 }
sub next { $_[0]->{NEXT} ||= $_[1] }
sub once { $_[0]->{ONCE}++ > 1 }
sub mate {
    my ($tokn) = @_;
    return $tokn->{MATE} if ($tokn->type);
    return $tokn->{MATE} ||=
      join(
        "" =>
        "#/",
        substr($_[0]->{DATA}, 1)
    );
}

sub drip {
     # Don't cache here.
    return
        $_[0]->{DRIP}
     || Text::Wigwam::Engine::Totem::Drip->new(@_);
}

sub args {
    my ($tokn, $Api) = @_;
 ##    return $_[0]->{PARMS} if $tokn->{PARMS};
 ##    my ($tokn, $Api) = @_;
    my $drip = $tokn->drip($Api);

    return $tokn->{PARMS} = [
        Text::Wigwam::Engine::Totem::Argument->new($drip),
        ($drip->expand)[2,3,4]
    ] if $tokn->{DRIP};# && ($Api->get_options qw/ drip_cache / & 4); # Cache OK
    return [
        Text::Wigwam::Engine::Totem::Argument->new($drip),
        ($drip->expand)[2,3,4]
    ]; # Don't Cache
}

sub exec {
    my ($tokn, $want, $pArg, $Api, $core) = @_;
     # Processing a directive
    $core->next_token;
    my ($Arg, $token, $handler, $vars) =
       @{$tokn->{PARMS} || $tokn->args($Api)};
    $Api->_new_arg($Arg=$Arg->clone);
    if ($want & RUNONCE) {
        $tokn->{ONCE} ||= 1;
        if( $tokn->once ) {
            $Api->kill_arg($Arg->remain);
            $Api->_pop_arg;
            return $Arg->null_data($want);
        }
    }
    elsif ($tokn->{ONCE} && $tokn->once) {
        $Api->exception( 'Priority directive called in non-VOID context' )
          unless ($want & VOID);
        $Api->kill_arg($Arg->remain);
        $Api->_pop_arg;
        return $Arg->null_data($want);
#        if ($want & VOID) {
#            $Api->kill_arg($Arg->remain);
#            $Api->_pop_arg;
#            return $Arg->null_data($want);
#        }
#        else {
#            $Api->exception( 'Priority directive called in non-VOID context' )
#        }
    }

#    elsif ($want & (VOID) && $tokn->{ONCE} && $tokn->once) {
#        $Api->kill_arg($Arg->remain);
#        $Api->_pop_arg;
#        return $Arg->null_data($want);
#    }
#
#    if (($want & (VOID|RUNONCE)) && $tokn->{ONCE} && $tokn->once) {
#        $Api->kill_arg($Arg->remain);
#        $Api->_pop_arg;
#        return $Arg->null_data($want);
#    }

    $Api->inc_scope( q/BLOCK/ ) if BLOCK & $want;
    $Api->add_global( @$vars ) if $vars;
    local $@;
    my $value = eval {
        $handler->(
            $Api,
            ($want & (HASH|ARRAY|ANY)) ? 1 : 0,  # List context
            ($want & VOID),
        )
    };
    my $err = $@;

    $Api->del_global( @$vars ) if $vars;
    $Api->dec_scope( q/BLOCK/ ) if BLOCK & $want;

    $Api->_pop_arg;

    if( (my $remain = $Arg->remain) || $err ) {
        $err ||= "$token did not pull all of its arguments, $remain remain.";
        $Api->exception( $err );
        $Api->kill_arg($remain) if $remain;
        return $Arg->null_data($want);
    }
    $tokn->{NEXT} ||= $Api->curr_addr;
    return $tokn->cast(
        $pArg,
        $Api,
        $want,
        $tokn->wg_type( $Api, $value ),
        $value,
    );
}

sub skip {
    my ($tokn, $list, $pArg, $Api, $core) = @_;
    if( my $next = $tokn->{NEXT} ){
        $core->curr_addr($next);
        return;
    }
    $core->next_token;
    return $core->curr_token->skip( $list, $pArg, $Api, $core )
      if !($tokn->type & TEXT_BLOCK) && ($pArg->type & TEXT_BLOCK);

    my $Arg = $Api->_new_arg(
        ( @{$tokn->{PARMS} || $tokn->args($Api)} )[0]->clone
    );

    $Arg->skip($Api) while( $Arg->remain );
    $Api->_pop_arg;
    $tokn->{NEXT} ||= ($core->curr_addr);
    return;
}

sub paint {
    my ($tokn, $pArg, $Api, $core) = @_;

    my $drip = $tokn->drip($Api, 1);
    my $Arg = $Api->_new_arg(
        Text::Wigwam::Engine::Totem::Argument->new($drip)
    );

    $core->next_token;
    my $itemlist = Text::Wigwam::Engine::Totem::Paint::Group->new(
        $tokn->_paint(
            $Api,
            (
                ($Arg->type & FAKED)
                   ? $drip->error || "Unreported error"
                   : ""
            ),
            $core,
        )
    );

    while( $Arg->remain ){
      no warnings 'uninitialized';
      if( $Arg->want & TERM ){
        my ($text, $end);
        my $list = Text::Wigwam::Engine::Totem::Paint::Sublist->new(
            Text::Wigwam::Engine::Totem::Paint::Group->new
        );
        until( $end ){
            ($text, $end)=$core->curr_token->paint(
                $Arg,
                $Api,
                $core
            );
            $list->add( $text ) unless $end;
        }
        $itemlist->add( $list, $text );
      }
      else{
        my ($text) = $core->curr_token->paint(
            $Arg,
            $Api,
            $core,
        );
        $itemlist->add( @$text );
      }
      $Arg->next;
    }
    $tokn->{NEXT} ||= $core->curr_addr;
    $Api->_pop_arg;
    return( $itemlist, 0 );
}

sub _paint {
    my ($self, $Api, $error, $core) = @_;
    unless ( $self->type & (TEXT_BLOCK | CODE_BLOCK) ) {
        return Text::Wigwam::Engine::Totem::Paint::Group->new(
         [
          Text::Wigwam::Engine::Totem::Paint::Token->new(
            $self->{DRAW}[0],
            $core->curr_addr,
            'directive'
          )
         ]
        ) unless $error;
        $Api->add_comment( $error );
        return Text::Wigwam::Engine::Totem::Paint::Group->new(
         [
          Text::Wigwam::Engine::Totem::Paint::Token->new(
            $self->{DRAW}[0],
            $core->curr_addr,
            'error'
          )
         ]
        );
    }
    return Text::Wigwam::Engine::Totem::Paint::Group->new(
     [
      Text::Wigwam::Engine::Totem::Paint::Token->new(
        $self->{DRAW}[0],
        $core->curr_addr,
        'tagentity'
      )
     ]
    ) unless $error;
    $Api->add_comment( $error );
    return Text::Wigwam::Engine::Totem::Paint::Group->new(
     [
      Text::Wigwam::Engine::Totem::Paint::Token->new(
        $self->{DRAW}[0],
        $core->curr_addr,
        'error'
      )
     ]
    );
}

1;
