
#include <string.h>
#include <math.h>
#include "glut.h"
#include "glui.h"
#include "ermodel.h"

ERModel E1;

Entity::Entity() : numFromRelationships(0), numToRelationships(0), num_int(0), num_float(0), num_ptr(0),
					xsize(0.10), ysize(0.10) { }

void Entity::addAttribute(char *str, int t) {

	if (t==0) { strcpy(intAttrNames[num_int],str); num_int++;
	} else if (t==1) { strcpy(floatAttrNames[num_float],str); num_float++;
	} else if (t==2) { strcpy(ptrAttrNames[num_ptr],str); num_ptr++;
	}
}

void Entity::addFromRelationship(Relationship *r) {
	fromRelationships[numFromRelationships] = r;
	numFromRelationships++;
}

void Entity::addToRelationship(Relationship *r) {
	toRelationships[numToRelationships] = r;
	numToRelationships++;
}


void Entity::Draw() {

	int i, j;
	float yoff;

	glColor3f(0.8,0.8,0.8);
	glBegin(GL_QUADS);
	glVertex3f(x, y, 0.0);
	glVertex3f(x, y+ysize, 0.0);
	glVertex3f(x+xsize, y+ysize, 0.0);
	glVertex3f(x+xsize, y, 0.0);
	glEnd();

	glColor3f(0.2,0.2,0.2);
	for (i=0;i<strlen(name);i++) {
		glRasterPos3f(0.01*(float)i+x,y+ysize,0.1f);
		glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,name[i]);	
	}

	yoff = 0.02;
	for (i=0;i<num_int;i++) {
		for (j=0;j<strlen(intAttrNames[i]);j++) {
			glRasterPos3f(0.01*(float)j+x,y+ysize-yoff,0.1f);
			glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,intAttrNames[i][j]);	
		}
		yoff += 0.02;
	}

	for (i=0;i<num_float;i++) {
		for (j=0;j<strlen(floatAttrNames[i]);j++) {
			glRasterPos3f(0.01*(float)j+x,y+ysize-yoff,0.1f);
			glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,floatAttrNames[i][j]);	
		}
		yoff += 0.02;
	}

	for (i=0;i<num_ptr;i++) {
		for (j=0;j<strlen(ptrAttrNames[i]);j++) {
			glRasterPos3f(0.01*(float)j+x,y+ysize-yoff,0.1f);
			glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,ptrAttrNames[i][j]);	
		}
		yoff += 0.02;
	}

}

void Entity::setCoords(float a, float b) { x = a; y = b; }


void Entity::Save(FILE *fptr) {
	int i, temp;
	fwrite(name,sizeof(char),MAX_STRLEN,fptr);
	fwrite(&num_int,sizeof(int),1,fptr);
	fwrite(&num_float,sizeof(int),1,fptr);
	fwrite(&num_ptr,sizeof(int),1,fptr);
	for (i=0;i<num_int;i++) fwrite(intAttrNames[i],sizeof(char),MAX_STRLEN,fptr);
	for (i=0;i<num_float;i++) fwrite(floatAttrNames[i],sizeof(char),MAX_STRLEN,fptr);
	for (i=0;i<num_ptr;i++) fwrite(ptrAttrNames[i],sizeof(char),MAX_STRLEN,fptr);
	fwrite(&numFromRelationships,sizeof(int),1,fptr);
	for (i=0;i<numFromRelationships;i++) {
		temp = fromRelationships[i]->id;
		fwrite(&temp,sizeof(int),1,fptr);
	}
	fwrite(&numToRelationships,sizeof(int),1,fptr);
	for (i=0;i<numToRelationships;i++) {
		temp = toRelationships[i]->id;
		fwrite(&temp,sizeof(int),1,fptr);
	}
	fwrite(&x,sizeof(float),1,fptr);
	fwrite(&y,sizeof(float),1,fptr);
	fwrite(&xsize,sizeof(float),1,fptr);
	fwrite(&ysize,sizeof(float),1,fptr);
}


void Entity::Load(FILE *fptr) {
	int i, temp;
	fread(name,sizeof(char),MAX_STRLEN,fptr);
	fread(&num_int,sizeof(int),1,fptr);
	fread(&num_float,sizeof(int),1,fptr);
	fread(&num_ptr,sizeof(int),1,fptr);
	for (i=0;i<num_int;i++) fread(intAttrNames[i],sizeof(char),MAX_STRLEN,fptr);
	for (i=0;i<num_float;i++) fread(floatAttrNames[i],sizeof(char),MAX_STRLEN,fptr);
	for (i=0;i<num_ptr;i++) fread(ptrAttrNames[i],sizeof(char),MAX_STRLEN,fptr);
	fread(&numFromRelationships,sizeof(int),1,fptr);
	for (i=0;i<numFromRelationships;i++) {
		fread(&temp,sizeof(int),1,fptr);
		fromRelationships[i] = &(E1.relationships[temp]);
	}
	fread(&numToRelationships,sizeof(int),1,fptr);
	for (i=0;i<numToRelationships;i++) {
		fread(&temp,sizeof(int),1,fptr);
		toRelationships[i] = &(E1.relationships[temp]);
	}
	fread(&x,sizeof(float),1,fptr);
	fread(&y,sizeof(float),1,fptr);
	fread(&xsize,sizeof(float),1,fptr);
	fread(&ysize,sizeof(float),1,fptr);
}


void Entity::Print(FILE *fptr) {
	char typestr[16];
	int i;
	fprintf(fptr,"Entity: %s\n", name);
	fprintf(fptr,"\n");
}

Relationship::Relationship() : numAttributes(0) { }

void Relationship::addAttribute(char *str, int t) {
	strcpy(attributeNames[numAttributes],str);
	attributeTypes[numAttributes] = t;
	numAttributes++;
}

void Relationship::Draw() {

	float x1, y1, x2, y2;
	int i, j;
	float yoff;
	float vecx, vecy, vecmag;
	float perpx, perpy, perpmag;

	x1 = entity0->x;
	y1 = entity0->y + entity0->ysize;
	x2 = entity1->x;
	y2 = entity1->y + entity1->ysize;

	glColor3f(0.2,0.2,0.2);
	glLineWidth(2);

	// draw the line
	glBegin(GL_LINES);
	glVertex3f(x1, y1, 0.0);
	glVertex3f(x2, y2, 0.0);
	glEnd();
	
	// draw the arrow
	vecx = x2-x1;
	vecy = y2-y1;
	perpx = -vecy;
	perpy = vecx;
	vecmag = sqrt(vecx*vecx+vecy*vecy);
	if (vecmag>0.1) {
		perpx = -vecy;
		perpy = vecx;
		perpmag = sqrt(perpx*perpx+perpy*perpy);
		glBegin(GL_LINES);
		glVertex3f(x1+0.5*vecx+perpx*(0.01/perpmag),y1+0.5*vecy+perpy*(0.01/perpmag),0.0);
		glVertex3f(x1+(0.5+0.01/vecmag)*vecx,y1+(0.5+0.01/vecmag)*vecy,0.0);
		glVertex3f(x1+0.5*vecx-perpx*(0.01/perpmag),y1+0.5*vecy-perpy*(0.01/perpmag),0.0);
		glVertex3f(x1+(0.5+0.01/vecmag)*vecx,y1+(0.5+0.01/vecmag)*vecy,0.0);
		glEnd();
	}

	// trick, to force name to right, cardinality to left
	if (perpx<0.0) perpmag = -perpmag;

	// label name and attributes of the relationship
	glColor3f(0.2,0.2,0.2);
	for (i=0;i<strlen(name);i++) {
		glRasterPos3f(0.01*(float)i+x1+0.5*vecx+perpx*(0.01/perpmag),y1+0.5*vecy+perpy*(0.01/perpmag),0.1f);
		glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,name[i]);	
	}
	yoff = 0.02;
	for (i=0;i<numAttributes;i++) {
		for (j=0;j<strlen(attributeNames[i]);j++) {
			//glRasterPos3f(0.01*(float)j+midx,midy-yoff,0.1f);
			glRasterPos3f(0.01*(float)j+x1+0.5*vecx+perpx*(0.01/perpmag),y1+0.5*vecy+perpy*(0.01/perpmag)+yoff,0.1f);
			glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,attributeNames[i][j]);	
		}
		yoff += 0.02;
	}

	// label cardinality of the relationship
	glRasterPos3f(x1+(0.5-0.02/vecmag)*vecx-perpx*(0.01/perpmag),y1+(0.5-0.02/vecmag)*vecy-perpy*(0.01/perpmag),0.0);
	if ((type==0)||(type==1)) {
		glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,'1');
	} else {
		glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,'m');
	}
	glRasterPos3f(x1+(0.5+0.02/vecmag)*vecx-perpx*(0.01/perpmag),y1+(0.5+0.02/vecmag)*vecy-perpy*(0.01/perpmag),0.0);
	if ((type==0)||(type==2)) {
		glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,'1');
	} else {
		glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,'m');
	}

}

void Relationship::Save(FILE *fptr) {
	int i, temp;
	fwrite(name,sizeof(char),MAX_STRLEN,fptr);
	fwrite(&type,sizeof(int),1,fptr);
	fwrite(&numAttributes,sizeof(int),1,fptr);
	for (i=0;i<numAttributes;i++) fwrite(attributeNames[i],sizeof(char),MAX_STRLEN,fptr);
	for (i=0;i<numAttributes;i++) fwrite(&(attributeTypes[i]),sizeof(int),1,fptr);
	temp = entity0->id;
	fwrite(&temp,sizeof(int),1,fptr);
	temp = entity1->id;
	fwrite(&temp,sizeof(int),1,fptr);
}


void Relationship::Load(FILE *fptr) {
	int i, temp;
	fread(name,sizeof(char),MAX_STRLEN,fptr);
	fread(&type,sizeof(int),1,fptr);
	fread(&numAttributes,sizeof(int),1,fptr);
	for (i=0;i<numAttributes;i++) fread(attributeNames[i],sizeof(char),MAX_STRLEN,fptr);
	for (i=0;i<numAttributes;i++) fread(&(attributeTypes[i]),sizeof(int),1,fptr);
	fread(&temp,sizeof(int),1,fptr);
	entity0 = &(E1.entities[temp]);
	fread(&temp,sizeof(int),1,fptr);
	entity1 = &(E1.entities[temp]);
}

void Relationship::Print(FILE *fptr) {
	char typestr[16];
	int i;
	if (type==0) strcpy(typestr,"one-to-one");
	if (type==1) strcpy(typestr,"one-to-many");
	if (type==2) strcpy(typestr,"many-to-one");
	if (type==3) strcpy(typestr,"many-to-many");
	fprintf(fptr,"Relationship: %s\n  Cardinality: %s\n  From entity: %s\n  To entity: %s\n", name, typestr, entity0->name, entity1->name);
	for (i=0;i<numAttributes;i++) {
		if (attributeTypes[i]==0) strcpy(typestr,"int");
		if (attributeTypes[i]==1) strcpy(typestr,"string");
		if (attributeTypes[i]==2) strcpy(typestr,"float");
		fprintf(fptr,"  Attribute \"%s\" type: %s\n", attributeNames[i], typestr);
	}
	fprintf(fptr,"\n");
}

ERModel::ERModel() : numEntities(0), numRelationships(0) {
	int i;
	for (i=0;i<MAX_ENTITIES;i++) entities[i].id = i;
	for (i=0;i<MAX_RELATIONSHIPS;i++) relationships[i].id = i;
}

void ERModel::Init() {
	
	Entity *ent_oasc, *ent_as, *ent_ipprefix, *ent_host;
	Entity *ent_operator, *ent_aspath, *ent_bgpupdatemessage;
	Entity *ent_tcppacket, *ent_router;
	Relationship *rel_pathcontains;

	ent_oasc = addEntity("OASC");
	ent_oasc->addAttribute("type",0);
	ent_oasc->addAttribute("time",0);
	ent_oasc->setCoords(0.02,0.06);
	ent_as = addEntity("AS");
	ent_as->addAttribute("id",0);
	ent_as->setCoords(0.22,0.01);
	ent_ipprefix = addEntity("IPPrefix");
	ent_ipprefix->addAttribute("IPAddress",0);
	ent_ipprefix->addAttribute("mask",0);
	ent_ipprefix->setCoords(0.41,0.01);
	ent_host = addEntity("Host");
	ent_host->addAttribute("IPAddress",0);
	ent_host->setCoords(0.05,0.52);
	ent_operator = addEntity("Operator");
	ent_operator->addAttribute("name",1);
	ent_operator->setCoords(0.08,0.33);
	ent_aspath = addEntity("ASPath");
	ent_aspath->addAttribute("length",0);
	ent_aspath->setCoords(0.42,0.39);
	ent_bgpupdatemessage = addEntity("BGPUpdateMessage");
	ent_bgpupdatemessage->addAttribute("time",0);
	ent_bgpupdatemessage->setCoords(0.45,0.25);
	ent_tcppacket = addEntity("TCPpacket");
	ent_tcppacket->addAttribute("sequenceNum",0);
	ent_tcppacket->setCoords(0.10,0.62);
	ent_router = addEntity("Router");
	ent_router->addAttribute("IPAddress",0);
	ent_router->setCoords(0.65,0.18);
	addRelationship("owns",1,ent_operator,ent_as);
	addRelationship("owns",1,ent_operator,ent_ipprefix);
	addRelationship("owns",1,ent_operator,ent_host);
	addRelationship("fromAS",3,ent_oasc,ent_as);
	addRelationship("toAS",3,ent_oasc,ent_as);
	addRelationship("involves",2,ent_oasc,ent_ipprefix);
	addRelationship("involves",2,ent_bgpupdatemessage,ent_ipprefix);
	addRelationship("involves",2,ent_bgpupdatemessage,ent_aspath);
	rel_pathcontains = addRelationship("pathContains",3,ent_aspath,ent_as);
	rel_pathcontains->addAttribute("position",0);
	addRelationship("observes",1,ent_router,ent_oasc);
	addRelationship("observes",1,ent_router,ent_bgpupdatemessage);
	addRelationship("owns",1,ent_operator,ent_router);


	Entity *ent_tcpconnection;
	Entity *ent_port;
	Entity *ent_ipdatagram, *ent_ipfragment;

	ent_tcpconnection = addEntity("TCPConnection");
	ent_port = addEntity("Port");
	ent_port->addAttribute("number",0);
	ent_ipdatagram = addEntity("IPDatagram");
	ent_ipdatagram->addAttribute("flags",0);
	ent_ipfragment = addEntity("IPFragments");
	ent_ipfragment->addAttribute("flags",0);

	addRelationship("sourcePort",2,ent_tcpconnection,ent_port);
	addRelationship("destinationPort",2,ent_tcpconnection,ent_port);
	addRelationship("contains",1,ent_host,ent_port);
	addRelationship("fragmented",1,ent_ipdatagram,ent_ipfragment);
	addRelationship("sent by",0,ent_tcppacket,ent_ipdatagram);
	addRelationship("contains",1,ent_tcpconnection,ent_tcppacket);
	addRelationship("sourceHost",2,ent_ipdatagram,ent_host);
	addRelationship("destinationHost",2,ent_ipdatagram,ent_host);
	addRelationship("includes",3,ent_ipprefix,ent_host);



}

void ERModel::Extend() {


}


Entity *ERModel::addEntity(char *str) {
	strcpy(entities[numEntities].name,str);
	numEntities++;
	return (&(entities[numEntities-1]));
}

Relationship *ERModel::addRelationship(char *str, int t, Entity *e0, Entity *e1) {
	strcpy(relationships[numRelationships].name,str);
	relationships[numRelationships].type = t;
	relationships[numRelationships].entity0 = e0;
	relationships[numRelationships].entity1 = e1;
	numRelationships++;
	e0->addFromRelationship(&(relationships[numRelationships-1]));
	e1->addToRelationship(&(relationships[numRelationships-1]));
	return (&(relationships[numRelationships-1]));
}

void ERModel::Draw() {
	int i;
	for (i=0;i<numEntities;i++) entities[i].Draw();
	for (i=0;i<numRelationships;i++) relationships[i].Draw();	
}


void ERModel::SelectEntity(float x, float y) {
	int i;
	selectedEntity = -1;
	for (i=0;i<numEntities;i++) {
		if ((entities[i].x < x) && (entities[i].y < y) &&
			(entities[i].x + entities[i].xsize > x) &&
			(entities[i].y + entities[i].ysize > y)) {
			selectedEntity = i;
		}
	}

	printf("clicked %f %f\n", x, y);
}

void ERModel::MoveEntity(float x, float y) {
	if ((selectedEntity>=0) && (selectedEntity<numEntities)) {
		entities[selectedEntity].setCoords(x,y);
	}
}

void ERModel::Save() {
	FILE *fptr;
	int i;

	fptr = fopen("model.bin","wb");
	fwrite(&numEntities,sizeof(int),1,fptr);
	for (i=0;i<numEntities;i++) entities[i].Save(fptr);
	fwrite(&numRelationships,sizeof(int),1,fptr);
	for (i=0;i<numRelationships;i++) relationships[i].Save(fptr);
	fclose(fptr);


}

void ERModel::Load() {

	FILE *fptr;
	int i;

	fptr = fopen("model.bin","rb");
	fread(&numEntities,sizeof(int),1,fptr);
	for (i=0;i<numEntities;i++) entities[i].Load(fptr);
	fread(&numRelationships,sizeof(int),1,fptr);
	for (i=0;i<numRelationships;i++) relationships[i].Load(fptr);
	fclose(fptr);

}

void ERModel::Print() {
	FILE *fptr;
	int i;

	fptr = fopen("model.txt","wt");
	for (i=0;i<numEntities;i++) entities[i].Print(fptr);
	for (i=0;i<numRelationships;i++) relationships[i].Print(fptr);
	fclose(fptr);

}


