#include <ai_scavenger.h>
#include <ai_glut.h>

// Windows headers have DrawText() macro defined somewhere.
#ifdef DrawText
#undef DrawText
#endif

namespace ai
{
  namespace Scavenger
  {
    static GLfloat plainMaterial[] = {0.1f, 0.6f, 0.1f, 1.0f};
    static GLfloat rocksMaterial[] = {0.1f, 0.4f, 0.4f, 1.0f};
    static GLfloat mudMaterial[]   = {0.4f, 0.2f, 0.1f, 1.0f};
    static GLfloat iceMaterial[]   = {0.9f, 0.9f, 0.9f, 1.0f};
    static GLfloat wallMaterial[]  = {0.1f, 0.1f, 0.1f, 1.0f};
    static GLfloat cliffMaterial[] = {0.5f, 0.5f, 0.1f, 1.0f};
    static std::map<std::string, GLfloat *> interface_material;
    static double interface_offset[][6] =
      {
	{-1,  1,  1,  1,  0, -1 },
	{-1, -1,  1, -1,  0,  1 },
	{ 1,  1,  1, -1, -1,  0 },
	{-1,  1, -1, -1,  1,  0 }
      };
    
    EnvironmentDisplay::EnvironmentDisplay(ai::Agent::Environment *env)
      : ai::Agent::EnvironmentDisplay(env)
    {
      interface_material["plain"] = plainMaterial;
      interface_material["rocks"] = rocksMaterial;
      interface_material["mud"]   = mudMaterial;
      interface_material["ice"]   = iceMaterial;
      interface_material["wall"]  = wallMaterial;
      interface_material["cliff"] = cliffMaterial;
      
      Environment *sw_env = dynamic_cast<Environment *>(env);
      std::map<Location, Cell *>::iterator it;
      
      minx = miny = 1e12;
      maxx = maxy = -minx;
      
      spanx = 0.;
      spany = 0.;
      
      for(it = sw_env->cells.begin(); it != sw_env->cells.end(); it++)
        {
	  if(it->second == 0)
	    {
	      std::cerr << "bad pointer" << std::endl;
	      continue;
	    }
	  Location location = it->second->GetLocation();
	  Cell     *cell_x  = it->second->GetNeighbor(Location::WEST);
	  Cell     *cell_y  = it->second->GetNeighbor(Location::SOUTH);
	  if(cell_x && spanx == 0.)
	    {
	      Location l2 = cell_x->GetLocation();
	      spanx = location.GetX() - l2.GetX();
	    }
	  if(cell_y && spany == 0.)
	    {
	      Location l2 = cell_y->GetLocation();
	      spany = location.GetY() - l2.GetY();
	    }
	      
	  if(location.GetX() < minx)
	    {
	      minx = location.GetX();
	    }
	  if(location.GetX() > maxx)
	    {
	      maxx = location.GetX();
	    }
	  if(location.GetY() < miny)
	    {
	      miny = location.GetY();
	    }
	  if(location.GetY() > maxy)
	    {
	      maxy = location.GetY();
	    }
        }
      double dx = (maxx-minx) + spanx;
      double dy = (maxy-miny) + spany;
      double delta = dx > dy ? dx : dy;
      scale = 800/delta;
      
      width  = (int)(dx*scale);
      height = (int)(dy*scale);
      glutReshapeWindow(width, height);
      glutMainLoopEvent();
    }
  
    EnvironmentDisplay::~EnvironmentDisplay()
    {
    }

    static void myDrawText(float x, float y, const char *str)
    {
      void *font = GLUT_BITMAP_TIMES_ROMAN_10;
      int   i;
      int   len;
    
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      glEnable(GL_BLEND);

      glRasterPos2f(x, y);
      len = (int) strlen(str);
      for (i = 0; i < len; i++) 
	{
	  glutBitmapCharacter(font, str[i]);
	}

      glDisable(GL_BLEND);
    }
    
    
    void EnvironmentDisplay::Redraw(ai::Agent::Environment *env)
    {
      Environment *sw_env = dynamic_cast<Environment *>(env);
      double x0, y0;
      Location location;
      
      //GLfloat whiteMaterial[] = {1.0, 1.0, 1.0, 1.0};
      GLfloat redMaterial[]   = {1.0, 0.0, 0.0, 1.0};
      GLfloat blackMaterial[] = {0.0, 0.0, 0.0, 1.0};
      
      glutSetWindow(window);
      glViewport(0, 0, width, height);
	
      // go into 2D mode
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluOrtho2D(0, width, height, 0);
      glMatrixMode(GL_MODELVIEW);

      // redraw
      glClear(GL_COLOR_BUFFER_BIT);

      std::map<Location, Cell *>::iterator it;
      for(it = sw_env->cells.begin(); it != sw_env->cells.end(); it++)
        {
	  if(it->second == 0)
	    {
	      std::cerr << "bad pointer" << std::endl;
	      continue;
	    }
	  location = it->second->GetLocation();
	  x0 = (location.GetX() - minx) + spanx/2.;
	  y0 = (location.GetY() - miny) + spany/2.;

	  if(1)
	    {
	      GLfloat elevationMaterial[4] = { 0.0, 0.0, 0.0, 1.0 };
	      
	      double x1 = ((location.GetX() - minx)) * scale;
	      double y1 = height - ((location.GetY() - miny)) * scale;
	      double x2 = ((location.GetX() - minx) + spanx)*scale;
	      double y2 = height - ((location.GetY() - miny) + spany)*scale;
	      
	      double dz = location.GetElevation() + 1000.;
	      dz = dz/2000.;
	      if(dz < 0.0) { dz = 0.; }
	      if(dz > 1.0) { dz = 1.; }
	      elevationMaterial[0] = (GLfloat)(1. - dz);
	      elevationMaterial[1] = (GLfloat)(1. - dz);
	      elevationMaterial[2] = (GLfloat)dz;
	      
	      SetColor(elevationMaterial);
	      DrawRectangle((int) x1,
			    (int) y1,
			    (int) x2,
			    (int) y2);
	    }
	  /*
	  SetColor(redMaterial);
	  DrawBox((float)((x0-spanx/2)*scale), height - (float)((y0-spany/2.)*scale),
		  (float)((x0+spanx/2)*scale), height - (float)((y0+spany/2.)*scale));
	  */


	  /*
	  double p1 = 100;
	  double p2 = 20;
	  */
	  
	  double p1 = 100;
	  double p2 = 10; // smaller numbers -> thicker lines
	  
	  int d;
	  for(d = Location::NORTH; d <= Location::WEST; d++)
	    {
	      CellInterface ci = it->second->GetInterface((Location::Direction)d);
	      GLfloat *material = interface_material[ci.GetTitle()];
	      SetColor(material);

	      DrawRectangle((int)((x0+interface_offset[d][0]*spanx/2.)*scale
				  - (interface_offset[d][2]*spany/p1*scale)),
			    
			    height - (int)((y0+interface_offset[d][1]*spany/2.)*scale
					   - (interface_offset[d][3]*spany/p1*scale)),
			    
			    (int)((x0+interface_offset[d][2]*spanx/2.)*scale
				  + (interface_offset[d][4]*spanx/p2*scale)),
			    
			    height -
			    (int)((y0+interface_offset[d][3]*spany/2.)*scale
				  + (interface_offset[d][5]*spanx/p2*scale))
			    );
	      
	    }

	  char  buf[128];
	  if(0)
	    {
	      std::sprintf(buf, "%.0f,%.0f,%.0f",
			   location.GetX(),
			   location.GetY(),
			   location.GetElevation());
	      SetColor(blackMaterial);
	      myDrawText((float)((x0-spanx/3.)*scale), (float)(height - ((y0+spany/3.)*scale)), buf);
	      std::sprintf(buf, "%d",
			   it->second->GetId());
	      SetColor(blackMaterial);
	      myDrawText((float)((x0-spanx/3.)*scale), (float)(height - ((y0+spany/3.)*scale)+15), buf);

	      std::string obj_str = "";
	      std::vector<Object *> &objs = it->second->GetObjects();
	      unsigned int j;
	      for(j = 0; j < objs.size(); j++)
		{
		  obj_str += objs[j]->GetShortName();
		  obj_str += " ";
		}
	      if(objs.size() > 0)
		{
		  SetColor(blackMaterial);
		  myDrawText((float)((x0-spanx/3.)*scale), (float)(height - ((y0+spany/3.)*scale)+30), obj_str.c_str());
		}
	    }
	  else
	    {
	      std::vector<Object *> &objs = it->second->GetObjects();
	      if(objs.size() > 0)
		{
		  std::sprintf(buf, "%d",
			       objs.size());
		  SetColor(blackMaterial);
		  myDrawText((float)((x0-spanx/3.)*scale)+3, (float)(height - ((y0+spany/3.)*scale)+8), buf);
		}
	    }
        }

      unsigned int i;
      for(i = 0; i < sw_env->agents.size(); i++)
	{
	  location = *(dynamic_cast<Location *>(sw_env->agents[i]->GetLocation()));
	  x0 = (location.GetX() - minx) + spanx/2.;
	  y0 = (location.GetY() - miny) + spany/2.;

	  SetColor(redMaterial);
	  DrawCircle((float)(x0*scale), (float)(height - y0*scale), (float)(100.*scale));
	}
      
      for(i = 0; i < sw_env->bases.size(); i++)
	{
	  location = sw_env->bases[i]->GetCell()->GetLocation();
	  x0 = (location.GetX() - minx) + spanx/2.;
	  y0 = (location.GetY() - miny) + spany/2.;

	  SetColor(blackMaterial);
	  DrawCircle((float)(x0*scale), (float)(height - y0*scale), (float)(50.*scale));
	}
      
      
      glutSwapBuffers();
      glutMainLoopEvent();
    }
  
  }  
}
