/* Investigating the scan line algorithm. 
 *
 * Not using incremental arithmetic, exploring the algorithm.
 *
 * Note that the difficult case of merging was avoided,
 *   I was a little lazy.  The example given didn't need it,
 *   but as y increases if it hits new lines they must
 *   be inserted into the current scan line v in the
 *   correct order.  This is merging two
 *   already sorted lists. 
 */

#include <cassert>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <vector>
using namespace std;

#include <GL/glut.h>



class line
{
public:
  
  unsigned int ymax;
  float dx;
  float x;

  line
  (
    unsigned int const ymax_,
    float const dx_,
    unsigned int const x_
  )
    : ymax(ymax_), dx(dx_), x(x_) {}

 

  bool const isdead(unsigned int const y) const
    { return ymax >= y; }

  void inc()
    { x += dx; }

  ostream & print(ostream & os) const 
    { return os << "{" << ymax << "," << dx << "," << x << "}"; }

};

ostream & operator << (ostream & os, line const & ln)
{
  return ln.print(os);
}
  



class scanlines 
{
public:
  vector< vector<line> > scn;
  unsigned int N;

  // Current Line
  vector<line> v;
  unsigned int index;


  void example();

  scanlines()
  {  
    N = 20;
    scn.resize(N); 
    index=0;

    example();
  }

  void remove_dead_and_inc()
  {  
    if (v.empty())
      return;

    vector<line> v2;
    for (unsigned int i=0; i<v.size(); ++i)
    {
      if (v[i].ymax>index)
      {
        v[i].inc();
        v2.push_back(v[i]);
      }
    }
    v = v2;
  }

  bool const operator ! () const
    { return index<N; }

  void operator ++ ()
  {
    remove_dead_and_inc();
    ++index;
  }


  void drawline
  (
    line const & a, 
    line const & b, 
    unsigned int const y
  ) const
  {
    float const wx = 10.0;
    float const wy = 10.0;

cout << "a=" << a << " ";
cout << "b=" << b << endl;

    assert(a.x<b.x);

    glBegin(GL_LINES);
    glColor3f(1.0,0.0,0.0);

    glVertex2f(wx*a.x,wy*y);
    glVertex2f(wx*b.x,wy*y);

    glEnd();
  }

  void write_current_line() const
  {
    if (v.empty())
      return;

    assert((v.size()%2)==0);

    for (unsigned int i=0; i<v.size(); i+=2 )
    {
      if (v[i].x!=v[i+1].x)
        drawline(v[i],v[i+1],index);
    }
  }

};


void scanlines::example()
{
  scn[5].push_back( line(13,0.0,0) );
  scn[5].push_back( line(9,0.0,8) );
  scn[5].push_back( line(9,-2.0,16) );
  scn[5].push_back( line(13,0.5,16) );
}


scanlines sc;


void axes0(double const sx, double const sy, double const len )
{
  assert(len>0.0);

  glPushMatrix();
  glTranslated(sx,sy,0.0);
  
  glDisable(GL_LIGHTING);
  glBegin(GL_LINES);

  glVertex2f(-len,0.0);
  glVertex2f(len,0.0);
  glVertex2f(0.0,-len);
  glVertex2f(0.0,len);

  glEnd();
  glPopMatrix();
}

void axes()
{
  glColor3f(0.0,1.0,0.0);

  for (int i=0; i<=20; ++i)
    axes0(i*10.0,i*10.0,1000.0);
}


void display(void)
{
  glClear (GL_COLOR_BUFFER_BIT);

  glPushMatrix();

  axes();

//  glColor3f(1.0,0.0,0.0);
//  glTranslatef(150.0,150.0,0.0);


  for ( ; !sc; ++sc )
    sc.write_current_line();

  glFlush ();

  glPopMatrix();
}

void myinit()
{
  glClearColor (0.0, 0.0, 0.0, 0.0);
  glShadeModel (GL_FLAT);

  sc.index = 5;
  sc.v = sc.scn[5];
}

static void reshape(GLsizei w, GLsizei h)
{
  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0.0, (GLdouble)w, 0.0, (GLdouble)h, -1.0, 1.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}


void key(unsigned char k, int x, int y)
{
  switch (k) 
  {
    // Escape
    case 27: exit(0); break;

    default:
      return;
  }
  glutPostRedisplay();
}


int main(int argc, char** argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
  glutInitWindowSize (210, 150);
  glutCreateWindow (argv[0]);
  myinit ();
  glutDisplayFunc(display);
  glutReshapeFunc(reshape);
  glutKeyboardFunc(key);
  glutMainLoop();

  return 0; 
}



