/* ==================================================== ======== ======= *
 *
 *  lensmap.cpp
 *  Ubit Project [Elc::2003]
 *  Author: Eric Lecolinet
 *
 *  Part of the Ubit Toolkit: A Brick Construction Game Model for Creating GUIs
 *
 *  (C) 1999-2003 Eric Lecolinet @ ENST Paris
 *  WWW: http://www.enst.fr/~elc/ubit   Email: elc@enst.fr (subject: ubit)
 *
 * ***********************************************************************
 * COPYRIGHT NOTICE : 
 * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY AND WITHOUT EVEN THE 
 * IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 
 * YOU CAN REDISTRIBUTE IT AND/OR MODIFY IT UNDER THE TERMS OF THE GNU 
 * GENERAL PUBLIC LICENSE AS PUBLISHED BY THE FREE SOFTWARE FOUNDATION; 
 * EITHER VERSION 2 OF THE LICENSE, OR (AT YOUR OPTION) ANY LATER VERSION.
 * SEE FILES 'COPYRIGHT' AND 'COPYING' FOR MORE DETAILS.
 * ***********************************************************************
 *
 * ==================================================== [Elc:03] ======= *
 * ==================================================== ======== ======= */

#include <iostream>
#include "lensmap.hpp"
using namespace std;

// NOTE that flags MUST be declared as consts
const UFlag SiteFlag, MuseumFlag, StoreFlag, StationFlag;
const UFlag ImageFlag, DetailFlag;

extern MapElem *station_data, *store_data, *museum_data, *site_data;

/* ==================================================== ======== ======= */

int main(int argc, char *argv[]) {

  UConf conf(argc, argv);
  //conf.double_buffering = true;    // double buffering
  //conf.transp_scrollbars = true;   // transparent scrollbars

  UAppli appli(conf);   // creates the application context.

  // where the appli must search images (if no absolute path is provided)
  appli.setImaPath("../images");

  // create the main frame and add it to the UAppli
  Lensmap lensmap;
  UFrame frame(lensmap);
  appli.add(frame);

  // show the main frame
  frame.show();

  // set scrollbars in the middle of the scrollpane
  lensmap.scene_spane.getHScrollbar()->setValue(50.);
  lensmap.scene_spane.getVScrollbar()->setValue(50.);
  
  // opens clones on alternate display
  for (int k = 1; k < argc; k++) {
    UDisp* disp = appli.openDisp(argv[k]);

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

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

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

Lensmap::Lensmap() {

  UBox& toolbar = uhbox
    (
     UBgcolor::white + uhspacing(9) + uvmargin(5) + uhmargin(5)
     //+ uitem(UPix::windows + "Overview" + umenu(uscale(-6) + scene_container))
     + uitem(UPix::edit + "Notes"    + ucall(this, 0, &Lensmap::addLens))
     + uitem(UPix::windows +"Portal" + ucall(this, 1, &Lensmap::addLens))
     + uitem(UPix::colors + "Lens"   + ucall(this, 2, &Lensmap::addLens))
     + uhflex() + ulabel("")
     + uright()
     + uitem(UMode::canSelect + UOn::select / UPix::leftarrow
	     + UOn::unselect / UPix::rightarrow
	     + "Controls"
	     + UOn::select / ushow(&controls, true)
	     + UOn::unselect / ushow(&controls, false)
	     )
     );

  /* === layers ======================================== ======== ======= */

  UGroup& stores_layer =
    makeElems(store_data,
              StoreFlag,
              UFont::bold 
              + ualpha(0.35) + UBgcolor::blue
              + UOn::enter / UBgcolor::black
               + UColor::black + UOn::enter / UColor::red
              + UBorder::none
              );
  
  UGroup& museums_layer =
    makeElems(museum_data,
              MuseumFlag,
              UFont::bold + UBorder::none
              + UColor::red + UOn::enter / UColor::white
              + UBgcolor::none + UOn::enter / UBgcolor::black
              );

  UGroup& sites_layer =
    makeElems(site_data,
              SiteFlag,
              UFont::bold + ualpha(0.75)
              + UColor::navy + UOn::enter/UColor::yellow
              + UBgcolor::none + UOn::enter/UBgcolor::teal
              + UBorder::none
              );

  UGroup& stations_layer =
    makeElems(station_data,
              StationFlag,
              UFont::bold
              + UColor::white
              + UBgcolor::orange + UOn::enter / UBgcolor::red
              );
  
  /* === scene ========================================== ======== ======= */

  UFlagdef& fdef1 = uflagdef();
  UFlagdef& fdef2 = uflagdef(ImageFlag);  // default: shows images
  UFlagdef& fdef3 = uflagdef();

  scene.addlist
    (
     uheight(700) + uwidth(800) + scene_scale

     // GLOBAL Flag Defs
     // (NOTE: these defs are different from the local defs of the lenses)
     + fdef1 + fdef2 + fdef3

     // background layer = the physical map
     //+ upix("paris-metro.gif") bizarrement: tres long dans certains cas
     + uima("paris-metro.gif")

     // flagged layers
     + stores_layer + museums_layer + sites_layer + stations_layer
     );

   /* ==================================================== ======== ======= */

  controls.addlist
    (
     UBgcolor::white
     + ulabel(UFont::bold + UColor::orange + "Show")
     + ulistbox(uitem("Nothing" 
		       + UOn::select / usetref(&fdef1, UFlag::none))
		+ uitem("Museums"
			+ UOn::select / usetref(&fdef1, MuseumFlag))
		+ uitem("Sites"
			+ UOn::select / usetref(&fdef1, SiteFlag))
		+ uitem("Dept. Stores"
			+ UOn::select / usetref(&fdef1, StoreFlag))
		+ uitem("Train Stations" 
			+ UOn::select / usetref(&fdef1, StationFlag))
		)

     + ulabel(UFont::bold + UColor::orange + "With")
     + uvbox(UBorder::etchedIn
             + uitem("Images" + UMode::selected
                   + UOn::select / usetref(&fdef2, ImageFlag)
                   + UOn::unselect/uset(&fdef2, UFlag::none)
                   )
             + uitem("Details"
                     + UOn::select / usetref(&fdef3, DetailFlag)
                     + UOn::unselect/usetref(&fdef3, UFlag::none)
                     )
             )

     + ulabel(UFont::bold + UColor::orange + "Lenses")
     + uvbox(UBorder::etchedIn + UBgcolor::white
	     + uitem("Notes"  + ucall(this, 0, &Lensmap::addLens))
	     + uitem("Portal" + ucall(this, 1, &Lensmap::addLens))
	     + uitem("Lens"   + ucall(this, 2, &Lensmap::addLens))
	     )


     + ulabel(UFont::bold + UColor::orange + "More info")
     + uvbox(UBorder::etchedIn+ UBgcolor::white
	     + ulinkbutton("www.pageszoom.com"
			   + ucall( //(void (*)(const char*))system,
				   // (const char*)
				   "netscape -remote 'openURL(http://www.pageszoom.com/pt/villes/paris/fra/acc.htm)' &",
				   system
				   )
			   ))
     );
  controls.show(false);
  
   /* ==================================================== ======== ======= */

  // NOTE: scene_container avoids to create a loop in the instance graph
  // when lenses are added

  scene_container.addlist(uleft() + utop() + scene);

  scene_spane.addlist(uwidth(550) + uheight(500) + scene_container);

  UBox& working_area = uhbox
    (
     uvflex()
     // 'scene_spane'is flexible in vert and horiz directions
     + uhflex() + scene_spane 
     // 'controls'is flexible in vert direction only
     + uright() + controls
     );

   /* ==================================================== ======== ======= */
  // add to MainWin

  this->addlist
    (
     uhflex()
     // flexible in horiz direction only
     + utop() + toolbar
     // 'working_area':flexible in vert and horiz directions
     + uvflex() + working_area
    );
}

/* ==================================================== ======== ======= */
/* ==================================================== ======== ======= */

class ZElem : public UButton  {
public:
  UPos pos;
  ZElem(Lensmap* lm, const UFlag& flag, MapElem& mel, const UArgs& args);
};


ZElem::ZElem(Lensmap* lm, const UFlag& flag, MapElem& mel, const UArgs& args) {

  USymbol* sym = null;
  char* p = strchr(mel.title, '$');
  if (p) {
    switch(p[1]) {
    case '<': sym = &USymbol::left ; break;
    case '>': sym = &USymbol::right ; break;
    case '^': sym = &USymbol::up ; break;
    case 'v': sym = &USymbol::down ; break;
    }
  }
  
  UGroup* t = null;
  if (!p) t = &uhbox(UMode::ignoreEvents + mel.title);
  else {
    int ldeb = p - mel.title + 1;
    char* deb = new char[ldeb];
    strncpy(deb, mel.title, ldeb-1); deb[ldeb-1] = 0;
    
    t = &uhbox(UMode::ignoreEvents + ustr(deb) + sym + ustr(p+2));
    delete [] deb;
  }

  /*
  UGroup& out = uitem
    (
     UOrient::vertical + ualpha(0.35)
     + UBgcolor::none
     + UOn::enter  / UBgcolor::blue
     + UOn::arm    / UBgcolor::orange
     + usrange(-4,99) / ugroup(uscale(-1) + UColor::red + mel.name)
     + usrange(-1,99) / ugroup(uscale(-5) + uima(mel.image))
     + usrange(2,99)  / ustr(mel.details)
     );
  */

  /*
  UGroup& in = ubutton
    (
     UOrient::vertical + args
     + t 
     + ImageFlag / uima(mel.image)
     + DetailFlag / ugroup(UFont::small + mel.details)
     );
*/
  
  pos.set(mel.x, mel.y);
  //addlist(pos + flag / in);
  addlist
  (
   pos
   + UOrient::vertical + args
   + t
   + ImageFlag / uima(mel.image)
   + DetailFlag / ugroup(UFont::small + mel.details)
   );
  
}

/* ==================================================== ======== ======= */

UGroup& Lensmap::makeElems(MapElem tab[], const UFlag& flag, const UArgs& args)
{
  UGroup& grp = ugroup();  
  for (int k = 0; tab[k].name != null; k++) {
    grp.add(flag / new ZElem(this, flag, tab[k], args));
  }
  return grp;
}

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

void Lensmap::addLens(int lenstype) {
  static int x = 300, y = 300;
  x += 25;
  y += 25;

  // NOTE: we add the Lens to scene_container (not scene itself!)
  // in order to avoid to create a loop in the instance graph
  // (as the Lens may also point to the scene)
  Lens *lens = null;

  switch (lenstype) {
  case 0:
    lens = new Note(*this, "Note", x, y);
    break;
  case 1:
    lens = new Portal(*this, "Portal", x, y);
    break;
  case 2:
    lens = new Magic(*this, "Magic Lens", x, y);
    break;
  default:
    return;
  }

  scene_container.add(lens);
}

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

Lens::Lens(Lensmap& _map, const char* title, u_pos x, u_pos y) 
  : map(_map) {

  pos.set(x,y);

  lens_handle.addlist
    (
     ualpha(0.7) + UBgcolor::navy + UColor::white 
     + UFont::bold + " " + ugroup(title)
     + uhflex() 
     + ubox(UMode::ignoreEvents + UBgcolor::none + " ")  // padding
     + uright() 
     + ubox(UBgcolor::none + UOn::arm/UColor::red
	    + "X" + ucall(this, &Lens::deleteLens))
    );

  inbox.addlist
    (
     UBgcolor::none
     + UBorder::etchedIn
     + uhflex() + utop() + lens_handle
     + uvflex()
     );

  addlist
    (
     pos
     + UBgcolor::none
     + uhflex() + uvflex() + inbox
     );

  lens_handle.add(UOn::mpress   / ucall(this, &Lens::pressLens));
  lens_handle.add(UOn::mdrag    / ucall(this, &Lens::dragLens));
  lens_handle.add(UOn::mrelease / ucall(this, &Lens::dragLens));
}

/* ==================================================== ======== ======= */

Note::Note(Lensmap& _map, const char* _title, u_pos x, u_pos y)
  : Lens(_map, _title, x, y) {

  text_color.set(UColor::red);

  inbox.add(uflowbox
      (
       uheight(100) + uwidth(125)
       + ualpha(0.50) + UBgcolor::wheat 
       + uedit()
       + text_color + UFont::bold
       + "you can write your notes here"
       ));

  add(uhbox
      (
       UFont::x_small
       + ubutton(UBgcolor::black + "  "  + usetref(&text_color, UColor::black))
       + ubutton(UBgcolor::navy  + "  "  + usetref(&text_color, UColor::navy))
       + ubutton(UBgcolor::red   + "  "  + usetref(&text_color, UColor::red))
       + ubutton(UBgcolor::green + "  "  + usetref(&text_color, UColor::green))
       ));
}

/* ==================================================== ======== ======= */

Portal::Portal(Lensmap& _map, const char* _title, u_pos x, u_pos y)
  : Lens(_map, _title, x, y) {
    
  inbox.add(uscrollpane 
      (
       uheight(150) + uwidth(180)
       + uhcenter() + uvcenter()
       + map.scene
       ));
}

/* ==================================================== ======== ======= */

Magic::Magic(Lensmap& _map, const char* _title, u_pos x, u_pos y)
  : Lens(_map, _title, x, y) {

  show_topic.set(SiteFlag);
  show_images.set(ImageFlag);
  show_details.set(null);

  pane = &uscrollpane 
    (
     uheight(170) + uwidth(200) + uhmargin(0) + uvmargin(0)
     // NOTE: these are the LOCAL defs of the Lens
     // (different from the global defs of the scene)
     + show_topic + show_images + show_details
      + map.scene
     );
  inbox.add(pane);
  pane->getVScrollbar()->show(false);
  pane->getHScrollbar()->show(false);
    
  URadioSelect& show_mode = uradioSelect();
  UBox& choices = uhbox
    (
     UBgcolor::none + UFont::bold + UColor::orange
     + uitem("Museums" + show_mode
	       + UOn::select / usetref(&show_topic, MuseumFlag))

     + uitem("Sites" + show_mode + UMode::selected
		 + UOn::select / usetref(&show_topic, SiteFlag))

     + uitem("Stores" + show_mode
		 + UOn::select / usetref(&show_topic, StoreFlag))

     + uitem("Stations" + show_mode
	     + UOn::select / usetref(&show_topic, StationFlag))
     );

  add(uhbox(UBorder::none + choices));
}

/* ==================================================== ======== ======= */
/* ==================================================== ======== ======= */

void Lens::deleteLens(UEvent&) {
  // second arg == true ==> will also destroy this object (except if shared)
  map.scene_container.remove(this, true);
}

void Lens::pressLens(UEvent& e) {
  last_x = e.getXwin();
  last_y = e.getYwin();
}

void Lens::dragLens(UEvent& e) {
  // position precedente + ajouter le deplacement
  pos.set(pos.getX() + e.getXwin() - last_x,
	  pos.getY() + e.getYwin() - last_y);
  last_x = e.getXwin();
  last_y = e.getYwin();
}

void Magic::dragLens(UEvent& e) {
  Lens::dragLens(e);

  UPaneView *pane_view = (UPaneView*)pane->getView(0);
  UView *container_view = map.scene_container.getView(0);

  // new position is scene after deltax/y
  u_pos x_in_scene = pane_view->getXwin() - container_view->getXwin() ;
  u_pos y_in_scene = pane_view->getYwin() - container_view->getYwin();

  pane_view->setXScroll(x_in_scene);
  pane_view->setYScroll(y_in_scene);
}

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

MapElem _Stores[] = {
  {
    "Printemps",
    "Printemps",
    "64 bd Haussmann, 9e",
    "printemps.gif",
    314, 200
  },
  {
    "Galeries Lafayette",
    "Gal. Lafayette",
    "40 bd Haussmann, 9e",
    "galeries-lafayettes.gif",
    410, 195
  },
  {
    "Bon Marché",
    "Bon Marché",
    "24 rue de Sèvres, 7e",
    "bon-marche.gif",
    300, 438
  },
  {
    "Forum des Halles",
    "Forum des Halles",
    "M. Halles, 1e",
    "forum-des-halles.gif",
    490, 305
  },
  {
    "Samaritaine",
    "Samaritaine",
    "rue de la Monnaie, 1e",
    "samaritaine.gif",
    405, 350
  },
  {
    "BHV",
    "BHV",
    "140 rue du Bac, 7e",
    "bhv.gif",
    500, 355
  },
  {null, null}
};

MapElem* store_data = _Stores;

/* ==================================================== ======== ======= */

MapElem _Museums[] = {
  {
    "Musée du Louvre", 
    "$v Musée du Louvre", 
    "rue de Rivoli, 1e",
    "louvre.gif",
    417, 327
  },
  {
    "Musée d'Orsay",
    "$^ Musée d'Orsay",
    "7 quai Anatole France, 7e",
    "orsay.gif",
    362, 395,
  },
  {
    "Centre Pompidou",
    "$v Centre Pompidou",
    "19 rue Beaubourg, 4e",
    "pompidou.gif",
    525, 323,
  },
  {
    "Musée Picasso",
    "$< Musée Picasso",
    "5 rue Thorigny, 3e",
    "picasso.gif",
    578, 360
  },
  {
    "Musée Rodin", 
    "Musée Rodin $>",
    "77 rue de Varenne, 7e",
    "rodin.gif",
    245, 384,
  },
  {
    "Thermes de Cluny",
    "$< Thermes de Cluny",
    "6 place Painlevé, 5e",
    "cluny.gif",
    470, 470
  },
  {null, null}
};  

MapElem* museum_data = _Museums;

/* ==================================================== ======== ======= */

MapElem _Sites[] = {
  {
    "Tour Eiffel",
    "$< Tour Eiffel",
    null,
    "tour-eiffel.gif",
    219, 415
  },
  {
    "Notre Dame",
    "$^ Notre Dame",
    null,
    "notre-dame.gif",
    478, 407
  },
  {
    "Sainte Chapelle",
    "Sainte Chapelle",
    null,
    "sainte-chapelle.gif",
    417, 400
  },
  {
    "Sacré Coeur",
    "$^ Sacré Coeur",
    null,
    "sacre-coeur.gif",
    460, 100
  },
  {
    "Mosquée de Paris",
    "$< Mosquée de Paris",
    null,
    "mosquee.gif",
    549, 530
  },
  {
    "Madeleine",
    "$< Madeleine",
    null,
    "madeleine.gif",
    365, 290
  },
  {null, null}
};  

MapElem* site_data = _Sites;

/* ==================================================== ======== ======= */

MapElem _Stations[] = {
  {
    "Gare Austerlitz",
    "Gare Austerlitz",
    null,
    null,
    589, 490
  },
  {
    "Gare de l'Est",
    "Gare de l'Est",
    null,
    null,
    555, 200
  },
  {
    "Gare St Lazare",
    "Gare St Lazare",
    null,
    null,
    364, 210
  },
  {
    "Gare de Lyon",
    "Gare de Lyon",
    null,
    null,
    655, 476
  },
  {
    "Gare Montparnasse",
    "Gare Montparnasse",
    null,
    null,
    369, 519
  },
  {
    "Gare du Nord",
    "Gare du Nord",
    null,
    null,
    553, 155
  },
  {null, null}
};  

MapElem* station_data = _Stations;

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