#include <LEDA/plane.h>
#include <LEDA/window.h>

#include <LEDA/bitmaps/button32.h>


static window W(600,700,"LEDA Polygon Demo");
static polygon P;
static bool new_poly=false;

static color pcol = grey1;
static int grid_mode = 0;

static window* mwp;

static string msg_str;

static void msg_redraw( window* wp)
{ wp->clear();
  double x = (wp->xmin() + wp->xmax())/2;
  double y = (wp->ymin() + wp->ymax())/2;
  wp->draw_ctext(x,y,msg_str,black);
}

static void message(string s)
{ msg_str = s;
  msg_redraw(mwp);
 }


static void button_tracer(int, BASE_WINDOW* wp,int x, int y, int n, char* s)
{ panel H(s);
  H.text_item(string("\bf %s",s));
  H.text_item("");
  H.text_item(string("Help Text for button %d (%s)",n,s));
  H.text_item("");
  H.button("continue");
  H.open(*(window*)wp);
}



static polygon empty_polygon()
{ polygon Q;
  return Q;
}

static bool draw_poly(const polygon& P)
{ if (P.size() == 0)
  { panel msg("Error");
    msg.text_item("You first have to define a polygon. Please");
    msg.text_item("select `poly' in the window top panel.");
    msg.button("ok");
    msg.open(W);
    return false;
   }
  W.draw_filled_polygon(P,pcol);
  W.draw_polygon(P,black);
  return true;
}



static void A(int, list<point>&, double&, double&, double, double);
static void B(int, list<point>&, double&, double&, double, double);
static void C(int, list<point>&, double&, double&, double, double);
static void D(int, list<point>&, double&, double&, double, double);

static void A(int i, list<point>& L, double& x, double& y, double dx, double dy)
{ if (i > 0)
  { D(i-1,L,x,y,dx,dy); x-=dx; L.append(point(x,y));
    A(i-1,L,x,y,dx,dy); y-=dy; L.append(point(x,y));
    A(i-1,L,x,y,dx,dy); x+=dx; L.append(point(x,y));
    B(i-1,L,x,y,dx,dy);
   }
 }

static void B(int i, list<point>& L, double& x, double& y, double dx, double dy)
{ if (i > 0)
  { C(i-1,L,x,y,dx,dy); y+=dy; L.append(point(x,y));
    B(i-1,L,x,y,dx,dy); x+=dx; L.append(point(x,y));
    B(i-1,L,x,y,dx,dy); y-=dy; L.append(point(x,y));
    A(i-1,L,x,y,dx,dy);
   }
 }



static void C(int i, list<point>& L, double& x, double& y, double dx, double dy)
{ if (i > 0)
  { B(i-1,L,x,y,dx,dy); x+=dx; L.append(point(x,y));
    C(i-1,L,x,y,dx,dy); y+=dy; L.append(point(x,y));
    C(i-1,L,x,y,dx,dy); x-=dx; L.append(point(x,y));
    D(i-1,L,x,y,dx,dy);
   }
 }

static void D(int i, list<point>& L, double& x, double& y, double dx, double dy)
{ if (i > 0)
  { A(i-1,L,x,y,dx,dy); y-=dy; L.append(point(x,y));
    D(i-1,L,x,y,dx,dy); x-=dx; L.append(point(x,y));
    D(i-1,L,x,y,dx,dy); y+=dy; L.append(point(x,y));
    C(i-1,L,x,y,dx,dy);
   }
 }



polygon hilbert(int n) 
{
  double x1, y1, x2, y2;

  int gmode = W.get_grid_mode();

  if (gmode > 0)
     { int d = int((W.xmax() - W.xmin())/gmode) - 1;
       x1 = gmode;   
       y1 = gmode;
       x2 = d*gmode;
       y2 = d*gmode;
      }
  else
     { x1 = W.xmin() + 50;   
       y1 = W.ymin() + 50;
       x2 = W.xmax() - 100;
       y2 = W.ymax() - 100;
      }
 
  double x = x2;
  double y = y2;
  double dx = (x2-x1)/(1<<n);
  double dy = (y2-y1)/(1<<n);

  message("Computing Hilbert Curve");

  list<point> L;
  L.append(point(x+dx,y));
  L.append(point(x,y));
  A(n,L,x,y,dx,dy);
  L.append(point(x+dx,y));

  return L;
}


void poly_def(int i)
{ W.clear();
  if (i > 0) 
     P = hilbert(i);
  else
    { P = empty_polygon();
      message("Pleasse give a simple polygon P.");
      W >> P;
     }
  new_poly = true;
 }
  

void setgrid(int d) { grid_mode = d; }


void redraw(window* wp, double x0, double y0, double x1, double y1)
{ 
  if (P.size() > 0)
  { list<point> clip_P;
    clip_P.append(point(x0,y0));
    clip_P.append(point(x1,y0));
    clip_P.append(point(x1,y1));
    clip_P.append(point(x0,y1));
    list<polygon> L = P.intersection(clip_P);
    polygon Q;
    forall(Q,L)
    { wp->draw_filled_polygon(Q,pcol);
      wp->draw_polygon(Q,black);
     }
   }
 }


int main()
{ 
  int max_c = 1000;

  pcol = W.mono() ? white : grey1;

  panel help_panel("Help Panel");

  help_panel.text_item("This program demonstrates the intersection, union ");
  help_panel.text_item("and inside operation of polygons. First select the");
  help_panel.text_item("`poly' button in the window main panel to define a");
  help_panel.text_item("a simple polygon P. Now you can select one of the ");
  help_panel.text_item("operations `intersect', `union', or `inside' from");
  help_panel.text_item("the `action' menu and define a second polygon Q."); 
  help_panel.text_item("The result of the operation will be displayed.");

  help_panel.button("continue");


  menu poly_menu;
  poly_menu.button("mouse input",0,poly_def);
  poly_menu.button("hilbert(2) ",2,poly_def);
  poly_menu.button("hilbert(3) ",3,poly_def);
  poly_menu.button("hilbert(4) ",4,poly_def);
  poly_menu.button("hilbert(5) ",5,poly_def);
  poly_menu.button("hilbert(6) ",6,poly_def);

  menu grid_menu;
  grid_menu.button("no grid", 0,setgrid);
  grid_menu.button("dist 10",10,setgrid);
  grid_menu.button("dist 20",20,setgrid);
  grid_menu.button("dist 30",30,setgrid);
  grid_menu.button("dist 40",40,setgrid);
  grid_menu.button("dist 50",50,setgrid);

  menu op_menu;

  string poly_hlp;

  poly_hlp += "Hilfe Text fuer Polygonknopf.";
  poly_hlp += "Hilfe Text fuer Polygonknopf.";
  poly_hlp += "Hilfe Text fuer Polygonknopf.";
  poly_hlp += "Hilfe Text fuer Polygonknopf.";
  poly_hlp += "Hilfe Text fuer Polygonknopf.";
  poly_hlp += "Hilfe Text fuer Polygonknopf.";
  poly_hlp += "Hilfe Text fuer Polygonknopf.";
  poly_hlp += "Hilfe Text fuer Polygonknopf.";
  poly_hlp += "Hilfe Text fuer Polygonknopf.";
  poly_hlp += "Hilfe Text fuer Polygonknopf.";

  W.button(32,32,poly_bits,"polygon",   2, poly_menu, poly_hlp);  
  W.button(32,32,grid_bits,"set grid",  6, grid_menu);
  W.button(32,32,intersect_bits,"intersect", 3);
  W.button(32,32,union_bits,"union",    4);
  W.button(32,32,inside_bits,"inside",  5);
  W.button(32,32,help_bits,"help",      1);
  W.button(32,32,exit_bits,"exit",  0);

  //W.buttons_per_line(5);

  W.set_redraw(redraw);

  W.set_node_width(2);
  W.open(window::center,window::center);
  W.init(0,max_c,0,grid_mode);

  window msg_win(W.width()-2,20);
  msg_win.set_bg_color(grey1);
  msg_win.set_redraw(msg_redraw);
  msg_win.open(W,0,W.ypix(W.ymax()));
  mwp = &msg_win;

  //message("Please click on `help' for help."); 

  int but;
  while ((but = W.read_mouse()) != 0)
  {
     if (but > 2) 
          W.init(0,max_c,0,grid_mode); // clears window

    switch (but)  {

    case 1: { help_panel.open(W);
              W.clear();
              draw_poly(P);
              break;
             }

 
    case 2: { if (new_poly)
              { draw_poly(P);
                message(string("area(P) = %.2f",P.area()));
                new_poly = false;
              }
              break;
             }

    case 3: { if ( ! draw_poly(P)) break;
              message("Give a second polygon Q");
              polygon Q;
              W >> Q;
              W << Q;
              message("Computing Intersection");
              list<polygon> L = P.intersection(Q);
              double A = 0;
              polygon R;
              int c = 0;
              forall(R,L) 
              { color col = W.mono() ? black : c+2;
                W.draw_filled_polygon(R,col);
                W.draw_polygon(R,black);
                A += R.area();
                c = (c+1) % 11;
               }
              message(string("P.intersect(Q):  (area = %.2f)",A));
              break;
             }

    case 4: { if ( ! draw_poly(P)) break;
              message("Give a second polygon Q");
              polygon Q;
              W >> Q;
              W << Q;
              message("Computing Union");
              list<polygon> L = P.unite(Q);
              if (L.empty())
              { W.acknowledge("Empty union !");
                break;
               }
              polygon R = L.pop();
              double A = R.area();
              W.draw_filled_polygon(R,red);
              W.draw_polygon(R,black);
              W.set_mode(xor_mode);
              forall(R,L) 
              { A -= R.area();
                W.draw_filled_polygon(R,red);
                W.draw_polygon(R,black);
               }
              W.set_mode(src_mode);
              message(string("P.unite(Q):  (area = %.2f)",A));
              break;
             }
 
    case 5: { if ( ! draw_poly(P)) break;
              int i;
              for(i=0; i<5000; i++)
              { if (i % 10 == 0)
                    message(string("%5d  random points (click to stop)",i));
                if (W.get_button() != NO_BUTTON) break;

                point p(rand_int(0,max_c),rand_int(0,max_c));

                if (P.inside(p)) 
                   W.draw_filled_node(p,red);
                else
                   W.draw_filled_node(p,blue);
               }
              message(string("%5d  random points",i));
              break;
             }

    case 6: { if (P.size() > 0) draw_poly(P);
              break;
             }

    } // switch

  } // while

 return 0;

}
