#include "typeops.h"
#include "SynTree/Type.h"
#include "SynTree/Declaration.h"
#include "SynTree/Visitor.h"


namespace ResolvExpr {
    class PtrsAssignable : public Visitor {
      public:
	PtrsAssignable( Type *dest, const TypeEnvironment &env );
  
	int get_result() const { return result; }

	virtual void visit(VoidType *voidType);
	virtual void visit(BasicType *basicType);
	virtual void visit(PointerType *pointerType);
	virtual void visit(ArrayType *arrayType);
	virtual void visit(FunctionType *functionType);
	virtual void visit(StructInstType *inst);
	virtual void visit(UnionInstType *inst);
	virtual void visit(EnumInstType *inst);
	virtual void visit(ContextInstType *inst);
	virtual void visit(TypeInstType *inst);
	virtual void visit(TupleType *tupleType);
      private:
	Type *dest;
	int result;
	const TypeEnvironment &env;
    };

    int ptrsAssignable( Type *src, Type *dest, const TypeEnvironment &env ) {
	if ( TypeInstType *destAsTypeInst = dynamic_cast< TypeInstType* >( dest ) ) {
	    EqvClass eqvClass;
	    if ( env.lookup( destAsTypeInst->get_name(), eqvClass ) ) {
		return ptrsAssignable( src, eqvClass.type, env );
	    }
	}
	if ( dynamic_cast< VoidType* >( dest ) ) {
	    return 1;
	} else {
	    PtrsAssignable ptrs( dest, env );
	    src->accept( ptrs );
	    return ptrs.get_result();
	}
    }

    PtrsAssignable::PtrsAssignable( Type *dest, const TypeEnvironment &env ) : dest( dest ), result( 0 ), env( env ) {
    }

    void PtrsAssignable::visit(VoidType *voidType) {
	if ( dynamic_cast< FunctionType* >( dest ) ) {
	    result = 0;
	} else {
	    result = -1;
	}
    }

    void PtrsAssignable::visit( BasicType *basicType ) {
    }

    void PtrsAssignable::visit( PointerType *pointerType ) {
    }

    void PtrsAssignable::visit( ArrayType *arrayType ) {
    }

    void PtrsAssignable::visit( FunctionType *functionType ) {
	result = -1;
    }

    void PtrsAssignable::visit( StructInstType *inst ) {
	// I don't think we should be doing anything here, but I'm willing to admit that I might be wrong
    }

    void PtrsAssignable::visit( UnionInstType *inst ) {
	// I don't think we should be doing anything here, but I'm willing to admit that I might be wrong
    }

    void PtrsAssignable::visit( EnumInstType *inst ) {
	if ( dynamic_cast< EnumInstType* >( inst ) ) {
	    result = 1;
	} else if ( BasicType *bt = dynamic_cast< BasicType* >( inst ) ) {
	    result = bt->get_kind() == BasicType::SignedInt;
	}
    }

    void PtrsAssignable::visit( ContextInstType *inst ) {
	// I definitely don't think we should be doing anything here
    }

    void PtrsAssignable::visit( TypeInstType *inst ) {
	EqvClass eqvClass;
	if ( env.lookup( inst->get_name(), eqvClass ) ) {
	    result = ptrsAssignable( eqvClass.type, dest, env );
	} else {
	    result = 0;
	}
    }

    void PtrsAssignable::visit( TupleType *tupleType ) {
///  // This code doesn't belong here, but it might be useful somewhere else
///   if ( TupleType *destAsTuple = dynamic_cast< TupleType* >( dest ) ) {
///     int ret = 0;
///     std::list< Type* >::const_iterator srcIt = tupleType->get_types().begin();
///     std::list< Type* >::const_iterator destIt = destAsTuple->get_types().begin();
///     while( srcIt != tupleType->get_types().end() && destIt != destAsTuple->get_types().end() ) {
///       int assignResult = ptrsAssignable( *srcIt++, *destIt++ );
///       if ( assignResult == 0 ) {
///         result = assignResult;
///         return;
///       } else if ( assignResult < 0 ) {
///         ret = -1;
///       } else if ( ret > 0 ) {
///         ret += assignResult;
///       }
///     }
///     if ( srcIt == tupleType->get_types().end() && destIt == destAsTuple->get_types().end() ) {
///       result = ret;
///     } else {
///       result = 0;
///     }
///   }
    }
} // namespace ResolvExpr
