#include "typeops.h"
#include "SynTree/Type.h"
#include "TypeEnvironment.h"
#include "SymTab/Indexer.h"

namespace ResolvExpr {
    class AdjustExprType : public Mutator {
	typedef Mutator Parent;
      public:
	AdjustExprType( const TypeEnvironment &env, const SymTab::Indexer &indexer );
      private:
	virtual Type* mutate(VoidType *voidType);
	virtual Type* mutate(BasicType *basicType);
	virtual Type* mutate(PointerType *pointerType);
	virtual Type* mutate(ArrayType *arrayType);
	virtual Type* mutate(FunctionType *functionType);
	virtual Type* mutate(StructInstType *aggregateUseType);
	virtual Type* mutate(UnionInstType *aggregateUseType);
	virtual Type* mutate(EnumInstType *aggregateUseType);
	virtual Type* mutate(ContextInstType *aggregateUseType);
	virtual Type* mutate(TypeInstType *aggregateUseType);
	virtual Type* mutate(TupleType *tupleType);
  
	const TypeEnvironment &env;
	const SymTab::Indexer &indexer;
    };

    void adjustExprType( Type *&type, const TypeEnvironment &env, const SymTab::Indexer &indexer ) {
	AdjustExprType adjuster( env, indexer );
	Type *newType = type->acceptMutator( adjuster );
	type = newType;
    }

    AdjustExprType::AdjustExprType( const TypeEnvironment &env, const SymTab::Indexer &indexer )
	: env( env ), indexer( indexer ) {
    }

    Type *AdjustExprType::mutate(VoidType *voidType) {
	return voidType;
    }

    Type *AdjustExprType::mutate(BasicType *basicType) {
	return basicType;
    }

    Type *AdjustExprType::mutate(PointerType *pointerType) {
	return pointerType;
    }

    Type *AdjustExprType::mutate(ArrayType *arrayType) {
	PointerType *pointerType = new PointerType( arrayType->get_qualifiers(), arrayType->get_base()->clone() );
	delete arrayType;
	return pointerType;
    }

    Type *AdjustExprType::mutate(FunctionType *functionType) {
	PointerType *pointerType = new PointerType( Type::Qualifiers(), functionType );
	return pointerType;
    }

    Type *AdjustExprType::mutate(StructInstType *aggregateUseType) {
	return aggregateUseType;
    }

    Type *AdjustExprType::mutate(UnionInstType *aggregateUseType) {
	return aggregateUseType;
    }

    Type *AdjustExprType::mutate(EnumInstType *aggregateUseType) {
	return aggregateUseType;
    }

    Type *AdjustExprType::mutate(ContextInstType *aggregateUseType) {
	return aggregateUseType;
    }

    Type *AdjustExprType::mutate(TypeInstType *typeInst) {
	EqvClass eqvClass;
	if ( env.lookup( typeInst->get_name(), eqvClass ) ) {
	    if ( eqvClass.kind == TypeDecl::Ftype ) {
		PointerType *pointerType = new PointerType( Type::Qualifiers(), typeInst );
		return pointerType;
	    }
	} else if ( NamedTypeDecl *ntDecl = indexer.lookupType( typeInst->get_name() ) ) {
	    if ( TypeDecl *tyDecl = dynamic_cast< TypeDecl* >( ntDecl ) ) {
		if ( tyDecl->get_kind() == TypeDecl::Ftype ) {
		    PointerType *pointerType = new PointerType( Type::Qualifiers(), typeInst );
		    return pointerType;
		}
	    }
	}
	return typeInst;
    }

    Type *AdjustExprType::mutate(TupleType *tupleType) {
	return tupleType;
    }
} // namespace ResolvExpr
