module Fox
  
  class FXIconItem
    def <=>(otherItem)
      text <=> otherItem.text
    end
  end

  class FXListItem
    def <=>(otherItem)
      text <=> otherItem.text
    end
  end
  
  class FXTreeItem
    def <=>(otherItem)
      text <=> otherItem.text
    end
  end
  
  #
  # Point
  #
  class FXPoint
  
    alias initializeXY initialize
   
    #
    # Returns an initialized FXPoint instance.
    #
    def initialize(*args)
      if args.length == 0
        initializeXY(0, 0)
      elsif args.length == 1
        if args[0].kind_of?(FXSize)
          initializeXY(args[0].w, args[0].h)
        elsif args[0].kind_of?(FXPoint)
          initializeXY(args[0].x, args[0].y)
        else
          raise TypeError, 'expected FXPoint or FXSize'
        end
      else
        initializeXY(args[0], args[1])
      end
    end
    
    def clone
      FXPoint.new(x, y)
    end 
    
    def ==(other)
      if other.kind_of?(FXPoint)
        (self.x == other.x) && (self.y == other.y)
      else
        false
      end
    end
    
    def -@
      FXPoint.new(-self.x, -self.y)
    end
    
    def +(other)
      if other.kind_of?(FXPoint)
        FXPoint.new(self.x + other.x, self.y + other.y)
      elsif other.kind_of?(FXSize)
        FXPoint.new(self.x + other.w, self.y + other.h)
      else
        raise TypeError, 'expected FXPoint or FXSize'
      end
    end
    
    def -(other)
      if other.kind_of?(FXPoint)
        FXPoint.new(self.x - other.x, self.y - other.y)
      elsif other.kind_of?(FXSize)
        FXPoint.new(self.x - other.w, self.y - other.h)
      else
        raise TypeError, 'expected FXPoint or FXSize'
      end
    end
    
    def *(other)
      FXPoint.new(other*self.x, other*self.y)
    end
    
    def /(other)
      FXPoint.new(self.x/other, self.y/other)
    end
  end
  
  #
  # Rectangle
  #
  class FXRectangle
  
    alias initialize4 initialize # :nodoc:
   
    #
    # Returns an initialized FXRectangle instance.
    #
    def initialize(*args) # :nodoc:
      if args.length == 0
        initialize4(0, 0, 0, 0)
      elsif args.length == 2
        if args[0].kind_of?(FXPoint) && args[1].kind_of?(FXSize)
          p = args[0]
          s = args[1]
          initialize4(p.x, p.y, s.w, s.h)
        else
          topleft = args[0]
          bottomright = args[1]
          initialize4(topleft.x, topleft.y, bottomright.x-topleft.x+1, bottomright.y-topleft.y+1)
        end
      else
        xx, yy, ww, hh = args[0], args[1], args[2], args[3]
        initialize4(xx, yy, ww, hh)
      end
    end

    #
    # Return a new FXRectangle instance which is a copy of this one.
    #
    def clone
      FXRectangle.new(x, y, w, h)
    end 

    #
    # Return true if this rectangle is equal to _other_.
    #
    def ==(other)
      if other.kind_of?(FXRectangle)
        (self.x == other.x) && (self.y == other.y) && (self.w == other.w) && (self.h == other.h)
      else
        false
      end
    end
    
    # Point in rectangle?
    def contains?(*args) # :nodoc:
      if args.length == 1
        if args[0].kind_of?(FXPoint)
          contains?(args[0].x, args[0].y)
        elsif args[0].kind_of?(FXRectangle)
          r = args[0]
          x <= r.x && y <= r.y && r.x+r.w<=x+w && r.y+r.h<=y+h
        else
          raise TypeError, 'expected FXPoint or FXRectangle'
        end
      elsif args.length == 2
        xx, yy = args[0], args[1]
        x <= xx && y <= yy && xx < x+w && yy < y+h
      end
    end
    
    #
    # Return true if _other_ (another FXRectangle instance) and this rectangle overlap.
    #
    def overlaps?(other)
      if other.kind_of?(FXRectangle)
        self.x<other.x+other.w && self.y<other.y+other.h && other.x<self.x+self.w && other.y<self.y+self.h
      else
        raise TypeError, 'expected another FXRectangle'
      end
    end
    
    # 
    # Shift the upper left corner of this rectangle by (_dx_, _dy_)
    # and return a reference to the rectangle.
    #
    # ==== Parameters:
    #
    # +dx+::	number of units to move in the x-direction [Integer]
    # +dy+::	number of units to move in the x-direction [Integer]
    #
    def move!(dx, dy)
      self.x = self.x + dx
      self.y = self.y + dy
      self
    end
    
    # Grow by amount
    def grow!(*args) # :nodoc:
      if args.length == 1
        margin = args[0]
        grow!(margin, margin, margin, margin)
      elsif args.length == 2
        hormargin = args[0]
        vermargin = args[1]
        grow!(hormargin, hormargin, vermargin, vermargin)
      else
        leftmargin = args[0]
        rightmargin = args[1]
        topmargin = args[2]
        bottommargin = args[3]
        self.x = self.x - leftmargin
        self.y = self.y - topmargin
        self.w = self.w + (leftmargin + rightmargin)
        self.h = self.h + (topmargin + bottommargin)
      end
      self
    end
    
    # Shrink by amount
    def shrink!(*args) # :nodoc:
      if args.length == 1
        margin = args[0]
        shrink!(margin, margin, margin, margin)
      elsif args.length == 2
        hormargin = args[0]
        vermargin = args[1]
        shrink!(hormargin, hormargin, vermargin, vermargin)
      else
        leftmargin = args[0]
        rightmargin = args[1]
        topmargin = args[2]
        bottommargin = args[3]
        self.x = self.x + leftmargin
        self.y = self.y + topmargin
        self.w = self.w - (leftmargin + rightmargin)
        self.h = self.h - (topmargin + bottommargin)
      end
      self
    end
    
    #
    # Return an FXPoint instance for the top left corner.
    #
    def tl
      FXPoint.new(x, y)
    end
    
    #
    # Return an FXPoint instance for the top right corner.
    #
    def tr
      FXPoint.new(x+w-1, y)
    end
    
    #
    # Return an FXPoint instance for the bottom left corner.
    #
    def bl
      FXPoint.new(x, y+h-1)
    end
    
    #
    # Return an FXPoint instance for the bottom right corner.
    #
    def br
      FXPoint.new(x+w-1, y+h-1)
    end
    
    #
    # Return a new FXRectangle instance which is the union of this
    # rectangle and _other_.
    #
    def union(other)
      if other.kind_of?(FXRectangle)
        xx = [other.x, self.x].min
        ww = [other.x+other.w, self.x+self.w].max - xx
        yy = [other.y, self.y].min
        hh = [other.y+other.h, self.y+self.h].max - yy
        FXRectangle.new(xx, yy, ww, hh)
      else
        raise TypeError, 'expected FXRectangle'
      end
    end
    
    #
    # Return a new FXRectangle instance which is the intersection of this
    # rectangle and _other_.
    #
    def intersect(other)
      if other.kind_of?(FXRectangle)
        xx = [other.x, self.x].max
        ww = [other.x+other.w, self.x+self.w].min - xx
        yy = [other.y, self.y].max
        hh = [other.y+other.h, self.y+self.h].min - yy
        FXRectangle.new(xx, yy, ww, hh)
      else
        raise TypeError, 'expected FXRectangle'
      end
    end
  end
  
  #
  # Size
  #
  class FXSize
  
    alias initializeWH initialize # :nodoc:
    
    #
    # Returns an initialized FXSize instance.
    #
    def initialize(*args) # :nodoc:
      if args.length == 0
        initializeWH(0, 0)
      elsif args.length == 1
        if args[0].kind_of?(FXSize)
          initializeWH(args[0].w, args[0].h)
        else
          raise TypeError, 'expected FXSize'
        end
      else
        initializeWH(args[0], args[1])
      end
    end
    
    #
    # Return a new FXSize instance which is a clone of this one.
    #
    def clone
      FXSize.new(w, h)
    end 
    
    #
    # Returns +true+ if the width and height of the _other_ size
    # are equal to this size's width and height.
    #
    def ==(other)
      if other.kind_of?(FXSize)
        (self.w == other.w) && (self.h == other.h)
      else
        false
      end
    end
    
    #
    # Return a new FXSize instance whose width and height are
    # the negative values of this size's width and height.
    #
    def -@
      FXSize.new(-self.w, -self.h)
    end
    
    #
    # Returns a new size instance obtained by memberwise
    # addition of the _other_ size instance with this
    # one.
    #
    def +(other)
      if other.kind_of?(FXSize)
        FXSize.new(self.w + other.w, self.h + other.h)
      else
        raise TypeError, 'expected FXSize'
      end
    end
    
    #
    # Returns a new size instance obtained by memberwise
    # subtraction of the _other_ size instance from this
    # one.
    #
    def -(other)
      if other.kind_of?(FXSize)
        FXSize.new(self.w - other.w, self.h - other.h)
      else
        raise TypeError, 'expected FXSize'
      end
    end
    
    #
    # Returns a new size instance obtained by memberwise
    # multiplication of the width and height by _value_.
    #
    def *(value)
      FXSize.new(value*self.w, value*self.h)
    end
    
    #
    # Returns a new size instance obtained by memberwise
    # division of the width and height by _value_.
    #
    def /(value)
      FXSize.new(self.w/value, self.h/value)
    end
  end
  
  class FXDataTarget
    #
    # Returns the stringified representation of this
    # data target's value.
    #
    def to_s
      value.to_s
    end
  end
  
  class FXHeader
    #
    # Returns true if the specified header item's arrow points
    # up. Raises IndexError if _index_ is out of bounds.
    #
    def arrowUp?(index)
      if index < 0 || index >= numItems 
        raise IndexError, "header item index out of bounds"
      else
        getArrowDir(index) == Fox::TRUE
      end
    end

    #
    # Returns true if the specified header item's arrow points
    # down. Raises IndexError if _index_ is out of bounds.
    #
    def arrowDown?(index)
      if index < 0 || index >= numItems 
        raise IndexError, "header item index out of bounds"
      else
        getArrowDir(index) == Fox::FALSE
      end
    end

    #
    # Returns true if the specified header item does not display
    # any arrow. Raises IndexError if _index_ is out of bounds.
    #
    def arrowMaybe?(index)
      if index < 0 || index >= numItems 
        raise IndexError, "header item index out of bounds"
      else
        getArrowDir(index) == Fox::MAYBE
      end
    end
  end

  class FXSettings
    #
    # Read a boolean registry entry from the specified _section_ and _key_.
    # If no value is found, the _default_ value is returned.
    #
    def readBoolEntry(section, key, default=false)
      default = default ? 1 : 0
      readIntEntry(section, key, default) != 0
    end
    
    #
    # Write a boolean registry _value_ to the specified _section_ and _key_.
    #
    def writeBoolEntry(section, key, value)
      writeIntEntry(section, key, value ? 1 : 0)
    end
  end
  
  class FXWindow
    #
    # Iterate over the child windows for this window.
    #
    def each_child
      child = self.first
      while child
        yield child
	child = child.next
      end
    end
  end

  #
  # The drag-and-drop data used for colors is a sequence of unsigned short
  # integers, in native byte ordering. Here, we use the 'S' directive for
  # String#unpack (which treats two successive characters as an unsigned short
  # in native byte order) to decode the R, G, B and A values.
  #
  def Fox.fxdecodeColorData(data)
    clr = data.unpack('S4')
    Fox.FXRGBA((clr[0]+128)/257, (clr[1]+128)/257, (clr[2]+128)/257, (clr[3]+128)/257)
  end

  #
  # The drag-and-drop data used for colors is a sequence of unsigned short
  # integers, in native byte ordering. Here, we use the 'S' directive for
  # Array#pack (which treats two successive characters as an unsigned short
  # in native byte order) to encode the R, G, B and A values.
  #
  def Fox.fxencodeColorData(rgba)
    clr = [ 257*Fox.FXREDVAL(rgba), 257*Fox.FXGREENVAL(rgba), 257*Fox.FXBLUEVAL(rgba), 257*Fox.FXALPHAVAL(rgba) ]
    clr.pack('S4')
  end

  #
  # The drag-and-drop data used for clipboard strings (i.e. when the
  # drag type is FXWindow.stringType) is either a null-terminated
  # string (for Microsoft Windows) or a non-null terminated string
  # (for X11). Use this method to convert string data from the
  # clipboard back into a Ruby string.
  #
  def Fox.fxdecodeStringData(data)
    if /mswin/ =~ PLATFORM
      data.chop
    else
      data
    end
  end

  #
  # The drag-and-drop data used for clipboard strings (i.e. when the
  # drag type is FXWindow.stringType) is either a null-terminated
  # string (for Microsoft Windows) or a non-null terminated string
  # (for X11). Use this method to convert Ruby strings into a format
  # appropriate for the current platform.
  #
  def Fox.fxencodeStringData(str)
    if /mswin/ =~ PLATFORM
      str + "\0"
    else
      str
    end
  end

  #
  # FXStreamError is the base class for exceptions which can occur when
  # working with FXStream and its subclasses.
  #
  class FXStreamError < StandardError
    #
    # This is a factory method that takes an FXStreamStatus code
    # as its input and returns the appropriate exception class.
    #
    def FXStreamError.makeStreamError(status)
      case status
	when FXStreamEnd
	  FXStreamEndError
	when FXStreamFull
	  FXStreamFullError
	when FXStreamNoWrite
	  FXStreamNoWriteError
	when FXStreamNoRead
	  FXStreamNoReadError
	when FXStreamFormat
	  FXStreamFormatError
	when FXStreamUnknown
	  FXStreamUnknownError
	when FXStreamAlloc
	  FXStreamAllocError
	when FXStreamFailure
	  FXStreamFailureError
	else
	  FXStreamError
      end
    end
  end
  
  # Tried to read past the end of a stream
  class FXStreamEndError < FXStreamError ; end
  
  # Filled up a stream's internal buffer, or the disk is full
  class FXStreamFullError < FXStreamError ; end
  
  # Unable to open for write
  class FXStreamNoWriteError < FXStreamError ; end
  
  # Unable to open for read
  class FXStreamNoReadError < FXStreamError ; end
  
  # Stream format error
  class FXStreamFormatError < FXStreamError ; end
  
  # Trying to read unknown class
  class FXStreamUnknownError < FXStreamError ; end
  
  # Alloc failed
  class FXStreamAllocError < FXStreamError ; end
  
  # General failure
  class FXStreamFailureError < FXStreamError ; end
  
  class FXDC
    #
    # Draw a circle centered at (_x_, _y_), with specified radius.
    #
    # === Parameters:
    #
    # +x+::	x-coordinate of the circle's center [Integer]
    # +y+::	y-coordinate of the circle's center [Integer]
    # +r+::	radius of the circle, in pixels [Integer]
    #
    # See also #fillCircle.
    #
    def drawCircle(x, y, r)
      drawArc(x-r, y-r, 2*r, 2*r, 0, 360*64)
    end

    #
    # Draw a filled circle centered at (_x_, _y_), with specified radius.
    #
    # === Parameters:
    #
    # +x+::	x-coordinate of the circle's center [Integer]
    # +y+::	y-coordinate of the circle's center [Integer]
    # +r+::	radius of the circle, in pixels [Integer]
    #
    # See also #drawCircle.
    #
    def fillCircle(x, y, r)
      fillArc(x-r, y-r, 2*r, 2*r, 0, 360*64)
    end
  end

  class FXHVec
    def normalize!
      normalized = self.normalize
      0.upto(3) { |idx| self[idx] = normalized[idx] }
      self
    end
  end
end
