#include <cassert>
#include <string>

#include <menusystem.h>
#include <tokenizer.h>


namefont::namefont
(
  point2<GLint> const & X_,
  stringc & name_, 
  void* font_
)
  : X(X_), name(name_), font(font_) 
{
}


void namefont::draw()
{
  GOBJDEBUGCODE
  assert(font!=0);

  glRasterPos2i(X.x,X.y);
  uintc kmax = name.size();
  for (uint k=0; k<kmax; ++k)
    glutBitmapCharacter(font, name[k]);
}



void menusystem::addsubmenu(menusystem* submenu)
{
  submenues.push_back(submenu);
  submenu->parent = this;
}

menusystem::~menusystem()
{
  uint imax = submenues.size();
  for (uint i=0; i<imax; ++i)
  {
    delete submenues[i];
    submenues[i]=0;
  }
}

void menusystem::drawparent()
{
  assert(parent!=0);

  if (parent!=0)
    draw(parent);
}

void menusystem::draw(menusystem *x)
{
  assert(x!=0);

  // Save previous state.
  menusystem * t2 = x->current;

  // draw
  x->current = x;
  x->draw();
  x->current = t2;
}

void menusystem::drawparents()
{
  deque<menusystem*> parents;
  menusystem * t2 = this->parent;
  for ( ; t2 != 0; t2 = t2->parent )
    parents.push_front(t2);

  uint sz=parents.size();
  for (uint i=0; i<sz; ++i)
    draw(parents[i]);
}


void menusystem::drawparentsnested()
{
  deque<menusystem*> parents;
  menusystem * t2 = this->parent;
  for ( ; t2 != 0; t2 = t2->parent )
    parents.push_front(t2);

  uint sz=parents.size();

  if (sz==0)
  {
    menusystem::draw();
    return;
  }

  for (uint i=0; i<sz; ++i)
  {
    assertreturn(parents[i]);

    parents[i]->drawpre.draw();
    parents[i]->visibleGraphics.draw();
  }

  menusystem::draw();

  for (uint i=0; i<sz; ++i)
    parents[sz-1-i]->drawpost.draw();
}

void menusystem::transfercontroltoroot()
{
  if (parent==0)
    return;

  current = parent;
  for ( ; current->parent != 0; current = current->parent );
  current->current = current;
}

void menusystem::transfercontroltoparent()
{
  assert(parent!=0);
  current = parent;
  parent->current = parent;
}


void menusystem::transfercontroltosubmenu(uint k)
{
  assert( k<submenues.size() );

  assert(submenues[k]!=0);

  submenues[k]->current = submenues[k];
  current = submenues[k];
}

/*
Let the client do this.
void menusystem::nodedelete()
{
  uintc sz = node.size(); 
  for (uint i=0; i<sz; ++i) 
  {
    if (node[i]==0)
      continue;

    node[i]->nodedelete();
    delete node[i];
  }

  node.clear();
}
*/

void menusystem::readBufferedString(uintc index)
{
  readBufferedIndex = index;
  readBufferedStringInit = "";
  vItems[index]->name="";
  //glutPostRedisplay();
  readBufferedResult = "";

  readmodeimmediate=false;
}

void menusystem::readBufferedSet(uintc index)
{
  readBufferedIndex = index;
  readBufferedStringInit = vItems[readBufferedIndex]->name;
  readBufferedResult = "";

  readmodeimmediate=false;
}

void menusystem::readBuffered(charc ch)
{
//uint k = ch;
//cout << "*" << k << "*" << endl; 

  // Return key pressed.
  if (ch==13)
  {
    readmodeimmediate=true;
    vItems[readBufferedIndex]->name = readBufferedStringInit;
    readBufferedTerminationAction();
    return;
  }

  // Backspace key pressed
  if (ch==8)
  {
    uint sz = readBufferedResult.size();

    if (sz==0)
      return;

    // sz>0
    readBufferedResult.erase(sz-1,1);
    string & s(vItems[readBufferedIndex]->name);
    s.erase(s.size()-1,1);

    return;
  }

  readBufferedResult += ch;
  vItems[readBufferedIndex]->name += ch;
}


void menusystem::read(charc ch)
{
  assertreturn(current!=0);

  for ( ; current->current!=current; )
  {
    current = current->current;
    assert(current!=0);
  }

  if (current->readmodeimmediate)
    current->readImmediate(ch);
  else
    current->readBuffered(ch);

  glutPostRedisplay();
}

void menusystem::draw()
{
  GOBJDEBUGCODE
  if (this!=current)
  {
    if (current!=0)
    {
      current->draw();
      return;
    }
  }

  drawpre.draw();

  visibleGraphics.draw();

  drawpost.draw();
}

void menusystem::fontcolorenable()
{
// glDisable(GL_DEPTH_TEST);
// Test for depth test state!
  drawpre.push_back(new gobjglDisable(GL_DEPTH_TEST));
  drawpre.push_back(new gobjglEnable(GL_BLEND));
  drawpre.push_back(new gobjglBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA));

  drawpre.push_back(new gobjglColor4f(fontcolor.x,fontcolor.y,fontcolor.z,fontcolor.w));

  drawpost.push_front(new gobjglEnable(GL_DEPTH_TEST));
  drawpost.push_front(new gobjglDisable(GL_BLEND));
}

void menusystem::addfont10paragraphstart
(
  stringc & s, 
  uintc len,
  uintc newlines
)
{
  for (uint i=0; i<newlines; ++i)
    scrolldown();

  uint slen = s.size();
  uint k=0;
  size_t k2;
  uint k3;
  uint k4;

  vector<string> vi;

  for ( ; k<slen; )
  {
    k4 = k + len;
    // Test if at the end of the iteration.
    //if (slen-k <= len )
    if (k4 >= slen)
    {
      vi.push_back(s.substr(k));
      break;
    }

    k2 = s.find(" ",k4);
    if (k2==string::npos)
    {
      vi.push_back(s.substr(k));
      break; 
    }

    vi.push_back(s.substr(k,k2-k));

    k=k2;

    k3 = s.find_first_not_of(" ",k);
    if (k3==string::npos)
      break;

    k = k3;
  }

  uintc sz=vi.size();
  for ( uint i=0; i<sz; ++i)
  {
    addfont10start(vi[sz-i-1],1); 
  }
}


void menusystem::addfont10paragraph
(
  stringc & s, 
  uintc len,
  uintc newlines
)
{
  uint slen = s.size();
  uint k=0;
  uint k2;
  uint k3;
  uint k4;

  for ( ; k<slen; )
  {
    k4 = k + len;
    // Test if at the end of the iteration.
    //if (slen-k <= len )
    if (k4 >= slen)
    {
      addfont10(s.substr(k),1); 
      //addnewline();

      //return;
      break;
    }

    k2 = s.find(" ",k4);
    if (k2==string::npos)
    {
      addfont10(s.substr(k),1); 
      //addnewline();
      //return;
      break; 
    }

    addfont10(s.substr(k,k2-k),1);
    //addnewline();

    k=k2;

    k3 = s.find_first_not_of(" ",k);
    if (k3==string::npos)
      //return;
      break;

    k = k3;
  }

  addnewline(newlines);
}


void menusystem::addfont10(uint & index, stringc & s, uintc newlines)
{
  vItems.push_back( new namefont(X,s,GLUT_BITMAP_HELVETICA_10) );
  index = vItems.size()-1;
  visibleGraphics.push( vItems[index] );

  addnewline(newlines);
}

void menusystem::addfont10(stringc & s, point2<GLint> const & X2 )
{
  vItems.push_back( new namefont(X2,s,GLUT_BITMAP_HELVETICA_10) );
  visibleGraphics.push( vItems[vItems.size()-1] );
}


void menusystem::addfont10(stringc & s, uintc newlines)
{
  vItems.push_back( new namefont(X,s,GLUT_BITMAP_HELVETICA_10) );
  visibleGraphics.push( vItems[vItems.size()-1] );

  addnewline(newlines);
}

void menusystem::addfont10start(stringc & s, uintc newlines)
{
  for (uint i=0; i<newlines; ++i)
    scrolldown();

  vItems.push_back( new namefont(X0,s,GLUT_BITMAP_HELVETICA_10) );
  visibleGraphics.push( vItems[vItems.size()-1] );
}

void menusystem::addfont12(uint & index, stringc & s, uintc newlines)
{
//cout << SHOW(X) << " " << s << endl;
  vItems.push_back( new namefont(X,s,GLUT_BITMAP_HELVETICA_12) );
  index = vItems.size()-1;
  visibleGraphics.push( vItems[index] );

  addnewline(newlines);
}

void menusystem::addfont12start(stringc & s, uintc newlines)
{
  for (uint i=0; i<newlines; ++i)
    scrolldown();

  vItems.push_back( new namefont(X0,s,GLUT_BITMAP_HELVETICA_12) );
  visibleGraphics.push( vItems[vItems.size()-1] );
}

void menusystem::addfont12(stringc & s, uintc newlines)
{
//cout << "@" << SHOW(X) << " " << s << endl;
  vItems.push_back( new namefont(X,s,GLUT_BITMAP_HELVETICA_12) );
  visibleGraphics.push( vItems[vItems.size()-1] );

  addnewline(newlines);
}

void menusystem::addfont12(stringc & s, point2<GLint> const & X2 )
{
  vItems.push_back( new namefont(X2,s,GLUT_BITMAP_HELVETICA_12) );
  visibleGraphics.push( vItems[vItems.size()-1] );
}


void menusystem::addfont10blockstart
(
  stringc & s, 
  uintc newlines
)
{
  scroll(newlines);

  tokenizer tz(s);

  tz.subtract("\n");
  vector<string> vi;
  for ( list<string>::reverse_iterator i = tz.seq.rbegin(); i!=tz.seq.rend(); ++i )
    vi.push_back(*i);

  for (uint i=0; i<vi.size(); ++i)
    addfont10start(vi[i],1);
}

menusystem::menusystem
(
  menusystem * current_,
  menusystem * parent_,
  bool readmodeimmediate_,
  point2<GLint> const & X0_,
  GLint const columnchange_
) : 
  current(current_), 
  parent(parent_), 
  readmodeimmediate(readmodeimmediate_),
  visibleGraphics(true),
  X0(X0_), 
  columnchange(columnchange_),
  fontcolor(1.0,1.0,1.0,0.8) 
{
  reset();
}

menusystem::menusystem
(
  bool readmodeimmediate_,
  point2<GLint> const & X0_,
  GLint const columnchange_
) : 
  current(0), 
  parent(0), 
  readmodeimmediate(readmodeimmediate_),
  visibleGraphics(true),
  X0(X0_), 
  columnchange(columnchange_), 
  fontcolor(1.0,1.0,1.0,0.8) 
{
  reset();
}

menusystem::menusystem
(
  point2<GLint> const & X0_,
  GLint const columnchange_
) : 
  current(0), 
  parent(0), 
  readmodeimmediate(true),
  visibleGraphics(true),
  X0(X0_), 
  columnchange(columnchange_), 
  fontcolor(1.0,1.0,1.0,0.8) 
{
  reset();
}

void menusystem::scroll(GLint const k)
{
  uintc imax = vItems.size();
  for (uint i=0; i<imax; ++i)
  {
    if (vItems[i]==0)
      continue;

    vItems[i]->X.y += k;
  }
}

void menusystem::scrolldown()
{
  scroll(columnchange);
}

void menusystem::scrollup()
{
  scroll(-columnchange);
}

void menusystemOneShot::draw()
{
  GOBJDEBUGCODE
  switch (drawchoice)
  {
    case drawmenuthis:
      break;
    case drawmenuparent:
      drawparent();
      break;
    case drawmenuparents:
      drawparents();
      break;
  }
  menusystem::draw();
}

void menusystem::Xpush()
{
  Xstack.push_back(X);
}

void menusystem::Xpop()
{
  uint sz = Xstack.size();
  if (sz==0)
    return;
  X = Xstack[sz-1];

  Xstack.pop_back();
}

void menusystem::addfont10paragraphs
(
  stringc & s, 
  uintc len, 
  stringc & token
)
{
  tokenizer tk(s);
  tk.subtract(token);

  for ( tk.reset(); !tk; ++tk)
    addfont10paragraph(tk(),len,0);
}





