#include <cassert>
#include <sstream>
#include <string>
using namespace std;

#include <fnobjTfn.h>
#include <gobj.h>
#include <mazedisp03.h>
#include <pointsdisplay.h>
#include <stringconvert.h>
#include <typedefs.h>
#include <zprmouse.h>


mazedisp03::mazedisp03( mazegameD2state01 & mg_ ) 
  : mg(mg_), mousecallback(*this,&mazedisp03::mouseevent), zm(0)
{
}

mazedisp03::~mazedisp03()
{
  delete zm;
  zm=0;
}

void mazedisp03::game01default()
{
  dx=0.1;
  displaycellid=false;
  pipes=false;
  pipecolor=point3<double>(1.0,0.0,0.0);
  walls=true;
  wallcolor=point3<double>(0.0,0.0,1.0);
  origin=point2<double>(0.0,0.0);
  backgroundcolor=point3<double>(0.0,0.0,0.0);
  endpointratio=0.25;
  endpointcolor=point3<double>(0.0,1.0,0.0);
  currentposcolor=point3<double>(0.0,1.0,205.0/255.0);
}

void mazedisp03::draw()
{
//cout << "mazedisp03::draw()" << endl;
  gx.draw();
}

void mazedisp03::construct()
{
  //gx.push(staticgraphicsindex,*this,&mazedisp03::staticgraphics);
  gx.pushdeferred(staticgraphicsindex,*this,&mazedisp03::staticgraphics);
  gx.update();

  uint index;
  
  gx.pushimmediate(index,gobjcallbackcreatenew(*this,&mazedisp03::currentposdraw));


// Turned off mouse button
  bp02 = new buttonpanel02(zm);
//  gx.pushimmediate(index,bp02);

}

//void mazedisp03::construct()
//{
/*
  gx.graphicsDeferred.globalpush();
  staticgraphics();
  gobjContainer::globalpop();
  gx.update();
*/
//}

/*
void mazedisp03::staticgraphics()
{
  gobjpush( new gobjglPushMatrix() );
  gobjpush( new gobjglTranslated(point3<double>()) );
  gobjpush( new gobjglutSolidSphere(dx*3.0,35,35) );
  gobjpush( new gobjglPopMatrix() );
}
*/


point2<double> const mazedisp03::cellmidpoint(uintc id) const
{
  uint k = id-1;
  uint m=mg.mz.dim[0];
  uint n=mg.mz.dim[1];

  point2<uint> pos1;
  point2<double> pos2;

  pos1.x = k % n;
  pos1.y = (m*n-1)/n-k/n;
  pos2.x = dx*pos1.x;
  pos2.y = dx*pos1.y;

  return pos2+point2<double>(dx*0.5,dx*0.5);
}

void mazedisp03::staticgraphics()
{
  assert(dx!=0.0);
  assert(mg.mz.valid());
  assert(mg.mz.vi.size()==(1+mg.mz.dim[2]));

  gobjpush(new gobjglDisable(GL_LIGHTING));
  gobjpush(new gobjglBegin(GL_LINES));

  uint m=mg.mz.dim[0];
  uint n=mg.mz.dim[1];

  uint id;
  point2<uint> pos1;
  point2<double> pos2;
  uint K=m*n-1;

  if (walls)
  {
    gobjpush(new gobjglColor3d(wallcolor));

    for (uint k=0; k<mg.mz.dim[2]; ++k)
    {
      id=k+1;
      pos1.x = k % n;
      pos1.y = K/n-k/n;
      pos2.x = dx*pos1.x;
      pos2.y = dx*pos1.y;
      celldraw(mg.mz.vi[id],pos2);    
    }
  } 

  if (pipes)
  {
    gobjpush(new gobjglColor3d(pipecolor));
    for (uint k=0; k<mg.mz.dim[2]; ++k)
    {
      id=k+1;
      pos1.x = k % n;
      pos1.y = K/n-k/n;
      pos2.x = dx*pos1.x;
      pos2.y = dx*pos1.y;
      celldraw2(mg.mz.vi[id],pos2);
    }
  }
  
  gobjpush(new gobjglEnd()); 

  // Each cell has a midpoint in the maze, calculate it.
  vector< point2<double> > pi;
  pi.push_back(point2<double>());

  for (uint k=0; k<mg.mz.dim[2]; ++k)
  {
    pi.push_back(cellmidpoint(k+1));
/*
    id=k+1;
    pos1.x = k % n;
    pos1.y = K/n-k/n;
    pos2.x = dx*pos1.x;
    pos2.y = dx*pos1.y;

    pi.push_back(pos2+point2<double>(dx*0.5,dx*0.5));
*/
  }

  if (displaycellid)
  {
    assert(gobjContainer::global);
    gobjpush(new gobjglColor3f(point3<float>(1.0,0.0,0.0)));
    pointsdisplay2D< point2<double> > pd(*gobjContainer::global,pi,false,true,true);
  }

  gobjpush( new gobjglEnable(GL_LIGHTING) ); 
  gobjpush( new gobjglColor3d(endpointcolor) );

  //double endpointsratio=0.25;

  gobjpush( new gobjglPushMatrix() );
  gobjpush( new gobjglTranslated( pi[mg.gamestart] ) );
  gobjpush( new gobjglutSolidSphere(dx*endpointratio,35,35) );
  gobjpush( new gobjglPopMatrix() );

  gobjpush( new gobjglPushMatrix() );
  gobjpush( new gobjglTranslated( pi[mg.gamefinish] ) );
  gobjpush( new gobjglutSolidSphere(dx*endpointratio,35,35) );
  gobjpush( new gobjglPopMatrix() );

}


void mazedisp03::celldraw
( 
  cellD2<uint> const & x,
  point2<double> const & p00
)
{
  point2<double> p10 = p00;
  p10.x += dx;
  point2<double> p01 = p00;
  p01.y += dx;
  point2<double> p11 = p00;
  p11.x += dx;
  p11.y += dx;

  if (x.ni[1]==0)
  {
    gobjpush(new gobjglVertex2d(p10));
    gobjpush(new gobjglVertex2d(p11));
  }

  if (x.ni[3]==0)
  {
    gobjpush(new gobjglVertex2d(p01));
    gobjpush(new gobjglVertex2d(p00));
  }
  
  if (x.ni[0]==0)
  {
    gobjpush(new gobjglVertex2d(p01));
    gobjpush(new gobjglVertex2d(p11));
  }
  
  if (x.ni[2]==0)
  {
    gobjpush(new gobjglVertex2d(p00));
    gobjpush(new gobjglVertex2d(p10));
  }
}

void mazedisp03::celldraw2
( 
  cellD2<uint> const & x,
  point2<double> const & p00
)
{
  double dx2 = dx*0.5;

  point2<double> p3 = p00;
  p3.y += dx2;
  point2<double> p1 = p00;
  p1.x += dx;
  p1.y += dx2;
  point2<double> p0 = p00;
  p0.x += dx2;
  p0.y += dx;
  point2<double> p2 = p00;
  p2.x += dx2;

  point2<double> pcenter = p00;
  pcenter.x += dx2;
  pcenter.y += dx2;

  if (x.ni[1]!=0)
  {
    gobjpush(new gobjglVertex2d(pcenter));
    gobjpush(new gobjglVertex2d(p1));
  }

  if (x.ni[3]!=0)
  {
    gobjpush(new gobjglVertex2d(pcenter));
    gobjpush(new gobjglVertex2d(p3));
  }
  
  if (x.ni[0]!=0)
  {
    gobjpush(new gobjglVertex2d(pcenter));
    gobjpush(new gobjglVertex2d(p0));
  }
  
  if (x.ni[2]!=0)
  {
    gobjpush(new gobjglVertex2d(pcenter));
    gobjpush(new gobjglVertex2d(p2));
  }
}

stringc mazedisp03::settings() const
{
  string options="";
  string s;

  options += ( "dx=" + stringconvert::tostring(dx) + " ");
  options += ( "id=" + stringconvert::tostring(displaycellid) + " ");
  options += ( "pipes=" + stringconvert::tostring(pipes) + " ");
  options += ( "pipecolor=" + stringconvert::findandreplacefn(stringto(pipecolor*255.0)," ",",") + " ");
  options += ( "walls=" + stringconvert::tostring(walls) + " ");
  options += ( "wallcolor=" + stringconvert::findandreplacefn(stringto(wallcolor*255.0)," ",",") + " ");
  options += ( "origin=" + stringconvert::findandreplacefn(origin," ",",") + " ");
  options += ( "backgroundcolor=" + stringconvert::findandreplacefn(stringto(backgroundcolor*255.0)," ",",") + " ");
  options += ( "endpointratio=" + stringconvert::tostring(endpointratio) + " ");
  options += ( "endpointcolor=" + stringconvert::findandreplacefn(stringto(endpointcolor*255.0)," ",",") + " ");
  options += ( "currentposcolor=" + stringconvert::findandreplacefn(stringto(currentposcolor*255.0)," ",",") + " ");

  return options;
}

void mazedisp03::currentposdraw()
{
  assert(mg.path.empty()==false);
  uint cp = mg.path[mg.path.size()-1];

  point2<double> cpx = cellmidpoint(cp);

  glPushAttrib(GL_CURRENT_BIT);
  glColor3d(currentposcolor.x,currentposcolor.y,currentposcolor.z);

  // Assume model view
  glPushMatrix();
  glTranslated(cpx.x,cpx.y,0.0);
  glutSolidSphere(dx*endpointratio,35,35);
  glPopMatrix();

  for (uint i=0; i<mg.path.size(); ++i)
  {
    point2<double> cppos = cellmidpoint(mg.path[i]);
    glPushMatrix();
    glTranslated(cppos.x,cppos.y,0.0);
    glutSolidSphere(dx*endpointratio*0.5,35,35);
    glPopMatrix();
  }

  glPopAttrib();

  //f01();
}

void mazedisp03::f01()
{
  assert(zm);

  // Code taken from tutorial
  //   http://basic4gl.wikispaces.com/2D+Drawing+in+OpenGL

  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  glOrtho(0,zm->zz.width,zm->zz.height,0,0,1);
  glDisable(GL_DEPTH_TEST);
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  glTranslatef(0.375,0.375,0);

  int radius=5;
  int x1=320;
  int y1=240;

//cout << "Drawing in 2D" << endl;

  glPushAttrib(GL_CURRENT_BIT);
  glPushAttrib(GL_LIGHTING_BIT);
  glDisable(GL_LIGHTING);
  glColor3f(1.0,0.0,0.0);

  double rad=3.141592*2.0/360.0;

  glBegin(GL_TRIANGLE_FAN);
  glVertex2f(x1,y1);
  for (double angle=0; angle<=360; angle+=2)
  {
    point2<double> x2(x1+ sin(rad*angle)*radius, y1 + cos(rad*angle)*radius);
    cout << SHOW(x2) << " ";
    glVertex2d(x2.x,x2.y);
    //glVertex2f(x1 + sin(rad*angle) * radius, y1 + cos(rad*angle) * radius);
    //glVertex2f(x1 + sin(angle)*radius, y1 + cos(angle)*radius);
  }
  glEnd();

cout << endl;

  glPopAttrib();
  glPopAttrib();

  // Returning to previous state.
  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);

  glEnable(GL_DEPTH_TEST);
}


void mazedisp03::mouseevent()
{
  assert(zm);
  if (zm==0)
    return;

  zm->update();

  //cout << "mouse() " << zm->mouse() << "  " << zm->world() << endl;

// Turned off button code because of bad usablity
/*
  assert(bp02);
  bool res(false);
  uint index;
  bp02->process(res,index,zm->mouseint());

  if (res==false)
    return;

  assertreturnfalse(index<4);

  mg.currentmove(index);

  glutPostRedisplay();
*/
}




