#include <ubit/ubit.hpp>
#include <ubit/ugraph.hpp>    // inclure ce header pour le graphique !
#include <vector>
using namespace std;

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

class Drawing: public UVbox {

  struct Point {
    int x, y;
    Point(int _x, int _y) {x = _x; y = _y;}
  };

  typedef std::vector<Point>      PointList;
  typedef std::vector<PointList*> FlowList;
  typedef vector<FlowList*>  DispList;

  DispList disp_list;

  UBox canvas;
  int view_count;
  UView** view_list;

  UColor fg;
  UBackground bg;

  PointList* getPointList(int flow_id, int disp_id);
  void mousePressed(UEvent&);
  void mouseReleased(UEvent&);
  void mouseDragged(UEvent&);
  void repaintGraphics(UEvent&);

public:
  Drawing();
};

/* ==================================================== [Elc:03] ======= */
/* ==================================================== ======== ======= */

int main(int argc, char *argv[]) {
  UConf conf(argc, argv);
  //conf.double_buffering = true;     // double buffering mode
  //conf.transp_scrollbars = false;
  
  UAppli appli(conf);
  appli.setImaPath("../images");     // where images should be located

  Drawing drawing;

  // creates the main frame window, adds it to the appli then shows it
  UFrame mainframe(drawing);
  appli.add(mainframe);
  mainframe.show();

  // opens clones on alternate display
  for (int k = 1; k < argc; k++) {
    UDisp* disp = appli.openDisp(argv[k]);

    if (disp) {
      UStr t = ustr("Drawing on ") & argv[k];
      UFrame& frame2 = uframe( utitle(t) + drawing );
      disp->add(frame2);
      frame2.show();
    }
  }

  return appli.mainLoop();    // starts the event loop
}

/* ==================================================== [Elc:03] ======= */
/* ==================================================== ======== ======= */

Drawing::Drawing() {

  view_count = 0;
  view_list = null;
  bg.set(UColor::white);
  fg.set(UColor::black);

  canvas.addlist            // drawing area
    (
     // NB: fg and bg will auto udpate the canvas when changed
     bg	+ fg
     + uwidth(600) + uheight(600) 
     + UOn::mpress / ucall(this, &Drawing::mousePressed)
     + UOn::mdrag  / ucall(this, &Drawing::mouseDragged)
     + UOn::mrelease  / ucall(this, &Drawing::mouseReleased)
     // repaint events
     + UOn::viewPaint / ucall(this, &Drawing::repaintGraphics)
     );

  UListbox& wallpapers = ulistbox
    (
     ulabel("Wallpaper")
     + uitem( "Ray" + usetref(&bg, UPix::ray))
     + uitem( "Bomb" + usetref(&bg, UPix::bomb))
     + uitem( "Frisco" + usetref(&bg, uima("frisco.gif")))
     + uitem( "Eiffel" + usetref(&bg, uima("tour-eiffel.gif")))
     + uitem( "printemps" + usetref(&bg, uima("printemps.gif")))
     + uitem( "Metal" + usetref(&bg, uima("metal.gif")))
     );  
  
  UBox& bg_panel = ubar
    (
     uhcenter() + uvmargin(5)
     + ulabel(UFont::bold + "Background: ")
     + ubutton( UBgcolor::black + "Black" + uset(&bg, UColor::black))
     + ubutton( UBgcolor::white + "White" + uset(&bg, UColor::white))
     + ubutton( UBgcolor::yellow+ "Yellow"+ uset(&bg, UColor::yellow))
     + ubutton( UBgcolor::red   + " Red " + uset(&bg, UColor::red))
     + ubutton( UBgcolor::green + "Green" + uset(&bg, UColor::green))
     + ucombobox(wallpapers)
     );

  UBox& fg_panel = ubar
    (
     uhcenter() + uvmargin(5)
     + ulabel(UFont::bold + "Foreround: ")
     + ubutton( UBgcolor::black + "Black" + uset(&fg, UColor::black))
     + ubutton( UBgcolor::white + "White" + uset(&fg, UColor::white))
     + ubutton( UBgcolor::yellow+ "Yellow"+ uset(&fg, UColor::yellow))
     + ubutton( UBgcolor::red   + " Red " + uset(&fg, UColor::red))
     + ubutton( UBgcolor::green + "Green" + uset(&fg, UColor::green))
     );

  addlist
    (
     uwidth(400) + uheight(300)
     + utop()    + fg_panel
     // canvas is shown twice
     + uvflex()  + uscrollpane(canvas) + uscrollpane(canvas)
     + ubottom() + bg_panel
     );
  }

/* ==================================================== [Elc:03] ======= */
/* ==================================================== ======== ======= */
// this version can manage several canvas views,
// several event flows and several displays

Drawing::PointList* Drawing::getPointList(int flow_id, int disp_id) {
  FlowList* flows = null;

  if (disp_id >= (int)disp_list.size()) {
    for (int k = disp_list.size(); k <= disp_id; k++)
      disp_list.push_back(new FlowList());
  }
  flows = disp_list[disp_id];

  if (flow_id >= (int)flows->size()) {
    for (int k = flows->size(); k <= flow_id; k++)
      flows->push_back(new PointList());
  }
  return (*flows)[flow_id];
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

void Drawing::repaintGraphics(UEvent& e) {
  UGraph g(e);
  g.setColor(fg);

  for (unsigned int d = 0; d < disp_list.size(); d++) {

    FlowList* flows = disp_list[d];
    for (unsigned int f = 0; f < flows->size(); f++) {

      PointList* points = (*flows)[f];
      for (unsigned int p = 1; p < points->size(); p++)
	g.drawLine((*points)[p-1].x, (*points)[p-1].y, 
		   (*points)[p].x, (*points)[p].y);
    }
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

void Drawing::mousePressed(UEvent& e) {

  // retreive the list of points that corresponds to appropriate
  // event flow and display
  PointList* points = getPointList(e.getFlowID(), e.getDispID());

  // resets the vector of points. should be done before canvas.update()
  // as this will fire the repaintGraphics() callback
  points->clear();

  // repaints all canvas views
  canvas.update(UUpdate::paint);

  // retreives the views of the canvas
  delete view_list;
  view_list = null;
  view_list = canvas.getViews(view_count);

  // add the starting point to the list of points
  points->push_back(Point(e.getX(), e.getY()));
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

void Drawing::mouseReleased(UEvent& e) {
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

void Drawing::mouseDragged(UEvent& e) {
  Point new_point(e.getX(), e.getY());

  // retreive the list of points that corresponds to appropriate
  // event flow and display
  PointList* points = getPointList(e.getFlowID(), e.getDispID());

  // draws graphics on all canvas views
  //
  for (int k = 0; k < view_count; k++) {
    UGraph g(view_list[k]);
    g.setColor(fg);

    // draw a line between the last point in the list and new_point
    int last_point = points->size() - 1;
    g.drawLine((*points)[last_point].x, (*points)[last_point].y,
	       new_point.x, new_point.y);
  }

  // add new_point to the list of points
  points->push_back(new_point);
}

/* ==================================================== [Elc:03] ======= */
/* ==================================================== ======== ======= */

