
#include "floorplan.h"
using namespace std;



FloorPlan::House::House(std::string FileName)
{
    ifstream is(FileName.c_str());

    Level* level = NULL;
    float x,y;
    string command;
    string vert1, vert2;
    map<string, FloorPlan::Vertex*> vertices;

    for(is.peek(); !is.eof(); is.peek())
    {
        is >> command;

        if(command == "LEVEL")
        {
            is >> x;
            level = this->AddLevel(x);
        }
        else if(command == "VERTEX")
        {
            is >> vert1 >> x >> y;
            vertices[vert1] = level->AddVertex(vert1, x,y);
        }
        else if(command == "WALL")
        {
            is >> vert1 >> vert2 >> x;
            level->AddWall(vertices[vert1], vertices[vert2], x);
        }
        else if(command == "QUIT")
        {
            break;
        }
    }
}


FloorPlan::House::~House()
{
	for(vector<Level*>::iterator i = Levels.begin(); i != Levels.end(); i++)
		delete *i;
}

FloorPlan::Level* FloorPlan::House::AddLevel(float height)
{
	Level* level = new Level;
	level->height = height;
	Levels.push_back(level);
	return level;
}

Scene* FloorPlan::House::ToScene()
{
    Scene* result = new Scene();
	float LevelBottom = 0;
	int i = 1;

    for(vector<Level*>::iterator l = Levels.begin(); l != Levels.end(); l++)
	{
        result->meshes.push_back(  (*l)->ToMesh(LevelBottom, i++)  );
        LevelBottom += (*l)->height;
	}

	return result;
}




FloorPlan::Vertex* FloorPlan::Wall::Other(Vertex* v)
{
	return v == to ? from : to;
}




FloorPlan::Level::Level()
{
    height = 220;
}

FloorPlan::Level::~Level()
{
	for(vector<Wall*>::iterator i = Walls.begin(); i != Walls.end(); i++)
		delete *i;
	for(vector<Vertex*>::iterator i = Vertices.begin(); i != Vertices.end(); i++)
		delete *i;
}

FloorPlan::Vertex* FloorPlan::Level::AddVertex(string name, float x, float y)
{
	Vertex* vertex = new Vertex;
	vertex->x = x;
	vertex->y = y;
	vertex->name = name;
	Vertices.push_back(vertex);
	return vertex;
}

FloorPlan::Wall* FloorPlan::Level::AddWall(Vertex* from, Vertex* to, float width)
{
	Wall* wall = new Wall;
	wall->width = width;
	wall->from = from;
	wall->to = to;
	Walls.push_back(wall);
	return wall;
}



Vector2D FloorPlan::Level::GetIntersectionPoint(FloorPlan::Wall* wall, FloorPlan::Vertex* v, FloorPlan::Wall* PrevWall)
{
	Vertex* NextVertex = wall->Other(v);
	Vertex* PrevVertex = PrevWall->Other(v);
	Vector2D vPos(v->x, v->y);
	Vector2D NextVertexPos(NextVertex->x, NextVertex->y);
	Vector2D PrevVertexPos(PrevVertex->x, PrevVertex->y);

	Vector2D wallDirection = NextVertexPos - vPos;
	Vector2D wallOffset = wallDirection.Perpendicular().Normalized() * (wall->width/2.0);
	Vector2D PrevWallDirection = vPos - PrevVertexPos;
	Vector2D PrevWallOffset = PrevWallDirection.Perpendicular().Normalized() * (PrevWall->width/2.0);;

	Vector2D result = Vector2D::IntersectionParameterForm(
		PrevVertexPos + PrevWallOffset, PrevWallDirection,
		vPos + wallOffset, wallDirection
	);

	if(isnan(result.x) || isnan(result.y))
	{
		result = vPos + wallOffset;
	}

	return result;
}

Mesh* FloorPlan::Level::ToMesh(float VerticalOffset, int id)
{

	map<Vertex*, vector<Wall*> > IncidentWalls;
	map<Vertex*, map<Wall*, int> > WallIndex;
    map<Vertex*, vector<Point3D*> > MeshVerticesForVertex;
    for(vector<Vertex*>::iterator v = Vertices.begin(); v != Vertices.end(); v++)
	{
		// Collect incident walls
		vector<Wall*> vIncidentWalls;
        for(vector<Wall*>::iterator w = Walls.begin(); w != Walls.end(); w++)
            if((*w)->from == *v || (*w)->to == *v)
                vIncidentWalls.push_back(*w);

		// Sort incident walls by Theta value
		for(int i = 0; i < vIncidentWalls.size(); i++)
		{
            Vertex* iOther = vIncidentWalls[i]->Other(*v);
            Vector2D iVector(iOther->x - (*v)->x, iOther->y - (*v)->y);
			float iTheta = iVector.Theta();

			int minidx = i;
			float minTheta = iTheta;
			for(int j = i+1; j < vIncidentWalls.size(); j++)
			{
                Vertex* jOther = vIncidentWalls[j]->Other(*v);
                Vector2D jVector(jOther->x - (*v)->x, jOther->y - (*v)->y);
				float jTheta = jVector.Theta();
				if(jTheta < minTheta)
				{
					minTheta = jTheta;
					minidx = j;
				}
			}
			if(minidx != i)
			{
				Wall* temp = vIncidentWalls[minidx];
				vIncidentWalls[minidx] = vIncidentWalls[i];
				vIncidentWalls[i] = temp;
			}

            WallIndex[*v][vIncidentWalls[i]] = i;
		}
        IncidentWalls[*v] = vIncidentWalls;
        MeshVerticesForVertex[*v] = vector<Point3D*>(vIncidentWalls.size(), NULL);
	}



	// Create Rooms
	vector<vector<Wall*> > Rooms;
	map<Vertex*, vector<bool> > RoomCreated;
    for(vector<Vertex*>::iterator v = Vertices.begin(); v != Vertices.end(); v++)
        RoomCreated[*v] = vector<bool>( IncidentWalls[*v].size(), false );

    for(vector<Vertex*>::iterator v = Vertices.begin(); v != Vertices.end(); v++)
    {
        for(int r = 0; r < IncidentWalls[*v].size(); r++)
		{
            if(!RoomCreated[*v][r])
			{
				vector<Wall*> Room;

                Vertex* currentvertex = *v;
                Wall* currentwall = IncidentWalls[*v][(r+1) % IncidentWalls[*v].size()];
				Wall* firstwall = currentwall;

				Vertex* nextvertex = NULL;
				Wall* nextwall = NULL;

                while(nextvertex != *v || nextwall != firstwall)
				{
					Room.push_back(currentwall);

					nextvertex = currentwall->Other(currentvertex);
					nextwall = IncidentWalls[nextvertex][(WallIndex[nextvertex][currentwall] + 1) % IncidentWalls[nextvertex].size()];
					RoomCreated[nextvertex][WallIndex[nextvertex][currentwall]] = true;

					currentvertex = nextvertex;
					currentwall = nextwall;
				}

				Rooms.push_back(Room);
			}
		}
	}



    // create mesh
	stringstream s;
	s << "Level_" << id;
    Mesh* mesh = new Mesh(s.str());


    map<Vertex*, map<Wall*, Point3D*> > Tops;

    for(vector<vector<Wall*> >::iterator Room = Rooms.begin(); Room != Rooms.end(); Room++)
	{
        Vertex* v = (*(Room->begin()))->from;
        if(v != (*Room)[Room->size()-1]->from
        && v != (*Room)[Room->size()-1]->to)
             v = (*(Room->begin()))->to;

        Face* RoomFloor = new Face();
        mesh->faces.push_back(RoomFloor);

        Vector2D IntersectionPoint = GetIntersectionPoint(*Room->begin(), v, Room->back());
        Point3D* vBottom = new Point3D(IntersectionPoint.x, -VerticalOffset, IntersectionPoint.y);
        mesh->points.push_back(vBottom);
        Point3D* vTop = new Point3D(IntersectionPoint.x, -(VerticalOffset+height), IntersectionPoint.y);
        mesh->points.push_back(vTop);
        MeshVerticesForVertex[v][WallIndex[v][*Room->begin()]] = vTop;
        Point3D* FirstVertexBottom = vBottom;
        Point3D* FirstVertexTop = vTop;

        for(vector<Wall*>::iterator wall = Room->begin(); wall != Room->end(); wall++)
		{
			Vertex* NextVertex = (*wall)->Other(v);
			vector<Wall*>::iterator nwall = wall;
			nwall++;
            Wall* NextWall = nwall == Room->end() ? *(Room->begin()) : *nwall;

			// compute position of the verts in the 3d space
            Point3D* NextBottom = NULL;
            Point3D* NextTop = NULL;
            if(nwall != Room->end())
			{
				IntersectionPoint = GetIntersectionPoint(NextWall, NextVertex, *wall);
                NextBottom = new Point3D(IntersectionPoint.x, -VerticalOffset, IntersectionPoint.y);
                mesh->points.push_back(NextBottom);
                NextTop = new Point3D(IntersectionPoint.x, -(VerticalOffset+height), IntersectionPoint.y);
                mesh->points.push_back(NextTop);
                MeshVerticesForVertex[NextVertex][WallIndex[NextVertex][NextWall]] = NextTop;
			}
			else
			{
				NextBottom = FirstVertexBottom;
				NextTop = FirstVertexTop;
			}

			// create the face on this rooms' side of the wall
            Face* face = new Face();
            face->verts.push_back(vBottom);
            face->verts.push_back(vTop);
            face->verts.push_back(NextTop);
            face->verts.push_back(NextBottom);
            mesh->faces.push_back(face);

			// Add the vertex to the floor of the room
            RoomFloor->verts.push_back(vBottom);

			// Top cap of the wall
			if((Tops.find(v) != Tops.end()) && (Tops[v].find(*wall) != Tops[v].end()))
			{
                Face* topface = new Face();
                topface->verts.push_back(vTop);
                topface->verts.push_back(NextTop);
                topface->verts.push_back(Tops[NextVertex][*wall]);
                topface->verts.push_back(Tops[v][*wall]);
                mesh->faces.push_back(topface);
			}
			else
			{
				Tops[v][*wall] = vTop;
				Tops[NextVertex][*wall] = NextTop;
			}

			// prepare for next iteration
			v = NextVertex;
			vBottom = NextBottom;
			vTop = NextTop;
		}
	}

	// close the holes over vertices of degree >= 3
    for(vector<Vertex*>::iterator v = Vertices.begin(); v != Vertices.end(); v++)
    {
        if(MeshVerticesForVertex[*v].size() >= 3)
		{
            Face* vface = new Face();
            vface->verts = MeshVerticesForVertex[*v];
            mesh->faces.push_back(vface);
		}
	}

	return mesh;
}












