/***********************************************************************
 * FXRuby -- the Ruby language bindings for the FOX GUI toolkit.
 * Copyright (c) 2001-2009 by Lyle Johnson. All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * For further information please contact the author by e-mail
 * at "lyle@lylejohnson.name".
 ***********************************************************************/

// Forward declarations
class FXWindow;
class FXIcon;
class FXBitmap;
class FXCursor;
class FXRootWindow;
class FXMainWindow;
class FXPopup;
class FXFont;
class FXDC;
class FXDCWindow;
class FXVisual;
class FXGLVisual;
class FXGLContext;


/// File input modes for addInput
enum FXInputMode {
  INPUT_NONE   = 0,                 /// Inactive
  INPUT_READ   = 1,                 /// Read input fd
  INPUT_WRITE  = 2,                 /// Write input fd
  INPUT_EXCEPT = 4                  /// Except input fd
  };


/// All ways of being modal
enum FXModality {
  MODAL_FOR_NONE,                 /// Non modal event loop (dispatch normally)
  MODAL_FOR_WINDOW,               /// Modal dialog (beep if outside of modal dialog)
  MODAL_FOR_POPUP                 /// Modal for popup (always dispatch to popup)
  };


/// Default cursors provided by the application
enum FXDefaultCursor {
  DEF_ARROW_CURSOR,                     /// Arrow cursor
  DEF_RARROW_CURSOR,                    /// Reverse arrow cursor
  DEF_TEXT_CURSOR,                      /// Text cursor
  DEF_HSPLIT_CURSOR,                    /// Horizontal split cursor
  DEF_VSPLIT_CURSOR,                    /// Vertical split cursor
  DEF_XSPLIT_CURSOR,                    /// Cross split cursor
  DEF_SWATCH_CURSOR,                    /// Color swatch drag cursor
  DEF_MOVE_CURSOR,                      /// Move cursor
  DEF_DRAGH_CURSOR,                     /// Resize horizontal edge
  DEF_DRAGV_CURSOR,                     /// Resize vertical edge
  DEF_DRAGTL_CURSOR,                    /// Resize upper-leftcorner
  DEF_DRAGBR_CURSOR=DEF_DRAGTL_CURSOR,  /// Resize bottom-right corner
  DEF_DRAGTR_CURSOR,                    /// Resize upper-right corner
  DEF_DRAGBL_CURSOR=DEF_DRAGTR_CURSOR,  /// Resize bottom-left corner
  DEF_DNDSTOP_CURSOR,                   /// Drag and drop stop
  DEF_DNDCOPY_CURSOR,                   /// Drag and drop copy
  DEF_DNDMOVE_CURSOR,                   /// Drag and drop move
  DEF_DNDLINK_CURSOR,                   /// Drag and drop link
  DEF_CROSSHAIR_CURSOR,                 /// Cross hair cursor
  DEF_CORNERNE_CURSOR,                  /// North-east cursor
  DEF_CORNERNW_CURSOR,                  /// North-west cursor
  DEF_CORNERSE_CURSOR,                  /// South-east cursor
  DEF_CORNERSW_CURSOR,                  /// South-west cursor
  DEF_HELP_CURSOR,                      /// Help arrow cursor
  DEF_HAND_CURSOR,                      /// Hand cursor
  DEF_ROTATE_CURSOR,                    /// Rotate cursor
  DEF_WAIT_CURSOR                       /// Wait cursor
  };


/// FOX Event 
struct FXEvent {
  FXEvent();
  FXuint      type;           /// Event type
  FXuint      time;           /// Time of last event
  FXint       win_x;          /// Window-relative x-coord
  FXint       win_y;          /// Window-relative y-coord
  FXint       root_x;         /// Root x-coord
  FXint       root_y;         /// Root y-coord
  FXint       state;          /// Keyboard/Modifier state
  FXint       code;           /// Button, Keysym, or mode; DDE Source
  FXString    text;           /// Text of keyboard event
  FXint       last_x;         /// Window-relative x-coord of previous mouse location
  FXint       last_y;         /// Window-relative y-coord of previous mouse location
  FXint       click_x;        /// Window-relative x-coord of mouse press
  FXint       click_y;        /// Window-relative y-coord of mouse press
  FXint       rootclick_x;    /// Root-relative x-coord of mouse press
  FXint       rootclick_y;    /// Root-relative y-coord of mouse press
  FXuint      click_time;     /// Time of mouse button press
  FXuint      click_button;   /// Mouse button pressed
  FXint       click_count;    /// Click-count
  FXbool      moved;          /// Moved cursor since press
  FXRectangle rect;           /// Rectangle
  FXbool      synthetic;      /// True if synthetic expose event
  FXDragType  target;         /// Target drag type being requested
  ~FXEvent();
  };


// FXChore is an opaque struct for FOX; this is just a declaration for SWIG
struct FXChore {
  };


// FXTimer is an opaque struct for FOX; this is just a declaration for SWIG
struct FXTimer {
  };

%rename("hasInputMethod?") FXApp::hasInputMethod() const;
%rename("hasChore?") FXApp::hasChore(FXObject *tgt,FXSelector sel) const;
%rename("hasTimeout?") FXApp::hasTimeout(FXObject *tgt,FXSelector sel) const;
%rename("focusWindow") FXApp::getFocusWindow() const;
%rename("activeWindow") FXApp::getActiveWindow() const;
%rename("scrollBarSize") FXApp::getScrollBarSize() const;
%rename("scrollBarSize=") FXApp::setScrollBarSize(FXint);

/// Application Object
class FXApp : public FXObject {
protected:

  /// Return TRUE when new raw event is available
  virtual bool getNextEvent(FXRawEvent& ev,FXbool blocking=TRUE);

  /// Dispatch raw event
  virtual bool dispatchEvent(FXRawEvent& ev);

public:
  long onCmdQuit(FXObject*,FXSelector,void* PTR_IGNORE);
  long onCmdDump(FXObject*,FXSelector,void* PTR_IGNORE);
  long onCmdHover(FXObject*,FXSelector,void* PTR_IGNORE);

public:

  // Messages applications understand
  enum {
    ID_QUIT=1,    /// Terminate the application normally
    ID_DUMP,      /// Dump the current widget tree
    ID_HOVER,
    ID_LAST
    };

public:

  %extend {
    // Copyright notice for library
    static VALUE copyright() {
      return rb_str_new2((const char *) FXApp::copyright);
    }
  }

public:

  %extend {
    /**
    * Construct application object; the name and vendor strings are used
    * as keys into the registry database for this application's settings
    */
    FXApp(const FXchar* name="Application",const FXchar* vendor="FoxDefault"){
      if(FXApp::instance()){
        rb_raise(rb_eRuntimeError,"attempted to create more than one FXApp instance");
        return 0;
        }
      else{
	      return FXRbApp::constructAndInit(name,vendor);
        }
      }
  }

  /// Get application name
  const FXString& getAppName() const;

  /// Get vendor name
  const FXString& getVendorName() const;

  /// Connection to display; this is called by init()
  bool openDisplay(const FXchar* dpyname=NULL);
  
  /// Close connection to the display
  bool closeDisplay();

  %extend {
    /// Return pointer to display
    unsigned long getDisplay() const {
      return reinterpret_cast<unsigned long>(self->getDisplay());
      }
    }

  /// Is application initialized
  bool isInitialized() const;

  /// Get argument count
  FXint getArgc() const;

  /// Get argument vector
  const FXchar *const *getArgv() const; // FIXME

  /// Return true if input method support
  bool hasInputMethod() const;

  /// Get default visual
  FXVisual* getDefaultVisual() const;

  %extend {
    /// Change default visual
    void setDefaultVisual(FXVisual* vis){
      FXASSERT(vis);
      if(vis->isMemberOf(FXMETACLASS(FXRbVisual))){
        dynamic_cast<FXRbVisual*>(vis)->ownedByApp=TRUE;
        }
      else if(vis->isMemberOf(FXMETACLASS(FXRbGLVisual))){
        dynamic_cast<FXRbGLVisual*>(vis)->ownedByApp=TRUE;
        }
      self->setDefaultVisual(vis);
      }
  }

  /// Get monochrome visual
  FXVisual* getMonoVisual() const;

  /// Get root Window
  FXRootWindow* getRootWindow() const;

  /// Set root Window
  void setRootWindow(FXRootWindow* rt);

  /// Return window at the end of the focus chain
  FXWindow *getFocusWindow() const;

  /// Get the window under the cursor, if any  
  FXWindow *getCursorWindow() const;

  /// Get the active toplevel window, if any
  FXWindow *getActiveWindow() const;

  /// Get current popup window, if any
  FXPopup* getPopupWindow() const;

  /// Find window from id
  FXWindow* findWindowWithId(FXID xid) const;

  /// Find window from root x,y, starting from given window
  FXWindow* findWindowAt(FXint rx,FXint ry,FXID window=0) const;

  /**
  * Add timeout message to be sent to target object in ms milliseconds;
  * the timer fires only once after the interval expires.  The void* ptr
  * is user data which will be passed into the void* ptr of the message
  * handler.  If a timer with the same target and message already exists,
  * it will be rescheduled.
  */
  void addTimeout(FXObject* tgt,FXSelector sel,FXuint ms=1000,void* ptr=NULL);

  /**
  * Remove timeout identified by tgt and sel.
  */
  void removeTimeout(FXObject* tgt,FXSelector sel);

   /**
   * Return TRUE if given timeout has been set
   */
  bool hasTimeout(FXObject *tgt,FXSelector sel) const;

   /**
  * Return, in ms, the time remaining until the given timer fires.
  * If the timer is past due, 0 is returned.  If there is no such
  * timer, infinity (UINT_MAX) is returned.
  */
  FXuint remainingTimeout(FXObject *tgt,FXSelector sel);

  /**
   * Process any timeouts due at this time.
   */
  void handleTimeouts();

  /**
  * Add a idle processing message to be sent to target object when
  * the system becomes idle, i.e. there are no events to be processed.
  * The void* ptr is user data which will be passed into the void* ptr
  * of the message handler. If a chore with the same target and message
  * already exists, it will be rescheduled.
  */
  void addChore(FXObject* tgt,FXSelector sel,void *ptr=NULL);

  /**
  * Remove idle processing message(s) identified by tgt and sel.
  */
  void removeChore(FXObject* tgt,FXSelector sel);

   /**
  * Return TRUE if given chore has been set
  */
  bool hasChore(FXObject *tgt,FXSelector sel) const;

  %extend {
    /**
    * Add signal processing message to be sent to target object when 
    * the signal sig is raised; flags are to be set as per POSIX definitions.
    * When immediate is TRUE, the message will be sent to the target right away;
    * this should be used with extreme care as the application is interrupted
    * at an unknown point in its execution.
    */
    void addSignal(VALUE sigObj,FXObject* tgt,FXSelector sel,FXbool immediate=FALSE,FXuint flags=0){
      const char *s;
      FXint sig;
      switch(TYPE(sigObj)){
        case T_STRING:
          s=StringValuePtr(sigObj);
          sig=FXRbSignalNameToNumber(s);
          if(sig==0) rb_raise(rb_eArgError,"unrecognized signal name `%s'",s);
          break;
        case T_FIXNUM:
          sig=(FXint)NUM2INT(sigObj);
          break;
        default:
          rb_raise(rb_eArgError,"bad signal type %s",rb_class2name(CLASS_OF(sigObj)));
          break;
        }
      self->addSignal(sig,tgt,sel,immediate,flags);
      }

    /// Remove signal message for signal sig
    void removeSignal(VALUE sigObj){
      const char *s;
      FXint sig;
      switch(TYPE(sigObj)){
        case T_STRING:
          s=StringValuePtr(sigObj);
          sig=FXRbSignalNameToNumber(s);
          if(sig==0) rb_raise(rb_eArgError,"unrecognized signal name `%s'",s);
          break;
        case T_FIXNUM:
          sig=(FXint)NUM2INT(sigObj);
          break;
        default:
          rb_raise(rb_eArgError,"bad signal type %s",rb_class2name(CLASS_OF(sigObj)));
          break;
        }
      self->removeSignal(sig);
      }
  }

  %extend {
    /**
    * Add a file descriptor fd to be watched for activity as determined
    * by mode, where mode is a bitwise OR (INPUT_READ, INPUT_WRITE, INPUT_EXCEPT).
    * A message of type SEL_IO_READ, SEL_IO_WRITE, or SEL_IO_EXCEPT will be sent 
    * to the target when the specified activity is detected on the file descriptor.
    */
    bool addInput(VALUE obj,FXuint mode,FXObject *tgt,FXSelector sel){
      FXInputHandle fd;
      FXuint m;
      if(mode&INPUT_READ){
        m=INPUT_READ;
	if(mode&INPUT_EXCEPT) m|=INPUT_EXCEPT;
        fd=FXRbGetReadFileHandle(obj);
	self->addInput(fd,m,tgt,sel);
        }
      if(mode&INPUT_WRITE){
        m=INPUT_WRITE;
	if(mode&INPUT_EXCEPT) m|=INPUT_EXCEPT;
        fd=FXRbGetWriteFileHandle(obj);
	self->addInput(fd,m,tgt,sel);
        }
      return true;
      }

    /**
    * Remove input message and target object for the specified file descriptor
    * and mode, which is a bitwise OR of (INPUT_READ, INPUT_WRITE, INPUT_EXCEPT).
    */
    bool removeInput(VALUE obj,FXuint mode){
      FXInputHandle fd;
      FXuint m;
      if(mode&INPUT_READ){
        m=INPUT_READ;
	if(mode&INPUT_EXCEPT) m|=INPUT_EXCEPT;
        fd=FXRbGetReadFileHandle(obj);
	self->removeInput(fd,m);
        }
      if(mode&INPUT_WRITE){
        m=INPUT_WRITE;
	if(mode&INPUT_EXCEPT) m|=INPUT_EXCEPT;
        fd=FXRbGetWriteFileHandle(obj);
	self->removeInput(fd,m);
        }
      return true;
      }
  }

  /// Return key state
  bool getKeyState(FXuint keysym) const;

  /// Peek to determine if there's an event
  bool peekEvent();

  /// Perform one event dispatch; return true if event was dispatched
  bool runOneEvent(bool blocking=true);

  /**
  * Run the main application event loop until stop() is called,
  * and return the exit code passed as argument to stop().
  */
  FXint run();

  /**
  * Run an event loop till some flag becomes non-zero, and
  * then return.
  */
  FXint runUntil(FXuint& condition); // FIXME

  /**
  * Run event loop while events are available, non-modally.
  * Return when no more events, timers, or chores are outstanding.
  */
  FXint runWhileEvents();

  /**
  * Run event loop while there are events are available in the queue.
  * Returns 1 when all events in the queue have been handled, and 0 when
  * the event loop was terminated due to stop() or stopModal().
  * Except for the modal window and its children, user input to all windows 
  * is blocked; if the modal window is NULL, all user input is blocked.
  */
  FXint runModalWhileEvents(FXWindow* window=NULL);

  /**
  * Run modal event loop, blocking keyboard and mouse events to all windows
  * until stopModal is called.
  */
  FXint runModal();

  /**
  * Run a modal event loop for the given window, until stop() or stopModal() is 
  * called. Except for the modal window and its children, user input to all
  * windows is blocked; if the modal window is NULL all user input is blocked.
  */
  FXint runModalFor(FXWindow* window);

  /**
  * Run modal while window is shown, or until stop() or stopModal() is called. 
  * Except for the modal window and its children, user input to all windows
  * is blocked; if the modal window is NULL all user input is blocked.
  */
  FXint runModalWhileShown(FXWindow* window);

  /**
  * Run popup menu while shown, until stop() or stopModal() is called.
  * Also returns when entering previous cascading popup menu.
  */
  FXint runPopup(FXWindow* window);

  /// True if the window is modal
  bool isModal(FXWindow* window) const;

  /// Return window of current modal loop
  FXWindow* getModalWindow() const;

  /// Return mode of current modal loop
  FXModality getModality() const;

  /**
  * Terminate the outermost event loop, and all inner modal loops;
  * All more deeper nested event loops will be terminated with code equal
  * to 0, while the outermost event loop will return code equal to value.
  */
  void stop(FXint value=0);

  /**
  * Break out of the matching modal loop, returning code equal to value.
  * All deeper nested event loops are terminated with code equal to 0.
  */
  void stopModal(FXWindow* window,FXint value=0);

  /** 
  * Break out of the innermost modal loop, returning code equal to value.
  */
  void stopModal(FXint value=0);

  /// Force GUI refresh
  void forceRefresh();

  /// Schedule a refresh
  void refresh();

  /// Flush pending repaints
  void flush(bool sync=false);

  /**
  * Paint all windows marked for repainting.
  * On return all the applications windows have been painted.
  */
  void repaint();

  /**
   * Return a reference to the registry.  The registry keeps
   * settings and configuration information for an application,
   * which are automatically loaded when the application starts
   * up, and saved when the application terminates.
   */
  FXRegistry& reg();

  /// Register new DND type
  FXDragType registerDragType(const FXString& name) const;

  /// Get drag type name
  FXString getDragTypeName(FXDragType type) const;

  /// Return drag window if a drag operation is in progress
  FXWindow* getDragWindow() const;

  /// Beep
  void beep();

  /// Return application instance
  static inline FXApp* instance();

  %extend {
    /// Change default font
    void setNormalFont(FXFont* font){
      FXASSERT(font);
      if(font->isMemberOf(FXMETACLASS(FXRbFont))){
        dynamic_cast<FXRbFont*>(font)->ownedByApp=TRUE;
	}
      self->setNormalFont(font);
      }
  }

  /// Return default font
  FXFont* getNormalFont() const;

  /// Begin of wait-cursor block; wait-cursor blocks may be nested.
  void beginWaitCursor();

  /// End of wait-cursor block
  void endWaitCursor();

  %extend {
    /// Change to a new wait cursor
    void setWaitCursor(FXCursor *cur){
      FXASSERT(cur);
      if(cur->isMemberOf(FXMETACLASS(FXRbCursor))){
        dynamic_cast<FXRbCursor*>(cur)->ownedByApp=TRUE;
	}
      else if(cur->isMemberOf(FXMETACLASS(FXRbCURCursor))){
        dynamic_cast<FXRbCURCursor*>(cur)->ownedByApp=TRUE;
	}
      else if(cur->isMemberOf(FXMETACLASS(FXRbGIFCursor))){
        dynamic_cast<FXRbGIFCursor*>(cur)->ownedByApp=TRUE;
	}
      self->setWaitCursor(cur);
      }
  }

  /// Return current wait cursor  
  FXCursor* getWaitCursor() const;

  /// Obtain a default cursor
  FXCursor* getDefaultCursor(FXDefaultCursor which) const;

  %extend {
    /// Change default cursor
    void setDefaultCursor(FXDefaultCursor which,FXCursor* cur){
      FXASSERT(cur);
      if(cur->isMemberOf(FXMETACLASS(FXRbCursor))){
        dynamic_cast<FXRbCursor*>(cur)->ownedByApp=TRUE;
	}
      else if(cur->isMemberOf(FXMETACLASS(FXRbCURCursor))){
        dynamic_cast<FXRbCURCursor*>(cur)->ownedByApp=TRUE;
	}
      else if(cur->isMemberOf(FXMETACLASS(FXRbGIFCursor))){
        dynamic_cast<FXRbGIFCursor*>(cur)->ownedByApp=TRUE;
	}
      self->setDefaultCursor(which,cur);
      }
  }

  /**
   * Write a window and its children, and all resources reachable from this
   * window, into the stream store.
   */
  FXbool writeWindow(FXStream& store,FXWindow *window);

  %extend {
    /**
     * Read a window and its children from the stream store, and append
     * it under father; note it is initially not created yet.
     */
    FXWindow* readWindow(FXStream& store,FXWindow*& window,FXWindow* father,FXWindow* owner){
      window=0;
      self->readWindow(store,window,father,owner);
      return window;
      }
  }

  /**
  * Return a reference to the application-wide mutex.
  * Normally, the main user interface thread holds this mutex,
  * insuring that no other threads are modifying data during the
  * processing of user interface messages. However, whenever the
  * main user interface thread blocks for messages, it releases
  * this mutex, to allow other threads to modify the same data.
  * When a new message becomes available, the main user interface
  * thread regains the mutex prior to dispatching the message.
  * Other threads should hold this mutex only for short durations,
  * so as to not starve the main user interface thread.
  */
  FXMutex& mutex();

  /**
  * Change message translator.
  * The new translator will be owned by FXApp.
  */
  void setTranslator(FXTranslator* trans);

  /// Return message translator
  FXTranslator* getTranslator() const;

  /// Obtain application-wide settings
  FXuint getTypingSpeed() const;
  FXuint getClickSpeed() const;
  FXuint getScrollSpeed() const;
  FXuint getScrollDelay() const;
  FXuint getBlinkSpeed() const;
  FXuint getAnimSpeed() const;
  FXuint getMenuPause() const;
  FXuint getTooltipPause() const;
  FXuint getTooltipTime() const;
  FXint getDragDelta() const;
  FXint getWheelLines() const;
  FXint getScrollBarSize() const;

  /// Change application-wide settings
  void setTypingSpeed(FXuint speed);
  void setClickSpeed(FXuint speed);
  void setScrollSpeed(FXuint speed);
  void setScrollDelay(FXuint delay);
  void setBlinkSpeed(FXuint speed);
  void setAnimSpeed(FXuint speed);
  void setMenuPause(FXuint pause);
  void setTooltipPause(FXuint pause);
  void setTooltipTime(FXuint time);
  void setDragDelta(FXint delta);
  void setWheelLines(FXint lines);
  void setScrollBarSize(FXint size);

  /// Obtain default colors
  FXColor getBorderColor() const;
  FXColor getBaseColor() const;
  FXColor getHiliteColor() const;
  FXColor getShadowColor() const;
  FXColor getBackColor() const;
  FXColor getForeColor() const;
  FXColor getSelforeColor() const;
  FXColor getSelbackColor() const;
  FXColor getTipforeColor() const;
  FXColor getTipbackColor() const;
  FXColor getSelMenuTextColor() const;
  FXColor getSelMenuBackColor() const;

  /// Change default colors
  void setBorderColor(FXColor color);
  void setBaseColor(FXColor color);
  void setHiliteColor(FXColor color);
  void setShadowColor(FXColor color);
  void setBackColor(FXColor color);
  void setForeColor(FXColor color);
  void setSelforeColor(FXColor color);
  void setSelbackColor(FXColor color);
  void setTipforeColor(FXColor color);
  void setTipbackColor(FXColor color);
  void setSelMenuTextColor(FXColor color);
  void setSelMenuBackColor(FXColor color);

  /// Dump widget information
  void dumpWidgets() const;

  /// Get number of existing windows
  FXuint getWindowCount() const;

  /// Destroy the application and all reachable resources
  virtual ~FXApp();
  };

DECLARE_FXOBJECT_VIRTUALS(FXApp)
DECLARE_FXAPP_VIRTUALS(FXApp)

#ifdef SWIGRUBY

%rename("threadsEnabled=") FXApp::setThreadsEnabled(FXbool);
%rename("threadsEnabled?") FXApp::threadsEnabled() const;

%extend FXApp {
  // Enable (or disable) support for multithreaded applications
  void setThreadsEnabled(FXbool enabled){
    FXASSERT(self->isMemberOf(FXMETACLASS(FXRbApp)));
    dynamic_cast<FXRbApp*>(self)->setThreadsEnabled(enabled);
    }

  // Are threads enabled?
  FXbool threadsEnabled() const {
    FXASSERT(self->isMemberOf(FXMETACLASS(FXRbApp)));
    return dynamic_cast<const FXRbApp*>(self)->threadsEnabled();
    }

  // Set the amount of time to sleep
  void setSleepTime(FXuint sleepTime){
    FXASSERT(self->isMemberOf(FXMETACLASS(FXRbApp)));
    dynamic_cast<FXRbApp*>(self)->setSleepTime(sleepTime);
    }

  // Get the amount of time to sleep
  FXuint getSleepTime() const {
    FXASSERT(self->isMemberOf(FXMETACLASS(FXRbApp)));
    return dynamic_cast<const FXRbApp*>(self)->getSleepTime();
    }
  }

#endif