
class Type;

/** Interface for HILTI types. */
class Concept : public hilti::util::type_erasure::ConceptBase {
public:
    Concept() = default;
    Concept(const Concept&) = default;
    Concept(Concept&&) = default;
    virtual ~Concept() = default;
    /** Returns true if the type is equivalent to another HILTI type. */
    virtual bool isEqual(const hilti::Type& other) const = 0;
    /**
* Returns any parameters associated with type. If a type is declared as
* `T<A,B,C>` this returns a vector of the AST nodes for `A`, `B`, and
* `C`.
*/
    virtual std::vector<Node> typeParameters() const = 0;
    /**
* Returns true if all instances of the same type class can be coerced
* into the current instance, independent of their pararameters. In HILTI
* source code, this typically corresponds to a type `T<*>`.
*/
    virtual bool isWildcard() const = 0;
    /** Returns the type of an iterator for this type. */
    virtual const hilti::Type& iteratorType(bool const_) const = 0;
    /** Returns the type of an view for this type. */
    virtual const hilti::Type& viewType() const = 0;
    /** Returns the type of elements the iterator traverse. */
    virtual const hilti::Type& dereferencedType() const = 0;
    /** Returns the type of elements the container stores. */
    virtual const hilti::Type& elementType() const = 0;
    /** Returns any parameters the type expects. */
    virtual hilti::node::Set<type::function::Parameter> parameters() const = 0;
    /** For internal use. Use ``type::isResolved` instead. */
    virtual bool _isResolved(type::ResolvedState* rstate) const = 0;
    /** Implements the `Node` interface. */
    virtual hilti::node::Properties properties() const = 0;
    /** Implements the `Node` interface. */
    virtual const std::vector<hilti::Node>& children() const = 0;
    /** Implements the `Node` interface. */
    virtual std::vector<hilti::Node>& children()  = 0;
    /** Implements the `Node` interface. */
    virtual const Meta& meta() const = 0;
    /** Implements the `Node` interface. */
    virtual void setMeta(Meta m)  = 0;
    /** Implements the `Node` interface. */
    virtual bool pruneWalk() const = 0;
    virtual bool _isAllocable() const = 0;
    virtual bool _isSortable() const = 0;
    virtual bool _isDereferenceable() const = 0;
    virtual bool _isIterable() const = 0;
    virtual bool _isViewable() const = 0;
    virtual bool _isIterator() const = 0;
    virtual bool _isView() const = 0;
    virtual bool _isParameterized() const = 0;
    virtual bool _isReferenceType() const = 0;
    virtual bool _isMutable() const = 0;
    virtual bool _isRuntimeNonTrivial() const = 0;
    virtual bool _takesArguments() const = 0;
    Concept& operator=(const Concept&) = default;
    Concept& operator=(Concept&&) = default;
    virtual Type _clone() const = 0;
    virtual hilti::IntrusivePtr<Concept> _clone_ptr() const = 0;
private:
    
};

template<typename T>
using ModelBase = hilti::util::type_erasure::ModelBase<T, Concept>;

template<class T>
class Model : public ModelBase<T> {
public:
    using ModelBase<T>::ModelBase;
    using ModelBase<T>::data;

    bool isEqual(const hilti::Type& other) const final;
    std::vector<Node> typeParameters() const final;
    bool isWildcard() const final;
    const hilti::Type& iteratorType(bool const_) const final;
    const hilti::Type& viewType() const final;
    const hilti::Type& dereferencedType() const final;
    const hilti::Type& elementType() const final;
    hilti::node::Set<type::function::Parameter> parameters() const final;
    bool _isResolved(type::ResolvedState* rstate) const final;
    hilti::node::Properties properties() const final;
    const std::vector<hilti::Node>& children() const final;
    std::vector<hilti::Node>& children()  final;
    const Meta& meta() const final;
    void setMeta(Meta m)  final;
    bool pruneWalk() const final;
    bool _isAllocable() const final;
    bool _isSortable() const final;
    bool _isDereferenceable() const final;
    bool _isIterable() const final;
    bool _isViewable() const final;
    bool _isIterator() const final;
    bool _isView() const final;
    bool _isParameterized() const final;
    bool _isReferenceType() const final;
    bool _isMutable() const final;
    bool _isRuntimeNonTrivial() const final;
    bool _takesArguments() const final;
    Type _clone() const final;
    hilti::IntrusivePtr<Concept> _clone_ptr() const final;
};

using ErasedBase = hilti::util::type_erasure::ErasedBase<hilti::trait::isType, Concept, Model>;

class Type : public ErasedBase, public hilti::trait::isNode {
public:
    using ErasedBase::ErasedBase;

    Type() noexcept = default;
    Type(Type&&) noexcept = default;
    Type& operator=(Type&&) = default;
    ~Type() override = default;
    Type(const Type& other) = default;
    Type& operator=(const Type& other) = default;

    bool isEqual(const hilti::Type& other) const;
    std::vector<Node> typeParameters() const;
    bool isWildcard() const;
    const hilti::Type& iteratorType(bool const_) const;
    const hilti::Type& viewType() const;
    const hilti::Type& dereferencedType() const;
    const hilti::Type& elementType() const;
    hilti::node::Set<type::function::Parameter> parameters() const;
    bool _isResolved(type::ResolvedState* rstate) const;
    hilti::node::Properties properties() const;
    const std::vector<hilti::Node>& children() const;
    std::vector<hilti::Node>& children() ;
    const Meta& meta() const;
    void setMeta(Meta m) ;
    bool pruneWalk() const;
    bool _isAllocable() const;
    bool _isSortable() const;
    bool _isDereferenceable() const;
    bool _isIterable() const;
    bool _isViewable() const;
    bool _isIterator() const;
    bool _isView() const;
    bool _isParameterized() const;
    bool _isReferenceType() const;
    bool _isMutable() const;
    bool _isRuntimeNonTrivial() const;
    bool _takesArguments() const;
    Type _clone() const;

    hilti::IntrusivePtr<Concept> _clone_ptr() const;

protected:
    type::detail::State _state_;

};

template<typename T>
bool Model<T>::isEqual(const hilti::Type& other) const { return data().isEqual(other); }

template<typename T>
std::vector<Node> Model<T>::typeParameters() const { 
    if constexpr ( std::is_base_of<hilti::type::trait::isParameterized, T>::value )
        return data().typeParameters();
    else
        return {};
 }

template<typename T>
bool Model<T>::isWildcard() const { 
    if constexpr ( std::is_base_of<hilti::type::trait::isParameterized, T>::value )
        return data().isWildcard();
    else
        return false;
 }

template<typename T>
const hilti::Type& Model<T>::iteratorType(bool const_) const { 
    if constexpr ( std::is_base_of<hilti::type::trait::isIterable, T>::value )
        return data().iteratorType(const_);
    else if constexpr ( std::is_base_of<hilti::type::trait::isView, T>::value )
        return data().iteratorType(const_);
    else
        throw std::bad_cast();
 }

template<typename T>
const hilti::Type& Model<T>::viewType() const { 
    if constexpr ( std::is_base_of<hilti::type::trait::isViewable, T>::value )
        return data().viewType();
    else
        throw std::bad_cast();
 }

template<typename T>
const hilti::Type& Model<T>::dereferencedType() const { 
    if constexpr ( std::is_base_of<hilti::type::trait::isDereferenceable, T>::value )
        return data().dereferencedType();
    else
        throw std::bad_cast();
 }

template<typename T>
const hilti::Type& Model<T>::elementType() const { 
    if constexpr ( std::is_base_of<hilti::type::trait::isIterable, T>::value )
        return data().elementType();
    else
        throw std::bad_cast();
 }

template<typename T>
hilti::node::Set<type::function::Parameter> Model<T>::parameters() const { 
    if constexpr ( std::is_base_of<hilti::type::trait::takesArguments, T>::value )
        return data().parameters();
    else
        throw std::bad_cast();
 }

template<typename T>
bool Model<T>::_isResolved(type::ResolvedState* rstate) const { return data()._isResolved(rstate); }

template<typename T>
hilti::node::Properties Model<T>::properties() const { return data().properties(); }

template<typename T>
const std::vector<hilti::Node>& Model<T>::children() const { return data().children(); }

template<typename T>
std::vector<hilti::Node>& Model<T>::children()  { return data().children(); }

template<typename T>
const Meta& Model<T>::meta() const { return data().meta(); }

template<typename T>
void Model<T>::setMeta(Meta m)  { return data().setMeta(m); }

template<typename T>
bool Model<T>::pruneWalk() const { return data().pruneWalk(); }

template<typename T>
bool Model<T>::_isAllocable() const { return std::is_base_of<hilti::type::trait::isAllocable, T>::value; }

template<typename T>
bool Model<T>::_isSortable() const { return std::is_base_of<hilti::type::trait::isSortable, T>::value; }

template<typename T>
bool Model<T>::_isDereferenceable() const { return std::is_base_of<hilti::type::trait::isDereferenceable, T>::value; }

template<typename T>
bool Model<T>::_isIterable() const { return std::is_base_of<hilti::type::trait::isIterable, T>::value; }

template<typename T>
bool Model<T>::_isViewable() const { return std::is_base_of<hilti::type::trait::isViewable, T>::value; }

template<typename T>
bool Model<T>::_isIterator() const { return std::is_base_of<hilti::type::trait::isIterator, T>::value; }

template<typename T>
bool Model<T>::_isView() const { return std::is_base_of<hilti::type::trait::isView, T>::value; }

template<typename T>
bool Model<T>::_isParameterized() const { return std::is_base_of<hilti::type::trait::isParameterized, T>::value; }

template<typename T>
bool Model<T>::_isReferenceType() const { return std::is_base_of<hilti::type::trait::isReferenceType, T>::value; }

template<typename T>
bool Model<T>::_isMutable() const { return std::is_base_of<hilti::type::trait::isMutable, T>::value; }

template<typename T>
bool Model<T>::_isRuntimeNonTrivial() const { return std::is_base_of<hilti::type::trait::isRuntimeNonTrivial, T>::value; }

template<typename T>
bool Model<T>::_takesArguments() const { return std::is_base_of<hilti::type::trait::takesArguments, T>::value; }

template<typename T>
Type Model<T>::_clone() const { return T(data()); }

template<typename T>
hilti::IntrusivePtr<Concept> Model<T>::_clone_ptr() const {
    if constexpr ( std::is_base_of<hilti::util::type_erasure::trait::Singleton, T>::value )
        return nullptr;
    else
        return hilti::make_intrusive<Model>(T(data()));
}

inline bool Type::isEqual(const hilti::Type& other) const { return data()->isEqual(other); }

inline std::vector<Node> Type::typeParameters() const { return data()->typeParameters(); }

inline bool Type::isWildcard() const { return data()->isWildcard(); }

inline const hilti::Type& Type::iteratorType(bool const_) const { return data()->iteratorType(std::move(const_)); }

inline const hilti::Type& Type::viewType() const { return data()->viewType(); }

inline const hilti::Type& Type::dereferencedType() const { return data()->dereferencedType(); }

inline const hilti::Type& Type::elementType() const { return data()->elementType(); }

inline hilti::node::Set<type::function::Parameter> Type::parameters() const { return data()->parameters(); }

inline bool Type::_isResolved(type::ResolvedState* rstate) const { return data()->_isResolved(std::move(rstate)); }

inline hilti::node::Properties Type::properties() const { return data()->properties(); }

inline const std::vector<hilti::Node>& Type::children() const { return data()->children(); }

inline std::vector<hilti::Node>& Type::children()  { return data()->children(); }

inline const Meta& Type::meta() const { return data()->meta(); }

inline void Type::setMeta(Meta m)  { return data()->setMeta(std::move(m)); }

inline bool Type::pruneWalk() const { return data()->pruneWalk(); }

inline bool Type::_isAllocable() const { return data()->_isAllocable(); }

inline bool Type::_isSortable() const { return data()->_isSortable(); }

inline bool Type::_isDereferenceable() const { return data()->_isDereferenceable(); }

inline bool Type::_isIterable() const { return data()->_isIterable(); }

inline bool Type::_isViewable() const { return data()->_isViewable(); }

inline bool Type::_isIterator() const { return data()->_isIterator(); }

inline bool Type::_isView() const { return data()->_isView(); }

inline bool Type::_isParameterized() const { return data()->_isParameterized(); }

inline bool Type::_isReferenceType() const { return data()->_isReferenceType(); }

inline bool Type::_isMutable() const { return data()->_isMutable(); }

inline bool Type::_isRuntimeNonTrivial() const { return data()->_isRuntimeNonTrivial(); }

inline bool Type::_takesArguments() const { return data()->_takesArguments(); }

inline Type Type::_clone() const { return data() ? data()->_clone() : Type(); }

inline hilti::IntrusivePtr<Concept> Type::_clone_ptr() const {
    if ( data() )
        return data()->_clone_ptr();
    else
        return nullptr;
}



