// -*- C++ -*-

#include <ept/core/list.h>

#ifndef EPT_CORE_SOURCE_H
#define EPT_CORE_SOURCE_H

namespace ept {
namespace core {

template< typename Self, typename Setup,
          template< typename Setup::PropertyId > class PType >
struct Source {
    typedef typename Setup::PropertyId PropertyId;
    typedef typename Setup::Token Token;

    Token m_cachedToken;
    typename Setup::Internal m_cached;

    Self &self() { return *static_cast< Self * >( this ); }

    template< PropertyId property >
    typename PType< property >::T get( Token t ) {
        if ( m_cachedToken != t ) {
            m_cachedToken = t;
            m_cached = self().lookupToken( t );
        }
        return self().template getInternal< property >( m_cached );
    }

    void invalidate() {
        m_cachedToken = ept::Token();
        m_cached = typename Setup::Internal();
    }

    template< PropertyId _property >
    struct ComposedList : wibble::mixin::Comparable< ComposedList< _property > >
    {
        typedef Self Origin;
        typedef typename Setup::Token Token;
        typedef typename PType< _property >::T Property;
        typedef ComposedList Type;

        Origin *origin;
        typename Setup::InternalList internal;

        ComposedList tail() const {
            return ComposedList< _property >( *origin, internal.tail() );
        }

        bool empty() const { return internal.empty(); }

        bool operator<( const ComposedList &o ) const {
            return token() < o.token();
        }

        ComposedList &head() { return *this; }
        const ComposedList &head() const { return *this; }

        Token token() const { return origin->getToken( internal.head() ); }

        Property property() const {
            return origin->template getInternal< _property >(
                internal.head() );
        }

        template< PropertyId P >
        typename PType< P >::T
        get() const {
            return origin->template getInternal< P >( internal.head() );
        }

        ComposedList() : origin( 0 ) {}

        ComposedList( Origin &o, typename Setup::InternalList i )
            : origin( &o ), internal( i ) {}
    };

    template< PropertyId property >
    ComposedList< property > list()
    {
        return ComposedList< property >( self(), self().listInternal() );
    }

    template< PropertyId P, typename F >
    struct Propertify {
        F f;
        Propertify( F _f = F() ) : f( _f ) {}
        bool operator()( const ComposedList< P > &x ) const {
            return f( x.token(), x.property() );
        }
    };

    template< PropertyId P, typename F >
    struct PropertyFilter {
        typedef typename list::Filtered<
            ComposedList< P >, Propertify< P, F > > T;
    };

    template< PropertyId P, typename F >
    typename PropertyFilter< P, F >::T
    propertyFilter( F f ) {
        return list::filter( list< P >(), Propertify< P, F >( f ) );
    }

    Source()
    {
    }
};

}
}

#endif
