// references:
//
// http://www.fltk.org/articles.php?L770
// http://seriss.com/people/erco/fltk/#DraggableBoxes
// http://seriss.com/people/erco/fltk/#Fl_Image

#ifndef UI_MISC_H
#define UI_MISC_H 1

#include <FL/Fl.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Scroll.H>
#include <FL/fl_draw.H>
#include <FL/Fl_Color_Chooser.H>
#include <FL/Fl_Shared_Image.H>

#include <stdio.h>
#include <string> // std::string

#include "../grabpng.h"

class ImageBox;
class CrossBox;
class DragBox;
class ScrollArea;

class Enums
{
public:
    
  enum ActionType
  {
    gpDRAG,
    gpRELEASE,
    gpLOAD
  };
  
};

// base class
class __GrabPngUI
{
public:
  virtual void imagebox_cb(ImageBox *o, void *v){}
  virtual void colorpicker_cb(){}
  virtual void zoom_cb(){}
  virtual void window_cb(){}
};



// Self-closing window. Closes when it loses focus.
class PopupWindow : public Fl_Window
{
public:
 
  PopupWindow(int X, int Y, int W, int H, const char *L=0) : Fl_Window(X,Y,W,H,L) { }
  PopupWindow(int X, int Y) : Fl_Window(X,Y) { }
  
protected:
  
  int handle(int e) 
  {
    int ret = Fl_Window::handle(e);
    
    if (e == FL_UNFOCUS) 
    {
      int winleft = this->x();
      int wintop = this->y();
      int winright = winleft + this->w();
      int winbottom = wintop + this->h();
      
      int ex = Fl::event_x_root();
      int ey = Fl::event_y_root();
      
      if (ex < winleft || ex > winright || ey < wintop || ey > winbottom) 
        this->hide();
    }
    return ret;
  }
  
};


// a draggable box
class DragBox : public Fl_Box 
{

public:

  DragBox(int X, int Y, int W, int H, const char *L=0) : Fl_Box(X,Y,W,H,L) { }
  
  Fl_Widget *owner;
  
  Fl_Shared_Image *real_image;
  
protected:

  int handle(int e) 
  {
    static int offset[2] = { 0, 0 };
    int ret = Fl_Box::handle(e);
    Enums::ActionType d = Enums::gpDRAG;  // user data for callback
    Enums::ActionType r = Enums::gpRELEASE;  // user data for callback
    switch ( e ) 
    {
    case FL_PUSH:
      offset[0] = x() - Fl::event_x();    // save where user clicked for dragging
      offset[1] = y() - Fl::event_y();
      return(1);
    case FL_RELEASE:
      owner->do_callback(owner, &r);
      return(1);
    case FL_DRAG: // handle dragging
      // oob check
      if ( Fl::event_x() > owner->w() + owner->x() || Fl::event_x() < owner->x()
          || Fl::event_y() > owner->h() + owner->y() || Fl::event_y() < owner->y() )
        return(1);
      // set position
      position(offset[0]+Fl::event_x(), offset[1]+Fl::event_y()); 
      parent()->redraw();
      owner->do_callback(owner, &d);  // update the text boxes
      return(1);
      
    case FL_DND_ENTER:
    case FL_DND_RELEASE:
    case FL_DND_LEAVE:
    case FL_DND_DRAG:
      return 1;
      
    case FL_PASTE:
      owner->handle(e);
      return 1;
    }
    return(ret);
  }
  
  public:
    
  
  virtual void close_image(Fl_Shared_Image* img)
  {

    if(!img) return; // no image displayed
    
    // The image is shared, release until no more references
    
    if (img->refcount()) {img->release();}
  }
  
  virtual void fit_image()
  {
    Fl_Shared_Image* img = (Fl_Shared_Image*) image();
    image(image()->copy(w(), h()));
    close_image(img);
    redraw();
  }
  
};





// A custom scroller
class ScrollArea : public Fl_Scroll
{

public:

  ScrollArea(int X, int Y, int W, int H) : Fl_Scroll(X,Y,W,H) { }
  
  
protected:

  int handle(int e) 
  {
    //int ret = ;
    switch ( e ) 
    {
    case FL_DND_ENTER:
    case FL_DND_RELEASE:
    case FL_DND_LEAVE:
    case FL_DND_DRAG:
      return 1;
      
    case FL_PASTE:
      parent()->handle(e);
      return 1;
    }
    return Fl_Scroll::handle(e);
  }
  
};




// this guy does most of the work...

class ImageBox : public Fl_Box
{
protected:
  
  bool image_is_png;

  int zoom_level;


public:
  
  bool lockGuides;
  int lastGuides; // remember last guides (screen or sprite) so user can lock them


  ImageBox(int X, int Y, int W, int H, const char *L = 0);

  virtual ~ImageBox();
  
  std::string current_filename;
  
  DragBox *imgBox; // displays the image
  CrossBox *crossBox; // displays a crosshair
  ScrollArea *scrollArea;
  
  int image_alph; // 0 or 1

  // get position of origin (center of cross)
  int origin_x();
  int origin_y();
  
  void center();
  
  // get and set position of image relative to origin 
  int image_x();
  void image_x(int val);
  int image_y();
  void image_y(int val);
  
  // event handling
  int handle(int e);
 
  // overrides
  virtual void resize(int x, int y, int w, int h);
  virtual void color(Fl_Color c);
  
  // file handling
  const char *base_filename();
  void file_next(bool backward = false);
  void file_close();
  bool file_open(const char *fpath);
  void file_save();
  
  //view
  virtual void zoom(int z);
  virtual int zoom(){return zoom_level;}
  
  
  int img_h, img_w;
  int img_x, img_y;
  
protected:


  // drag and drop
  void dnd_open(const char *urls, size_t length);

  void create_png(); // save current file as a png
    
};




// A box with a crosshair
class CrossBox : public Fl_Box 
{

public:

  CrossBox(int X, int Y, int W, int H, const char *L=0) 
      : Fl_Box(X,Y,W,H,L) { }
      
  ImageBox *owner;
  
protected:

  int handle(int e) // pass dnd events to parent
  {
    //int ret = ;
    switch ( e ) 
    {
    case FL_DND_ENTER:
    case FL_DND_RELEASE:
    case FL_DND_LEAVE:
    case FL_DND_DRAG:
      return 1;
      
    case FL_PASTE:
      parent()->handle(e);
      return 1;
    }
    return Fl_Box::handle(e);
  }
  
  void draw() // draw a crosshair
  {
    
    if (owner->current_filename == "")
    { 
      label(owner->label());
      labelcolor(owner->labelcolor());
      Fl_Box::draw();
      return;
    }
    
    label(0);
    
    
    Fl_Box::draw();
    
    
    bool neg;
    
    if (owner->lockGuides) neg = owner->lastGuides < 1;
    else neg = owner->image_x() <= 0 && owner->image_y() <= 0;
    
    int zoom = owner->zoom();
    
    int _w = 320 * zoom;
    int _h = 200 * zoom;
    int bar_h = 32 * zoom;
    int _x = x();
    int _y = y();
    
    int x1 = _x+_w/2; 
    int y1 = _y+_h/2; 
    int x2 = x1+_w-1; 
    int y2 = y1+_h-1; 
    
    if (neg)
    {
      owner->lastGuides = -1;
      // draw top of status bar
      
      fl_color(FL_GREEN);
      fl_line(x1, y2-bar_h, x2, y2-bar_h);
      
      // draw screen border
      
      fl_color(FL_BLUE);
      fl_line(x1, y1, x1, y2);
      fl_line(x1, y1, x2, y1);
      
      fl_line(x1, y2, x2, y2);
      fl_line(x2, y1, x2, y2);
      
      // draw screen cross
      
      fl_line(_x+_w, y1, _x+_w, y2);
      fl_line(x1, _y+_h, x2, _y+_h);
    }
    else
    {
      owner->lastGuides = 1;
      // draw relative positioning cross
      
      fl_color(FL_RED);
      fl_line(x1, _y, x1, _y+_h-1);
      fl_line(_x, y1, _x+_w-1, y1);
    }
    
  }
  
};

#endif
