/************************************************
  Team: Jon Gregory, Gor Nishanov
  Project #2 Part 2
    for RPI course 66-648 Compiler Design Theory

  type.h:

     TypeList class

     Type class has protected constructor.

     To create new types use:

       Type *Type::NewArray;
       Type *Type::NewClass;
       Type *Type::NewMethod;

     or predeclared types

      Type::Char, ... Type::Void

  *****/

class Type;

struct TypeListItem : public QuickNew {
	Type* type;
	TypeListItem* nextInOwner;
	TypeListItem(Type* typ):type(typ),nextInOwner(0){}
};

struct TypeList {
	typedef TypeListItem T;
	T* first;
	T* last;
	int count;
public:
	TypeList():first(0),last(0),count(0){}
	TypeList(T* i):first(i),last(i),count(1){}
	int getCount() const {return count;}
	void add(Type* t) { TypeListItem* i = new TypeListItem(t); ++count;
		if(first == 0) last = first = i;
		else last = (last->nextInOwner = i);
	}
	void clear() { first = last = 0; count = 0; }
	void print(ostream& o) const;
	friend ostream& operator << (ostream& o, const TypeList& t){
	  t.print(o); return o; }
	friend ostream& operator << (ostream& o, const TypeList* t){
          t->print(o); return o;}
};

class Type : QuickNew {
protected:
  int typeCode;
  static Hash<Identifier, Type*, TYPE_HASH_SIZE> hash;
  static TypeList empty_type_list;

Type(const int typCode, const char* typSig):typeCode(typCode),typeSig(typSig) {
//  cout << "TYPE=" << typeSig << endl;
  hash.get(typeSig).data = this;
}
public:
  Identifier typeSig;

  friend ostream& operator << (ostream& o, Type* t) {
    return o << t->getTypeSig() ; }

  static Type *Error, *Null, *Void, *Bool, *Byte, *Char, *Short, *Int,
              *Float, *Long, *Double, *Object, *String, *Cloneable;

  const char* getTypeSig() const {return typeSig.toString();}
  int getTypeMask() const {return 1 << typeCode;}
	bool isType(int typCode) const {return typeCode == typCode;}
	bool inMask(int typeMask) const {	return ((1 << typeCode) & typeMask) != 0;	}
	char* copySignatureTo(char* a) const {return typeSig.copyTo(a);}

	static Type* NewArray(Type* elem);
	static Type* NewClass(Identifier className);
	static Type* NewMethod(Type* ret);
	static Type* NewMethod(Type* returnType, TypeList* argTypes);

	virtual Type* getElementType() const {	assert(false /* not an Array */);
		return Error; }
	virtual int getArrayDimension() const {	return 0;	}
	virtual Identifier getClassName() const { assert(false /* not a Class */);
		return Identifier::star; }
	virtual Type* getReturnType() const {	assert(false /* not a method */);
		return Error; }
	virtual TypeList* getArgumentTypes() const {assert(false /* not a method */);
		return &empty_type_list; }
	virtual bool equalArguments(Type*) const {return false;}

	friend ostream& operator << (ostream& o, const Type* t) {
		return o << t->getTypeSig(); }

	virtual int stackSize() const {
		switch (typeCode) {
			case TC_ERROR: case TC_VOID: return 0;
			case TC_BOOL: case TC_BYTE:case TC_SHORT:case TC_CHAR:
			case TC_INT:	case TC_FLOAT:case TC_ARRAY:case TC_CLASS:	return 1;
			case TC_LONG:	case TC_DOUBLE:	return 2;
		}
		cout << typeCode << endl;
		assert(false /*Bad Type Code */);
		return 0;
	}

	int getTypeCodeOffset() const {
		switch (typeCode) {
			case TC_BOOL:case TC_BYTE:case TC_SHORT:case TC_CHAR:	case TC_INT:return 0;
			case TC_LONG:return 1;
			case TC_FLOAT:return 2;
			case TC_DOUBLE:	return 3;
			case TC_NULL:	case TC_ARRAY:case TC_CLASS:return 4;
		}
		cout << typeCode << endl;
		assert(false /*Bad Type Code */);
		return 0;
	}
};

void TypeList::print(ostream& o) const {
  T* f = first;
  if( f == 0 ) o << "<nothing>";
  else while(f != 0) {
    if(f != first) o << ' ';
    o << f->type;
    f = f->nextInOwner;
  }
};


Hash<Identifier, Type*, TYPE_HASH_SIZE> Type::hash;
TypeList Type::empty_type_list;


Type * Type::Error 	   = new Type(TC_ERROR, 	"?");
Type * Type::Null 	   = new Type(TC_NULL, 	  "*");
Type * Type::Void 	   = new Type(TC_VOID, 	 SIG_VOID_S);
Type * Type::Bool    	 = new Type(TC_BOOL, 	 SIG_BOOL_S);
Type * Type::Byte 	   = new Type(TC_BYTE, 	 SIG_BYTE_S);
Type * Type::Char 	   = new Type(TC_CHAR, 	 SIG_CHAR_S);
Type * Type::Short 	   = new Type(TC_SHORT,	 SIG_SHORT_S);
Type * Type::Int 	     = new Type(TC_INT, 	 SIG_INT_S);
Type * Type::Float 	   = new Type(TC_FLOAT,	 SIG_FLOAT_S);
Type * Type::Long 	   = new Type(TC_LONG, 	 SIG_LONG_S);
Type * Type::Double 	 = new Type(TC_DOUBLE, SIG_DOUBLE_S);
Type * Type::Object 	 = Type::NewClass(Identifier::JavaLangObject);
Type * Type::String 	 = Type::NewClass(Identifier::JavaLangString);
Type * Type::Cloneable = Type::NewClass(Identifier::JavaLangCloneable);
////////////////

class ArrayType : public Type {
	Type * elemType;
	friend Type;
	ArrayType(char* typSig, Type * elType):
		Type(TC_ARRAY,typSig),elemType(elType) {}
public:
	virtual Type * getElementType() const { return elemType; }
	virtual int getArrayDimension() const {	return elemType->getArrayDimension() + 1; }
};

class ClassType : public Type {
	Identifier className; // !! Should be fully qualified
friend Type;
	ClassType(char* typSig, Identifier clazzName):
		Type(TC_CLASS, typSig),className(clazzName){}
public:
	virtual	Identifier getClassName() const {return className;}
};

class MethodType : public Type {
	Type * returnType;
	TypeList * argTypes;
friend Type;
	MethodType(char* typSig, Type* retType, TypeList* argTyps):
		Type(TC_METHOD, typSig), returnType(retType), argTypes(argTyps) {}
public:
	virtual Type * getReturnType() const {return returnType;}
	virtual TypeList * getArgumentTypes() const {	return argTypes; }
	virtual bool equalArguments(Type* t) const {
		if (!t->isType(TC_METHOD)) return false;
		MethodType* m = (MethodType*)t;
		if (argTypes->getCount() != m->argTypes->getCount()) return false;
		TypeListItem * i = argTypes->first, *j = m->argTypes->first;
		while(i != 0) {
			if(i->type != j->type) return false;
			i = i->nextInOwner; j = j->nextInOwner;
		}
		return true;
	}
	virtual int stackSize() const {
		int n = 0;
		TypeListItem* i = argTypes->first;
		while(i != 0) {
			n += i->type->stackSize();
			i = i->nextInOwner;
		}
		return n;
	}
};

////////////////
Type* Type::NewArray(Type* elem) {
	char sig[255]; sig[0] = SIG_ARRAY; elem->typeSig.copyTo(sig+1);
	Type** t = &(hash.get(Identifier(sig)).data);
	if(*t == 0) *t = new ArrayType(sig, elem);
	return *t;
}
Type * Type::NewClass(Identifier className) { // !!! className fully Q
  char sig[255]; sig[0] = SIG_CLASSBEG;
  char * end = className.copyTo(sig+1);	end[0] = SIG_CLASSEND; end[1] = 0;
//  cout << "SIG=" << sig << endl;;
  Type** t = &(hash.get(Identifier(sig)).data);
  if(*t == 0) *t = new ClassType(sig, className);
  return *t;
}
Type* Type::NewMethod(Type* ret) {
	return NewMethod(ret, &empty_type_list);
}
Type* Type::NewMethod(Type* returnType, TypeList* argTypes) {
		char sig[1024]; // !!! Later we should use get_tmp or sth
		char * ptr = sig+1; sig[0] = SIG_PARBEG;
		TypeListItem * i = argTypes->first;
		while(i != NULL) {
			ptr = i->type->copySignatureTo(ptr);
			i = i -> nextInOwner;
		}
  *ptr = SIG_PAREND; ptr = returnType->copySignatureTo(++ptr); *ptr = 0;
  Type** t = &(hash.get(Identifier(sig)).data);
  if(*t == 0) *t = new MethodType(sig, returnType, argTypes);
  return *t;
}

