# --------------------------------------------------------------------------- #
# LazySequence Class
# --------------------------------------------------------------------------- #
# - An Lazy Immutable Sequence class
# --------------------------------------------------------------------------- #

^LazySequence := ^Class.new({});
^LazySequence.set_name('LazySequence');
^LazySequence.set_version('0.0.1');
^LazySequence.set_authority('url:pugscode.org');

^LazySequence.set_roles([ ^Seq ]);

^LazySequence.set_superclasses([ ^Object ]);

^LazySequence.add_attribute('$!current',   nil); 
^LazySequence.add_attribute('&generator', nil); 

^LazySequence.add_method('BUILD', -> %params {  
    # cache the current (head) element 
    # at instance creation time :)
	self`set_attr('$!current', self`get_attr('&generator')`());
});

# --------------------------------------------------------------------------- #
# Methods dependent upon the repr type

^LazySequence.add_private_method('head', -> {  self`get_attr('$!current') });
^LazySequence.add_private_method('tail', -> { 
    # create a new instance, which then forces the 
    # current element to be generated (see BUILD above)
    $?CLASS.new({ '&generator' => self`get_attr('&generator') });
});

# these two need to be re-implemented to return LazySequences

^LazySequence.add_method('apply', -> &code {
	&old_generator := self`get_attr('&generator');
	$?CLASS.new({ '&generator' => -> { &code`(&old_generator`()) } });	
});

^LazySequence.add_method('filter', -> &code {
    &old_generator := self`get_attr('&generator');
    &new_generator := -> { 
        &redo  := &?SUB;
        $value := &old_generator`();
        &code`($value)`as_bit()`if_else(
            -> { $value   },
            -> { &redo`() }
        ); 
    };
    $?CLASS.new({ '&generator' => &new_generator });
});

^Seq.add_method('zip', -> @other {
    &my_generator    := self`get_attr('&generator');
    &other_generator := @other`get_attr('&generator');    
    $?CLASS.new({ '&generator' => -> { [ &my_generator`(), &other_generator`() ] } });    
});

# NOTE:
# ideally this would not need to be re-implemented
# here just so that I could return a non-lazy Sequence
# but I see no other way currently.
^LazySequence.add_method('reverse', -> { 
	-> $list, @acc {
		&redo := &?SUB;
		$list.is_empty()`if_else(
			-> { ^Sequence.new(@acc) }, # << Must return a regular Sequcene type
			-> { &redo`($list!tail(), [ $list!head() ]`concat(@acc)) }
		);
	}`(self!tail(), [ self!head() ]);
});

