#include <iostream>
#include <ubit/ubit.hpp>
#include <ubit/ext/uctlmenu.hpp>
using namespace std;

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

class Edi : public UVbox {
public:
  Edi(const UStr& path, const UArgs& args = UArgs::none);

  UStr path;            //complete filename of the page
  UScrollpane scrollpane;
  UBox*    page;
  UFilebox openbox;     // file box for selecting the page
  UBgcolor bgcolor;     // background color of the page
  UScale   scale;       // scale of the page
  USpinbox spinbox;     // control the scale

  // Callback METHODS (can have 0 to 3 arguments) :

  // sets 'path' according to value entered in filebox
  void setOpenPath() {path.set(openbox.getPath());}

  // loads the file designed by 'fname'
  void openFile(UStr& fname);

  void newDialog();
  class UCtlmenu* newCtlmenu();
};

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

int main(int argc, char* argv[]) {
  // create the application context
  UAppli appli(argc, argv);

  // pathname of the page that will be displayed first (if any)
  UStr path = (argc > 1) ? argv[1] : "./bovary.txt";

  // create the main panel
  Edi edi(path, uwidth(500) + uheight(500));

  // create the main frame window, add it to the appli and show it
  UFrame frame(edi);
  appli.add(frame);
  frame.show();
  
  // open clones on alternate display
  for (int k = 1; k < argc; k++) {
    UDisp* disp = appli.openDisp(argv[k]);

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

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

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

Edi::Edi(const UStr& _path, const UArgs& args) : UVbox(args) {
  path = _path;

  // background color of the page
  bgcolor.set(UBgcolor::white);

  // adds 2 callback methods to the 'openbox' file box
  // NB: for file boxes, UOn::action means "when the OK button is clicked"

  openbox.addlist
    (
     utitle("Open File")
     + UOn::action / ucall(this, &Edi::setOpenPath)
     + UOn::action / ucallref(this, path, &Edi::openFile)
     );

  page = null;

  // open page with path as an argument
  openFile(path);


  UBox& toolbar = ubar
    (
     uvcenter() + uvmargin(1)
     + uleft() 
     + ubutton( UPix::doc + umenu(openbox) )

     + uhflex() 
     // openFile() is called when the RETURN is entered
     + utextfield( path + ucallref(this, path, &Edi::openFile) )
     
     + uright() 
     + ubutton( " Exit " + ucall(0, &UAppli::quit) )
     );


  // the scale will be changed when the value of the spin box is changed
  spinbox.add(UOn::action / usetref(&scale, spinbox.value()));


  UBox& controls = uhbox
    (
     uleft() 
     + "Scale: " + ugroup(UColor::red + spinbox)

     + "     " 
     + ucombobox
     (ulistbox( uitem("first item sets size")
		+ uitem("bbbb  bbbb  bbbbbb")
		+ uitem(UPix::right + UPix::ray + "ccccc cccc ccccc")
		+ uitem(UFont::underline + UFont::italic
			+ "eeeee" + UPix::folder)
		+ ulabel("this is label")
		+ usepar()
		+ uitem(uima("../images/kaiman.gif"))
		+ uitem("xxxxxxx")
		)
      )

     + uhflex() + ulabel()
     + uright() 
     + ubutton(UPix::windows + " Overview " + ucall(this, &Edi::newDialog))
    );

  //================================================================

  this->addlist
    (
     uhflex()
     + utop() + toolbar
     + uvflex() + scrollpane
     + ubottom() + controls
     + newCtlmenu()
     );
}
 
/* ==================================================== [Elc:03] ======= */
/* ==================================================== ======== ======= */

void Edi::newDialog() {
  UDialog& d = udialog
    (
     uhflex() + uvflex() + uwidth(300) + uheight(300)
     + ubox( uhflex() + uvflex() 
	     + uscale(-3)
	     + scrollpane
	     )
     );
  //add the Dialog to the UAppli
  this->add(d);
  d.show(true);
}

/* ==================================================== ======== ======= */
// loads the file designed by 'fname'

void Edi::openFile(UStr& fname) {
  UStr& content = ustr();
  content.readFile(fname);

  // remove and destroy the previous page (if any)
  if (page) scrollpane.remove(page, true);

  // create the new page
  page = &uflowbox(bgcolor + scale 
		   + uhmargin(2) + uvmargin(2)
		   + uedit() + content
		   );
  // add the new page to the scrollpane
  scrollpane.add(page);
}

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

struct ZoomAction : public UCtlmenu::Action {
  static const int SCALE_QUANTUM = 10;
  Edi* mainbox;
  int scale_base, scale_incr;

  ZoomAction(Edi* _mainbox) {mainbox = _mainbox;}

  virtual void select(UEvent&, UCtlmenu&) {
    scale_base = mainbox->spinbox.value().getValue();
    scale_incr = 0;
  }

  virtual void mdrag(UEvent& e, UCtlmenu& c) {
    int new_scale = (e.getXwin() - c.getXselect()) / SCALE_QUANTUM;
    if (new_scale != scale_incr 
	&& scale_base + new_scale < 10
	&& scale_base + new_scale > -10
	) {
      scale_incr = new_scale;
      mainbox->spinbox.setValue(scale_base + scale_incr);
      mainbox->update();
    }
  }
};


struct ScrollAction : public UCtlmenu::Action {
  static const int SCROLL_QUANTUM = 3;
  Edi* mainbox;
  float scroll_base, scroll_incr;

  ScrollAction(Edi* _mainbox) {mainbox = _mainbox;}

  virtual void select(UEvent&, UCtlmenu&) {
    scroll_base = mainbox->scrollpane.getYScroll();
    scroll_incr = 0;
  }

  virtual void mdrag(UEvent& e, UCtlmenu& c) {
    int new_scroll = (e.getYwin() - c.getYselect()) / SCROLL_QUANTUM;
    if (new_scroll != scroll_incr 
	 && scroll_base + new_scroll < 100
	&& scroll_base + new_scroll > 0
	) {
      scroll_incr = new_scroll;
      mainbox->scrollpane.setYScroll(scroll_base + scroll_incr);
    }
  }
};

UCtlmenu* Edi::newCtlmenu() {
  UCtlmenu* cmenu = new UCtlmenu();

  ZoomAction* za = new ZoomAction(this);
  cmenu->setAction(0, uitem(UPix::right), *za);
  cmenu->setAction(4, uitem(UPix::left), *za);

  ScrollAction* sa = new ScrollAction(this);
  cmenu->setAction(2, uitem(UPix::up), *sa);
  cmenu->setAction(6, uitem(UPix::down), *sa);

  // Button3 opens the menu. last arg = true means that the menu is always
  // opened, even if the event is located inside a child of the opener 
  cmenu->autoOpens(*this, UEvent::MButton3, 0, true);

  return cmenu;
}

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