#ifndef MENUSYSTEM_H
#define MENUSYSTEM_H

#include <vector>
using namespace std;

#include <GL/glut.h>
#include <GL/gl.h>

#include <point.h>
#include <gobj.h>
#include <textoverlay.h>
#include <typedefs.h>

// TODO 
// http://stackoverflow.com/questions/538661/how-do-i-draw-text-with-glut-opengl-in-c
// glutbitmapstring http://linux.die.net/man/3/glutbitmapstring
// http://linux.die.net/man/3/glutstrokestring

/*! 
\brief Primitive text drawing class using Glut. 

Holds the string being printed, the font being used,
 the position to print and has the drawing function.
*/
class namefont : public gobj
{
public:

  /** Current position. */
  point2<GLint> X;
  /** The string being displayed. */
  string name;
  /** The glut font being pointed to. */
  void* font;

  /** The choices of font are GLUT_BITMAP_{HELVETICA_10, 
  HELVETICA_12, HELVETICA_18, 8_BY_13, 9_BY_15, 
  TIMES_ROMAN_10, TIMES_ROMAN_24 } */
  namefont
  (
    point2<GLint> const & X_,
    stringc & name_, 
    void* font_=GLUT_BITMAP_HELVETICA_10
  );

  /** Draw the text at position X. */
  void draw();
};



/*!
\brief  Display a primitive menu and process users 
  keyboard input in either immediate or deferred mode.

This class is a monolith by design which usually is not
 a good thing, having said that it was designed to be 
 a primitive menu system that does the job.  It also puts
 much generality in the base class so the derived classes
 implement the menu whereas the base gives the functionality.
 So it is not a standard monolith, but a deliberate/organized one.

Keyboard capture and graphics are brought together to build the menu system.
 
NOTES
 * the addfont* functions always reset X.x to the starting 
   value X0.x .  So addfont("abc",0); leaves X.x at the start. 
 * The current menu needs to be set and the tree holds its own state,
   so link the menues, set the current menu, and push the current menu 
   to the global graphics gobj.
\verbatim
  root = new menusystemtest03Main();
  ...
  menusystem * m = new menusystemtest03Config();
  root->addsubmenu(m);
  root->currentset();
  gobjpush(root);
\endverbatim
 * To make a menu the client uses inheritance deriving from a menusystem
   and over riding the appropriate functions.  For example over ride 
   readImediate and draw.
 * This class was designed to have the general functionality in the menusystem 
   class.  Specialized menues for input are derived, see menusystemOneShot.
 * This class contains variables which the derived class could choose to use.  
   For example the submenues vector allows for possible sub menus.  It also lets 
   the parent see such submenues too.
 * The derived class writes its graphics to the visibleGraphics container.

 * X is the current position is not uptodate with OpenGL's raster position.
   So do not expect to use addfont("abc",0) and have the cursor after c, this
   menu system is not that sophisticated. Instead manually move the cursor X.x yourself.
 * The menu system can also be used as a terminal without processing keys.
\par Example of Terminal
\verbatim
  menusystem * menu = 
    new menusystem(0,0,true,point2<GLint>(20,20),30);
  gobjpush(menu);

  menu->addfont10("this is a bad idea.");
\endverbatim
 * Moving/linking the menu tree is done by the transfercontrol* and addsubmenu
   functions.
 * Scrolling the graphics up and down is supported through scroll* functions.  
*/
class menusystem : public textoverlay
{
public:

  /** The current menu selected. */
  menusystem * current;
  /** The previous or parent menu. */
  menusystem * parent;
  /** The sub menues, each has been allocated from new operator. */
  vector<menusystem *> submenues;

  /** If true the keyboard events are processed in the 
  immediate mode, else the input is processed in the 
  deferred mode. */
  bool readmodeimmediate;

  /** The menu's visible graphics. */
  gobjContainer visibleGraphics;

  /** Initial Position */
  point2<GLint> X0;
  /** Current Position */
  point2<GLint> X;
  /** Line height change. */
  GLint columnchange;

  /** Temporary for saving current position. */
  vector< point2<GLint> > Xstack;
  /** Push the current position onto the stack. */
  void Xpush();
  /** Pop the top position of the stack making it the new position. */
  void Xpop();

  /** Text items to be displayed. */
  vector<namefont*> vItems;

  /** The color that the font is drawn. */
  point4<float> fontcolor;
  /** Turn on transparent fontcolor by writing to graphics to 
      drawpre and drawpost. */
  void fontcolorenable();

  /** Constructor with tree links, mode status, initial 
      position and line height. */
  menusystem
  (
    menusystem * current_,
    menusystem * parent_,
    bool readmodeimmediate_,
    point2<GLint> const & X0_,
    GLint const columnchange_
  ); 

  /** Constructor with minimal configuration, the client can 
      configure other items separately. */
  menusystem
  (
    bool readmodeimmediate_,
    point2<GLint> const & X0_,
    GLint const columnchange_
  ); 

  menusystem
  (
    point2<GLint> const & X0_,
    GLint const columnchange_
  ); 

  /** Assumes pointers allocated from new and cleans up by calling delete. */
  ~menusystem();

  /** Designed to display the current menusystem. */
  void draw();
  /** Temporarily make x current and draw. */
  void draw(menusystem *x);

  /** Draw the parent menu. */
  void drawparent();
  /** Recursively draw the parents menues. */
  void drawparents();
  /** Parent menu can enable setting as submenus inherit
      parents menu's scope. 
      Nested menus where drawpre are nested and drawn left
      to right and drawpost are drawn right to left. */
  void drawparentsnested();

  /** Characters are sent to this object, the processing is 
      forwarded to reading in immediate or buffered mode. */
  void read(charc ch);

  //
  // Functionality to configure the display
  //

  /** Set the current position to X0. */
  void reset()
    { X.x = X0.x; X.y = X0.y; }

  /** Clears the display, returning to starting position. */
  void clear()
    { vItems.clear(); visibleGraphics.nuke(); reset(); }

  /** Return to the start of the next line. */
  void addnewline()
    { X.x = X0.x; X.y += columnchange; }
  /** Add multiple blank lines. */
  void addnewline(uintc newlines)
    { X.x = X0.x; X.y += columnchange*newlines; }

  /** Write a 10 point string to specified position. */
  void addfont10(stringc & s, point2<GLint> const & X2 );
  /** Write a 10 point string to the current position X. */
  void addfont10(stringc & s, uintc newlines=0);
  /** Write a 10 point string and have a index to it. */
  void addfont10(uint & index, stringc & s, uintc newlines=0);

  /** Write a 12 point string to specified position. */
  void addfont12(stringc & s, point2<GLint> const & X2 );
  /** Write a 12 point string to the current position X. */
  void addfont12(stringc & s, uintc newlines=0);
  /** Write a 12 point string and have a index to it. */
  void addfont12(uint & index, stringc & s, uintc newlines=0);

  /** The new lines call scrolldown first. */
  void addfont10start(stringc & s, uintc newlines=0);
  /** The new lines call scrolldown first. */
  void addfont12start(stringc & s, uintc newlines=0);

  /** Interpret the string as a paragraph with minimum len 
      character width. */
  void addfont10paragraph
  (
    stringc & s, 
    uintc len, 
    uintc newlines=0
  );
  /** At a paragraph to the start of the display. */
  void addfont10paragraphstart
  (
    stringc & s, 
    uintc len, 
    uintc newlines=0
  );
  void addfont10blockstart
  (
    stringc & s, 
    uintc newlines=0
  );
  /** Split the string on the token. */
  void addfont10paragraphs
  (
    stringc & s, 
    uintc len, 
    stringc & token="\n" 
  );

  /** Scroll all graphics down one colum */
  void scrolldown();
  /** Scroll all graphics up one colum */
  void scrollup();
  /** Scroll all graphics down for positive k. */
  void scroll(GLint const k);

  //
  // Control menu tree.
  // 
  //

  /** Set this menu to be the current menu to be viewed. */
  void currentset() { current=this; }

  /** Change the current menu to the root menu. */
  void transfercontroltoroot();
  /** Change the current menu to the parent. */
  void transfercontroltoparent();
  /** Change the current menu to the k'th submenu. */
  void transfercontroltosubmenu(uint k);

  /** Add a submenu, linking it to this menu. */
  void addsubmenu(menusystem* submenu);

protected:

  /** Redefine to perform an action on receiving the
  character. */
  virtual void readImmediate(charc ch) {}
  //! Redefine to store the characters until a termination character is reached. */


  /** Temporary: initial string displayed as the user 
      enters text. */
  string readBufferedStringInit;
  /** Temporary: storage of index. */
  uint readBufferedIndex;
  /** Temporary: the result of readBuffered is accumulated. */
  string readBufferedResult;

  /** Optionally define a callback function for when the 
      buffered read has completed. */
  virtual void readBufferedTerminationAction() {};

  /** Set the input mode to deferred so users keys are ouput to the screen
      as they type.  index points to a namefont in vItems. 
      The input is appended. */
  void readBufferedSet(uint index);
  /** Set the input mode to deferred so users keys are ouput to the screen
      as they type.  index points to a namefont in vItems. 
      Deletes the previous string pointed to by index. */
  void readBufferedString(uint index);
  /** Read the given character. Default behaviour: return 
      terminates reading, backspace supported. */
  virtual void readBuffered(charc ch);
};


/*!
\brief Menu used when reading in data input from user.

To turn on reading set immediate mode to true and make
 this the current menu.

Once the client passes a return character through read(charc)
 the menu system control is passed back to the parent menu.   
 Hence the name one-shot because when the job is done 
 control is passed back.
*/
class menusystemOneShot : public menusystem
{
  /** Reference to string in vItems to echo output to 
      the texts end.*/
  uintc index;
public:

  /** drawmenuthis only echos the input text only. 
      drawmenuparent draws the parent too.  drawmenuparents 
      draws all the parents recursively up. */
  enum drawmenu { drawmenuthis, drawmenuparent, drawmenuparents};

  /** The current drawing choice. */
  drawmenu drawchoice; 

  /** Create the menu without hooking it up to other menues,
      do this after construction. */
  menusystemOneShot
  (
    point2<int> const & p, 
    uintc columnchange_,
    uintc index_,
    drawmenu drawchoice_=drawmenuthis
  )
    : menusystem(true,p,columnchange_), index(index_), 
      drawchoice(drawchoice_) {}

  /** Draw the menu and parents based on the drawchoice 
      state. */
  void draw();

protected:

  /** The immediate mode is used to initialize the read. */
  void readImmediate(charc ch) 
  {
    readmodeimmediate=false;
    readBufferedSet(index);
    read(ch);
  }

  /** readBufferedResult contains the string that was read. */
  void readBufferedTerminationAction()
  {
    readmodeimmediate=true;
    transfercontroltoparent();
  }
  
};




#endif


