#pragma once

#include "lazy.h"

namespace sequence {

template< typename T >
struct Parameter {
    Parameter( size_t n, const T &previous ) :
        n( n ),
        previous( previous )
    {}
    size_t n;
    const T &previous;
};

template< typename T >
struct Optional {

    Optional() :
        _set( false )
    {}
    Optional( const T &value ) :
        _set( true )
    {
        new ( &_u.data ) T( value );
    }
    Optional( const Optional &other ) :
        _set( other._set )
    {
        if ( _set )
            new ( &_u.data ) T( other._u.data );
    }
    Optional &operator=( Optional other ) {
        swap( other );
        return *this;
    }
    ~Optional() {
        if ( _set )
            _u.data.~T();
    }

    explicit operator bool() const noexcept {
        return _set;
    }

    template< typename... Args >
    auto operator()( Args &&... args ) {
        return get()( std::forward< Args >( args )... );
    }
    template< typename... Args >
    auto operator()( Args &&... args ) const {
        return get()( std::forward< Args >( args )... );
    }

    void set( const T &data ) {
        if ( _set )
            _u.data = data;
        else {
            new ( &_u.data ) T( data );
            _set = true;
        }
    }
    T &get() {
        return _u.data;
    }
    const T &get() const {
        return _u.data;
    }

    void swap( Optional &other ) {
        using std::swap;

        if ( _set && other._set ) {
            swap( _u.data, other._u.data );
        }
        else if ( _set ) {
            move( *this, other );
        }
        else if ( other._set ) {
            move( other, *this );
        }
    }

private:

    static void move( Optional &source, Optional &target ) {
        new ( &target._u.data ) T( std::move( source._u.data ) );
        source._u.data.~T();
        source._set = false;
        target._set = true;
    }

    bool _set;
    union U {
        U() {}
        ~U() {}
        T data;
        char padding[ sizeof( T ) ];
    } _u;
};

template<
    typename F,
    typename T
>
struct Iterator : std::iterator< std::forward_iterator_tag, T >
{
    Iterator() = default;
    Iterator( F f ) :
        _f( f ),
        _end( true ),
        _n( 0 ),
        _limit( 0 )
    {}
    Iterator( F f, const T &initial, size_t limit = 0 ) :
        _f( f ),
        _end( false ),
        _n( 0 ),
        _limit( limit ),
        _value( initial )
    {}
    Iterator( const Iterator & ) = default;
    Iterator &operator=( const Iterator &other ) {
        _end = other._end;
        _n = other._n;
        _limit = other._limit;
        _value = other._value;
        return *this;
    }

    Iterator &operator++() {
        ++_n;
        if ( _limit && _n >= _limit )
            _end = true;
        else if ( _f )
            _value.set( _f( Parameter< T >( _n, _value.get() ) ) );
        else
            std::abort();
        return *this;
    }
    Iterator operator++( int ) {
        Iterator self( *this );
        ++( *this );
        return self;
    }

    const T &operator*() const {
        return _value.get();
    }
    const T *operator->() const {
        return &_value.get();
    }

    bool operator==( const Iterator &other ) const {
        if ( _end && other._end )
            return true;
        return
            _end == other._end &&
            _n == other._n &&
            _limit == other._limit;
    }
    bool operator!=( const Iterator &other ) const {
        return !( *this == other );
    }

private:
    Optional< F > _f;
    bool _end;
    size_t _n;
    size_t _limit;
    Optional< T > _value;
};

template< typename F, typename T >
lazy::Range< Iterator< F, T > > finite( F f, const T &initial, size_t limit ) {
    return{
        {f, initial, limit},
        {f}
    };
}

template< typename F, typename T >
lazy::Range< Iterator< F, T > > infinite( F f, const T &initial ) {
    return{
        { f, initial },
        { f }
    };
}


}