#ifndef TYPE_H
#define TYPE_H

#include "SynTree.h"
#include "Visitor.h"
#include "Mutator.h"


class Type {
  public:
    struct Qualifiers {  
      Qualifiers(): isConst( false ), isVolatile( false ), isRestrict( false ), isLvalue( false ) {}
      Qualifiers( bool isConst, bool isVolatile, bool isRestrict, bool isLvalue ): isConst( isConst ), isVolatile( isVolatile ), isRestrict( isRestrict ), isLvalue( isLvalue ) {}
	
	Qualifiers &operator+=( const Qualifiers &other );
	Qualifiers &operator-=( const Qualifiers &other );
	Qualifiers operator+( const Type::Qualifiers &other );
	bool operator==( const Qualifiers &other );
	bool operator!=( const Qualifiers &other );
	bool operator<=( const Qualifiers &other );
	bool operator>=( const Qualifiers &other );
	bool operator<( const Qualifiers &other );
	bool operator>( const Qualifiers &other );
	
	bool isConst;
	bool isVolatile;
	bool isRestrict;
	bool isLvalue;
    };	

    Type( const Qualifiers &tq );
    Type( const Type &other );
    virtual ~Type();

    Qualifiers &get_qualifiers() { return tq; }
    bool get_isConst() { return tq.isConst; }
    bool get_isVolatile() { return tq.isVolatile; }
    bool get_isRestrict() { return tq.isRestrict; }
    bool get_isLvalue() { return tq.isLvalue; }
    void set_isConst( bool newValue ) { tq.isConst = newValue; }
    void set_iisVolatile( bool newValue ) { tq.isVolatile = newValue; }
    void set_isRestrict( bool newValue ) { tq.isRestrict = newValue; }
    void set_isLvalue( bool newValue ) { tq.isLvalue = newValue; }
    std::list<TypeDecl*>& get_forall() { return forall; }

    virtual Type *clone() const = 0;
    virtual void accept( Visitor &v ) = 0;
    virtual Type *acceptMutator( Mutator &m ) = 0;
    virtual void print( std::ostream &os, int indent = 0 ) const;
  private:
    Qualifiers tq;
    std::list<TypeDecl*> forall;
};

class VoidType : public Type {
  public:
    VoidType( const Type::Qualifiers &tq );

    virtual VoidType *clone() const { return new VoidType( *this ); }
    virtual void accept( Visitor &v ) { v.visit( this ); }
    virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
    virtual void print( std::ostream &os, int indent = 0 ) const;
};

class BasicType : public Type {
  public:
    enum Kind {  
	Bool,
	Char,
	SignedChar,
	UnsignedChar,
	ShortSignedInt,
	ShortUnsignedInt,
	SignedInt,
	UnsignedInt,
	LongSignedInt,
	LongUnsignedInt,
	LongLongSignedInt,
	LongLongUnsignedInt,
	Float,
	Double,
	LongDouble,
	FloatComplex,
	DoubleComplex,
	LongDoubleComplex,
	FloatImaginary,
	DoubleImaginary,
	LongDoubleImaginary,
	NUMBER_OF_BASIC_TYPES
    };  

    BasicType( const Type::Qualifiers &tq, Kind bt );

    Kind get_kind() { return kind; }
    void set_kind( Kind newValue ) { kind = newValue; }

    virtual BasicType *clone() const { return new BasicType( *this ); }
    virtual void accept( Visitor &v ) { v.visit( this ); }
    virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
    virtual void print( std::ostream &os, int indent = 0 ) const;

    bool isInteger() const;
  private:
    Kind kind;
};

class PointerType : public Type {
  public:
    PointerType( const Type::Qualifiers &tq, Type *base );
    PointerType( const Type::Qualifiers &tq, Type *base, Expression *dimension, bool isVarLen, bool isStatic );
    PointerType( const PointerType& );
    virtual ~PointerType();

    Type *get_base() { return base; }
    void set_base( Type *newValue ) { base = newValue; }
    Expression *get_dimension() { return dimension; }
    void set_dimension( Expression *newValue ) { dimension = newValue; }
    bool get_isVarLen() { return isVarLen; }
    void set_isVarLen( bool newValue ) { isVarLen = newValue; }
    bool get_isStatic() { return isStatic; }
    void set_isStatic( bool newValue ) { isStatic = newValue; }

    virtual PointerType *clone() const { return new PointerType( *this ); }
    virtual void accept( Visitor &v ) { v.visit( this ); }
    virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
    virtual void print( std::ostream &os, int indent = 0 ) const;
  private:
    Type *base;
    
    // In C99, pointer types can be qualified in many ways e.g., int f( int a[ static 3 ] )
    Expression *dimension;
    bool isVarLen;
    bool isStatic;
};

class ArrayType : public Type {
  public:
    ArrayType( const Type::Qualifiers &tq, Type *base, Expression *dimension, bool isVarLen, bool isStatic );
    ArrayType( const ArrayType& );
    virtual ~ArrayType();

    Type *get_base() { return base; }
    void set_base( Type *newValue ) { base = newValue; }
    Expression *get_dimension() { return dimension; }
    void set_dimension( Expression *newValue ) { dimension = newValue; }
    bool get_isVarLen() { return isVarLen; }
    void set_isVarLen( bool newValue ) { isVarLen = newValue; }
    bool get_isStatic() { return isStatic; }
    void set_isStatic( bool newValue ) { isStatic = newValue; }

    virtual ArrayType *clone() const { return new ArrayType( *this ); }
    virtual void accept( Visitor &v ) { v.visit( this ); }
    virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
    virtual void print( std::ostream &os, int indent = 0 ) const;
  private:
    Type *base;
    Expression *dimension;
    bool isVarLen;
    bool isStatic;
};

class FunctionType : public Type {
  public:
    FunctionType( const Type::Qualifiers &tq, bool isVarArgs );
    FunctionType( const FunctionType& );
    virtual ~FunctionType();

    std::list<DeclarationWithType*>& get_returnVals() { return returnVals; }
    std::list<DeclarationWithType*>& get_parameters() { return parameters; }
    bool get_isVarArgs() { return isVarArgs; }
    void set_isVarArgs( bool newValue ) { isVarArgs = newValue; }

    virtual FunctionType *clone() const { return new FunctionType( *this ); }
    virtual void accept( Visitor &v ) { v.visit( this ); }
    virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
    virtual void print( std::ostream &os, int indent = 0 ) const;
  private:
    std::list<DeclarationWithType*> returnVals;
    std::list<DeclarationWithType*> parameters;

    // does the function accept a variable number of arguments following the arguments
    // specified in the parameters list.    This could be because of
    // - an ellipsis in a prototype declaration
    // - an unprototyped declaration
    bool isVarArgs;
};

class ReferenceToType : public Type {
  public:
    ReferenceToType( const Type::Qualifiers &tq, const std::string &name );
    ReferenceToType( const ReferenceToType &other );
    virtual ~ReferenceToType();

    std::string get_name() const { return name; }
    void set_name( std::string newValue ) { name = newValue; }
    std::list< Expression* >& get_parameters() { return parameters; }
    
    virtual ReferenceToType *clone() const = 0;
    virtual void accept( Visitor &v ) = 0;
    virtual Type *acceptMutator( Mutator &m ) = 0;
    virtual void print( std::ostream &os, int indent = 0 ) const;
  protected:
    virtual std::string typeString() const = 0;
    std::list< Expression* > parameters;
  private:
    std::string name;
};

class StructInstType : public ReferenceToType {
    typedef ReferenceToType Parent;
  public:
    StructInstType( const Type::Qualifiers &tq, const std::string &name ) : Parent( tq, name ), baseStruct( 0 ) {}
    StructInstType( const StructInstType &other ) : Parent( other ), baseStruct( other.baseStruct ) {}

    StructDecl *get_baseStruct() const { return baseStruct; }
    void set_baseStruct( StructDecl *newValue ) { baseStruct = newValue; }
    
    // a utility function
    void lookup( const std::string &name, std::list< Declaration* > &foundDecls ) const;

    virtual StructInstType *clone() const { return new StructInstType( *this ); }
    virtual void accept( Visitor &v ) { v.visit( this ); }
    virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }

  private:
    virtual std::string typeString() const;
    
    // this decl is not "owned" by the struct inst; it is merely a pointer to elsewhere in the tree,
    // where the structure used in this type is actually defined
    StructDecl *baseStruct;
};

class UnionInstType : public ReferenceToType {
    typedef ReferenceToType Parent;
  public:
    UnionInstType( const Type::Qualifiers &tq, const std::string &name ) : Parent( tq, name ), baseUnion( 0 ) {}
    UnionInstType( const UnionInstType &other ) : Parent( other ), baseUnion( other.baseUnion ) {}

    UnionDecl *get_baseUnion() const { return baseUnion; }
    void set_baseUnion( UnionDecl *newValue ) { baseUnion = newValue; }
    
    // a utility function
    void lookup( const std::string &name, std::list< Declaration* > &foundDecls ) const;

    virtual UnionInstType *clone() const { return new UnionInstType( *this ); }
    virtual void accept( Visitor &v ) { v.visit( this ); }
    virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
  private:
    virtual std::string typeString() const;
    
    // this decl is not "owned" by the union inst; it is merely a pointer to elsewhere in the tree,
    // where the union used in this type is actually defined
    UnionDecl *baseUnion;
};

class EnumInstType : public ReferenceToType {
    typedef ReferenceToType Parent;
  public:
    EnumInstType( const Type::Qualifiers &tq, const std::string &name ) : Parent( tq, name ) {}
    EnumInstType( const EnumInstType &other ) : Parent( other ) {}

    virtual EnumInstType *clone() const { return new EnumInstType( *this ); }
    virtual void accept( Visitor &v ) { v.visit( this ); }
    virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }

  private:
    virtual std::string typeString() const;
};

class ContextInstType : public ReferenceToType {
    typedef ReferenceToType Parent;
  public:
    ContextInstType( const Type::Qualifiers &tq, const std::string &name ) : Parent( tq, name ) {}
    ContextInstType( const ContextInstType &other );
    ~ContextInstType();

    std::list< Declaration* >& get_members() { return members; }

    virtual ContextInstType *clone() const { return new ContextInstType( *this ); }
    virtual void accept( Visitor &v ) { v.visit( this ); }
    virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
  private:
    virtual std::string typeString() const;
    
    // this member is filled in by the validate pass, which instantiates the members of the correponding
    // aggregate with the actual type parameters specified for this use of the context
    std::list< Declaration* > members;
};

class TypeInstType : public ReferenceToType {
    typedef ReferenceToType Parent;
  public:
    TypeInstType( const Type::Qualifiers &tq, const std::string &name, TypeDecl *baseType );
    TypeInstType( const Type::Qualifiers &tq, const std::string &name, bool isFtype );
    TypeInstType( const TypeInstType &other ) : Parent( other ), baseType( other.baseType ), isFtype( other.isFtype ) {}

    TypeDecl *get_baseType() const { return baseType; }
    void set_baseType( TypeDecl *newValue );
    bool get_isFtype() const { return isFtype; }
    void set_isFtype( bool newValue ) { isFtype = newValue; }
    
    virtual TypeInstType *clone() const { return new TypeInstType( *this ); }
    virtual void accept( Visitor &v ) { v.visit( this ); }
    virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
    virtual void print( std::ostream &os, int indent = 0 ) const;
  private:
    virtual std::string typeString() const;
    // this decl is not "owned" by the type inst; it is merely a pointer to elsewhere in the tree,
    // where the type used here is actually defined
    TypeDecl *baseType;
    bool isFtype;
};

class TupleType : public Type {
  public:
    TupleType( const Type::Qualifiers &tq );
    TupleType( const TupleType& );
    virtual ~TupleType();

    std::list<Type*>& get_types() { return types; }

    virtual TupleType *clone() const { return new TupleType( *this ); }
    virtual void accept( Visitor &v ) { v.visit( this ); }
    virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
    virtual void print( std::ostream &os, int indent = 0 ) const;
  private:
    std::list<Type*> types;
};

class TypeofType : public Type {
  public:
    TypeofType( const Type::Qualifiers &tq, Expression *expr );
    TypeofType( const TypeofType& );
    virtual ~TypeofType();

    Expression *get_expr() const { return expr; }
    void set_expr( Expression *newValue ) { expr = newValue; }

    virtual TypeofType *clone() const { return new TypeofType( *this ); }
    virtual void accept( Visitor &v ) { v.visit( this ); }
    virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
    virtual void print( std::ostream &os, int indent = 0 ) const;
  private:
    Expression *expr;
};

class AttrType : public Type {
  public:
    AttrType( const Type::Qualifiers &tq, const std::string &name, Expression *expr );
    AttrType( const Type::Qualifiers &tq, const std::string &name, Type *type );
    AttrType( const AttrType& );
    virtual ~AttrType();

    std::string get_name() const { return name; }
    void set_name( const std::string &newValue ) { name = newValue; }
    Expression *get_expr() const { return expr; }
    void set_expr( Expression *newValue ) { expr = newValue; }
    Type *get_type() const { return type; }
    void set_type( Type *newValue ) { type = newValue; }
    bool get_isType() const { return isType; }
    void set_isType( bool newValue ) { isType = newValue; }

    virtual AttrType *clone() const { return new AttrType( *this ); }
    virtual void accept( Visitor &v ) { v.visit( this ); }
    virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
    virtual void print( std::ostream &os, int indent = 0 ) const;

  private:
    std::string name;
    Expression *expr;
    Type *type;
    bool isType;
};

inline Type::Qualifiers &Type::Qualifiers::operator+=( const Type::Qualifiers &other ) {
    isConst |= other.isConst;
    isVolatile |= other.isVolatile;
    isRestrict |= other.isRestrict;
    isLvalue |= other.isLvalue;
    return *this;
}

inline Type::Qualifiers &Type::Qualifiers::operator-=( const Type::Qualifiers &other ) {
    if ( other.isConst ) isConst = 0;
    if ( other.isVolatile ) isVolatile = 0;
    if ( other.isRestrict ) isRestrict = 0;
    return *this;
}

inline Type::Qualifiers Type::Qualifiers::operator+( const Type::Qualifiers &other ) {
    Qualifiers q = other;
    q += *this;
    return q;
}

inline bool Type::Qualifiers::operator==( const Qualifiers &other ) {
    return isConst == other.isConst
    && isVolatile == other.isVolatile
    && isRestrict == other.isRestrict;
///	    && isLvalue == other.isLvalue;
}

inline bool Type::Qualifiers::operator!=( const Qualifiers &other ) {
    return isConst != other.isConst
	|| isVolatile != other.isVolatile
	|| isRestrict != other.isRestrict;
///	    && isLvalue == other.isLvalue;
}

inline bool Type::Qualifiers::operator<=( const Type::Qualifiers &other ) {
    return isConst <= other.isConst
	&& isVolatile <= other.isVolatile
	&& isRestrict <= other.isRestrict;
///	    && isLvalue >= other.isLvalue;
}

inline bool Type::Qualifiers::operator>=( const Type::Qualifiers &other ) {
    return isConst >= other.isConst
	&& isVolatile >= other.isVolatile
	&& isRestrict >= other.isRestrict;
///	    && isLvalue <= other.isLvalue;
}

inline bool Type::Qualifiers::operator<( const Type::Qualifiers &other ) {
    return operator!=( other ) && operator<=( other );
}

inline bool Type::Qualifiers::operator>( const Type::Qualifiers &other ) {
    return operator!=( other ) && operator>=( other );
}

#endif // TYPE_H
