package Data::Enumerator::Deeply;
use strict;
use warnings;
use Data::Enumerator::Base;
use Data::Visitor::Lite;
{
    package Data::Enumerator::Deeply::_Swap;
    sub new {
        my ( $class,$id ) = @_;
        return bless \$id , $class;
    }
    sub value {
        my ( $self,$value ) = @_;
        return $value->[$self->id];
    }
    sub id{
        my ( $self ) = @_;
        $$self;
    }
}
{
    package Data::Enumerator::Deeply::_Fixed;
    use base qw/Data::Enumerator::Deeply::_Swap/;
    sub new {
        my ( $class,$generator ) = @_;
        return bless { generator => $generator } , $class;
    }
    sub _generator{
        shift->{generator};
    }
    sub value {
        my $value = shift->_iterator->();
        return if Data::Enumerator::Base->is_last($value);
        return $value;
    }
    sub _iterator {
        my ($self ) = @_;
        return $self->{_iterator} ||= $self->_generator->iterator;
    }
}
sub independ {
    return Data::Enumerator::Deeply::_Fixed->new($_[0]);
}

sub __swapper {
    my ( $id ) = @_;
    return Data::Enumerator::Deeply::_Swap->new($id);
}

sub __product_all {
    my ( @generators ) = @_;
    my $result = shift @generators;
    while( my $gen = shift @generators ) {
        $result = $result->product($gen);
    }
    return $result;
}

sub compose {
    my ( $class,    $struct ) = @_;
    my ( $template, $object ) = __get_template_and_generator($struct);
    return $object->select(sub{
        my $value = shift;
        __convert_by_template( $template,$value );
    });
}

sub __get_template_and_generator{
    my ( $struct ) = @_;
    my @generators;
    my $count = 0;
    my $v     = Data::Visitor::Lite->new(
        ['-instance' => 'Data::Enumerator::Base'=> sub {
            my ($obj) = @_;
            push @generators,$obj;
            return __swapper( $count++);
        }]
    );
    my $template  = $v->visit( $struct );
    my $producted = __product_all(@generators);
    return ($template,$producted);
}

sub __convert_by_template {
    my ( $template, $value ) = @_;
    my $v = Data::Visitor::Lite->new(
        [   -instance => 'Data::Enumerator::Deeply::_Swap' => sub {
                my ($obj ) = @_;
                return $obj->value($value);
                }
        ]
    );
    return $v->visit($template);
}

1;