//#define DJ_DBG
#include <djButtonWidget.h>
#include <djImageWidget.h>
#include <djMenuWidget.h>
#include <djWindow.h>
#include <djApp.h>

using namespace dj;

class myApp : public App {
public:

  myApp(int & argc, char ** argv);
  ~myApp();
  
  // File functions.
  //
  void fileOpen(const Str &);
  void fileClose();
  
  // Window functions.
  //
  void windowFit();
  void windowFullscreen();
  
private:

  // File callbacks.
  //
  DJ_CALLBACK(myApp, fileOpenCallback, const Str &)
  DJ_CALLBACK(myApp, fileOpenCallback, bool)
  DJ_FL_WIDGET_CALLBACK(myApp, fileOpenCallback)
  DJ_CALLBACK(myApp, fileCloseCallback, bool)
  DJ_FL_WIDGET_CALLBACK(myApp, fileCloseCallback)
  DJ_FL_WIDGET_CALLBACK(myApp, exitCallback)
  
  // Window callbacks.
  //
  DJ_CALLBACK(myApp, windowFitCallback, bool)
  DJ_FL_WIDGET_CALLBACK(myApp, windowFitCallback)
  DJ_CALLBACK(myApp, windowFullscreenCallback, bool)
  DJ_FL_WIDGET_CALLBACK(myApp, windowFullscreenCallback)
  
  // View callbacks.
  //
  DJ_CALLBACK(myApp, viewLeftCallback, bool)
  DJ_FL_WIDGET_CALLBACK(myApp, viewLeftCallback)
  DJ_CALLBACK(myApp, viewRightCallback, bool)
  DJ_FL_WIDGET_CALLBACK(myApp, viewRightCallback)
  DJ_CALLBACK(myApp, viewUpCallback, bool)
  DJ_FL_WIDGET_CALLBACK(myApp, viewUpCallback)
  DJ_CALLBACK(myApp, viewDownCallback, bool)
  DJ_FL_WIDGET_CALLBACK(myApp, viewDownCallback)
  DJ_CALLBACK(myApp, zoomInCallback, bool)
  DJ_FL_WIDGET_CALLBACK(myApp, zoomInCallback)
  DJ_CALLBACK(myApp, zoomOutCallback, bool)
  DJ_FL_WIDGET_CALLBACK(myApp, zoomOutCallback)
  DJ_CALLBACK(myApp, zoomResetCallback, bool)
  DJ_FL_WIDGET_CALLBACK(myApp, zoomResetCallback)
  DJ_CALLBACK(myApp, viewFitCallback, bool)
  DJ_FL_WIDGET_CALLBACK(myApp, viewFitCallback)

  Image _image;
  Window * _window;
  ImageViewWidget * _widget;
};

myApp::myApp(int & argc, char ** argv) :
  App("imageView", argc, argv),
  _window(0)
{
  DJ_DBG_FNC("myApp::myApp");
  
  // Widgets.
  //
  _window = new Window(name());
  MenubarWidget * menubar = new MenubarWidget;
  Widget * toolbar = new Widget;
  toolbar->box(FL_THIN_UP_BOX);
  _widget = new ImageViewWidget;
  
  // File widgets.
  //
  menubar->add("&File", 0, 0, 0, FL_SUBMENU);
  menubar->add("File/&Open", FL_CTRL + 'o', fileOpenCallback, this);
  menubar->add(
    "File/Clos&e", FL_CTRL + 'e', fileCloseCallback, this, FL_MENU_DIVIDER
  );
  menubar->add("File/E&xit", FL_CTRL + 'q', exitCallback, this);
  ToolButtonWidget * fileOpenWidget = new ToolButtonWidget("fileOpen");
  fileOpenWidget->tooltip("File open. (key: ctrl + o)");
  ToolButtonWidget * fileCloseWidget = new ToolButtonWidget("fileClose");
  fileCloseWidget->tooltip("File close. (key: ctrl + e)");
  
  // Window widgets.
  //
  menubar->add("&Window", 0, 0, 0, FL_SUBMENU);
  menubar->add("Window/&Fit Image", 'f', windowFitCallback, this);
  menubar->add("Window/F&ullscreen", 'u', windowFullscreenCallback, this);
  ToolButtonWidget * windowFitWidget = new ToolButtonWidget("windowFit");
  windowFitWidget->tooltip("Fit window to image. (key: f)");
  ToolButtonWidget * windowFullscreenWidget =
    new ToolButtonWidget("windowFullscreen");
  windowFullscreenWidget->tooltip("Window fullscreen. (key: ctrl + u)");
  
  // View widgets.
  //
  menubar->add("&View", 0, 0, 0, FL_SUBMENU);
  menubar->add("View/&Left", FL_Left, viewLeftCallback, this);
  menubar->add("View/&Right", FL_Right, viewRightCallback, this);
  menubar->add("View/&Up", FL_Up, viewUpCallback, this);
  menubar->add(
    "View/&Down", FL_Down, viewDownCallback, this, FL_MENU_DIVIDER
  );
  menubar->add("View/Zoom &In", '=', zoomInCallback, this);
  menubar->add("View/Zoom &Out", '-', zoomOutCallback, this);
  menubar->add(
    "View/Zoom R&eset", '0', zoomResetCallback, this, FL_MENU_DIVIDER
  );
  menubar->add("View/&Fit Image", FL_Insert, viewFitCallback, this);
  ToolButtonWidget * viewLeftWidget = new ToolButtonWidget("viewLeft");
  viewLeftWidget->type(ToolButtonWidget::REPEAT);
  viewLeftWidget->tooltip("Move view left. (key: left)");
  ToolButtonWidget * viewRightWidget = new ToolButtonWidget("viewRight");
  viewRightWidget->type(ToolButtonWidget::REPEAT);
  viewRightWidget->tooltip("Move view right. (key: right)");
  ToolButtonWidget * viewUpWidget = new ToolButtonWidget("viewUp");
  viewUpWidget->type(ToolButtonWidget::REPEAT);
  viewUpWidget->tooltip("Move view up. (key: up)");
  ToolButtonWidget * viewDownWidget = new ToolButtonWidget("viewDown");
  viewDownWidget->type(ToolButtonWidget::REPEAT);
  viewDownWidget->tooltip("Move view down. (key: down)");
  ToolButtonWidget * zoomInWidget = new ToolButtonWidget("viewZoomIn");
  zoomInWidget->type(ToolButtonWidget::REPEAT);
  zoomInWidget->tooltip("Zoom in. (key: =)");
  ToolButtonWidget * zoomOutWidget = new ToolButtonWidget("viewZoomOut");
  zoomOutWidget->type(ToolButtonWidget::REPEAT);
  zoomOutWidget->tooltip("Zoom out. (key: -)");
  ToolButtonWidget * zoomResetWidget = new ToolButtonWidget("viewZoomReset");
  zoomResetWidget->tooltip("Reset zoom. (key: 0)");
  ToolButtonWidget * viewFitWidget = new ToolButtonWidget("viewFit");
  viewFitWidget->tooltip("Fit view to image. (key: spacebar)");
  
  // Layout.
  //
  VLayout * layout = new VLayout(_window);
  layout->spacing(0);
  layout->margin(0);
  layout->add(menubar);
  layout->add(toolbar);
  HLayout * toolLayout = new HLayout(toolbar);
  toolLayout->margin(2);
  toolLayout->add(fileOpenWidget);
  toolLayout->add(fileCloseWidget);
  toolLayout->add(windowFitWidget);
  toolLayout->add(windowFullscreenWidget);
  toolLayout->add(viewLeftWidget);
  toolLayout->add(viewRightWidget);
  toolLayout->add(viewUpWidget);
  toolLayout->add(viewDownWidget);
  toolLayout->add(zoomInWidget);
  toolLayout->add(zoomOutWidget);
  toolLayout->add(zoomResetWidget);
  toolLayout->add(viewFitWidget);
  toolLayout->stretchAdd();
  layout->add(_widget);
  layout->stretch(_widget);
  
  // Initialize.
  //
  if (argc > 1) fileOpen(argv[1]);
  
  // Callbacks.
  //
  fileOpenWidget->sig().set(this, fileOpenCallback);
  fileCloseWidget->sig().set(this, fileCloseCallback);
  windowFitWidget->sig().set(this, windowFitCallback);
  windowFullscreenWidget->sig().set(this, windowFullscreenCallback);
  viewLeftWidget->sig().set(this, viewLeftCallback);
  viewRightWidget->sig().set(this, viewRightCallback);
  viewUpWidget->sig().set(this, viewUpCallback);
  viewDownWidget->sig().set(this, viewDownCallback);
  zoomInWidget->sig().set(this, zoomInCallback);
  zoomOutWidget->sig().set(this, zoomOutCallback);
  zoomResetWidget->sig().set(this, zoomResetCallback);
  viewFitWidget->sig().set(this, viewFitCallback);
  _window->callback(exitCallback, this);
  
  _window->show();
  
  valid(true);
}

myApp::~myApp()
{
  DJ_DBG_FNC("myApp::~myApp");
  
  if (_window) delete _window;
}

void myApp::fileOpen(const Str & in)
{
  ImageLoad * load = dj::imageLoad(in);
  if (! load) {
    error(Str("Cannot open file \"%1\".").arg(FileInfo(in).get(-1, false)));
    return;
  }
  load->get(&_image);
  _widget->set(&_image);
  //_widget->viewReset();
  if (! _window->fullscreen()) windowFit();
  else _widget->viewFit();
  _widget->redraw();
}

void myApp::fileOpenCallback(const Str & in)
{
  fileOpen(in);
}

void myApp::fileOpenCallback(bool)
{
  fileBrowser()->sig().set(this, fileOpenCallback);
  fileBrowser()->show();
}

void myApp::fileOpenCallback() { fileOpenCallback(true); }

void myApp::fileClose()
{
  _image.set(Vec2i(), Pixel());
  _widget->set(0);
  _widget->redraw();
}

void myApp::fileCloseCallback(bool) { fileClose(); }
void myApp::fileCloseCallback() { fileCloseCallback(true); }

void myApp::exitCallback() { exit(); }
  
void myApp::windowFit()
{
  _window->size(
    _widget->imageScreenSize() +
    Vec2i(0, _window->sizeHint().y())
  );
  _widget->viewCenter();
}

void myApp::windowFitCallback(bool) { windowFit(); }
void myApp::windowFitCallback() { windowFitCallback(true); }

void myApp::windowFullscreen()
{
  _window->fullscreen(! _window->fullscreen());
  _widget->viewFit();
}

void myApp::windowFullscreenCallback(bool) { windowFullscreen(); }
void myApp::windowFullscreenCallback() { windowFullscreenCallback(true); }

void myApp::viewLeftCallback(bool) { _widget->viewLeft(); }
void myApp::viewLeftCallback() { viewLeftCallback(true); }
void myApp::viewRightCallback(bool) { _widget->viewRight(); }
void myApp::viewRightCallback() { viewRightCallback(true); }
void myApp::viewUpCallback(bool) { _widget->viewUp(); }
void myApp::viewUpCallback() { viewUpCallback(true); }
void myApp::viewDownCallback(bool) { _widget->viewDown(); }
void myApp::viewDownCallback() { viewDownCallback(true); }
void myApp::zoomInCallback(bool) { _widget->viewZoomIn(); }
void myApp::zoomInCallback() { zoomInCallback(true); }
void myApp::zoomOutCallback(bool) { _widget->viewZoomOut(); }
void myApp::zoomOutCallback() { zoomOutCallback(true); }
void myApp::zoomResetCallback(bool) { _widget->viewZoomReset(); }
void myApp::zoomResetCallback() { zoomResetCallback(true); }
void myApp::viewFitCallback(bool) { _widget->viewFit(); }
void myApp::viewFitCallback() { viewFitCallback(true); }

int main(int argc, char ** argv)
{
  DJ_DBG_LOG();
  return myApp(argc, argv).exec();
}
