package Text::Wigwam::Cast;

    use strict;
    use warnings;
    no warnings 'uninitialized';
    use base 'Text::Wigwam::Cast::Base';
    our $VERSION = '0.01';

    use Text::Wigwam::Const qw(:all);
    use Scalar::Util qw/reftype blessed refaddr/;

    use constant DTYPE => HASH|ARRAY|STRING|SCALAR;

    sub wg_type {
        my ($self, $Api, $val) = @_;

        return EXPR if $Api->is_expr($val);
        my $type = reftype($val) or return STRING;
        return
            (blessed($val) ? BLESSED : 0)
          | (
                $type eq 'HASH'   ? HASH
              : $type eq 'ARRAY'  ? ARRAY
              : $type eq 'SCALAR' ? SCALAR
              : $type eq 'CODE'   ? CODE
              : 0
            )
        ;
    }

    sub vivify {
         # Vivify variable if undefined.
        my ($self, $Api, $drip, $want, $vnam) = @_;
        $Api->set_value(
            $vnam,
            $want & HASH ? {} :
            $want & ARRAY ? [] :
            $want & SCALAR ? \do{ my $scalar = '' } :
            ''
        ) unless $Api->is_defined($vnam);
        return $vnam;
    }

    sub cast {
           no warnings 'uninitialized';
        my ($self, $Arg, $Api, $want, $have, $value) = @_;

        return ($want & DTYPE) ?
          $self->vivify( $Api, $Arg->drip, $want, $value ) : $value
            if $want & VAR;

        $have = (
            $self->wg_type( $Api, $value = $Api->get_value($value) )
          | ($want & BLOCK)
        ) if $have & VAR;

        return $value
          if $want & ANY; # Defer casting...

         # Macro-mode EXPR
        return $self->_exec_expr( $Api, $Arg, $want, $value )
          if $have & EXPR;

        return $value if $have & $want;

        if( $have & CODE ){
            my $val = $value->();
            return $self->cast(
                $Arg,
                $Api,
                $want,
                $self->wg_type($Api, $val),
                $val
            );
        }

        local $@;
        eval { $value = Text::Wigwam::Cast::Base->bit_cast($have, $want, $value) };
        return $value unless $@;
        $Api->exception( $@ );
        return $Arg->null_data($want);

    }

    sub _exec_expr {
        my ($self, $Api, $Arg, $want, $value) = @_;

        $Api->inc_scope( 'BLOCK' ) if $want & BLOCK;
        $value = $value->execute;
        $Api->dec_scope( 'BLOCK' ) if $want & BLOCK;
        return $self->cast(
            $Arg,
            $Api,
            $want,
            $self->wg_type($Api, $value),
            $value
        );
    }

1;
