# Wrapper around the elisp 'overlay'.
#
# Represents an overlay and has finder methods for getting overlays
# for more, see http://www.gnu.org/software/emacs/elisp/html_node/Overlays.html
# represents an elisp overlay.  the Overlay class finder methods for getting overlays
#-- IMPORTANT NOTES:
#   start/end have been renamed to left/right respectively
class Overlay

  def initialize(elisp_overlay)
    # raise TypeError.new("argument must be elisp overlay") if overlayp(elisp_overlay)
    @overlay = elisp_overlay
  end

  def self.menu
    "
    - api/
      | Make some text have a style
      @ Overlay.face :underline, :left=>1, :right=>300
      |
      | Remove all overlays
      @ Overlay.delete_all
    "
  end

  # create elisp overlay and wrap it in a ruby object
  def self.make(left, right)
    Overlay.new($el.make_overlay(left, right))
  end

  # returns all overlays that contain at least one character between left and right
  # empty overlays are included
  def self.between(left, right)
    overlays = $el.overlays_in(left, right).to_a
    overlays.map { |o| Overlay.new(o) }
  end

  # returns all overlays for current buffer
  def self.all
    between(View.top, View.bottom)
  end

  # returns all overlays that have left and right exactly
  def self.on(left, right)
    between(left, right).select { |o| o.left == left && o.right == right }
  end

  # returns all overlays that cover pos
  def self.at(pos)
    overlays = $el.overlays_at(pos).to_a
    overlays.map { |o| Overlay.new(o) }
  end

  def self.overlays_with(left, right, properties = {})
    # implementation needed
  end

  def self.find_or_make(left, right)
    overlays = on(left, right)
    if overlays.any?
      puts "warning: more than 1 overlay" if overlays.size > 1
      overlays.first
    else
      make(left, right)
    end
  end

  # maybe subclass NamedOverlay
  # what about uniqueness of name?
  def self.find_overlay_by_name(name)
    # search overlays for all overlays with property name
  end

  def self.remove_overlays
    # implementation needed
  end

  # Code Sample:  Overlay.face :trailing_whitespace, :what=>:line
  # Apply face to region
  def self.face(face, options={})
    left ||= options[:left]
    right ||= options[:right]
    if options[:what] == :line or left.nil?
      left, right = Line.left, Line.right+1
    end

    o = Overlay.find_or_make(left, right)
    o[:face] = face
    o
    nil
  end

  def self.delete_all
    $el.overlays_in(View.top, View.bottom).to_a.each{|o| $el.delete_overlay o}
    nil
  end

  # returns the buffer that overlay belongs to. It returns nil if overlay has been deleted.
  def buffer
    $el.overlay_buffer @overlay
  end

  # returns a copy of the property list.
  def properties
    $el.overlay_properties @overlay
  end

  # returns the position at which overlay starts, as an integer.
  def left
    $el.overlay_start @overlay
  end

  # returns the position at which overlay ends, as an integer.
  def right
    $el.overlay_end @overlay
  end

  # deletes overlay. The overlay continues to exist as a Lisp object,
  # and its property list is unchanged, but it ceases to be attached
  # to the buffer it belonged to, and ceases to have any effect on
  # display.
  # A deleted overlay is not permanently disconnected. You
  # can give it a position in a buffer again by calling move-overlay.
  def delete
    $el.delete_overlay @overlay
  end

  # def move
  # end

  def to_elisp
    @overlay
  end

  # method missing candidate.  name space with 'set' or 'propset'
  def invisible
    $el.overlay_get(@overlay, :invisible)
  end

  def invisible=(arg)
    if arg
      $el.overlay_put(@overlay, :invisible, true)
    else
      $el.overlay_put(@overlay, :invisible, nil)
    end
  end

  def isearch_open_invisible_temporary
    $el.overlay_get(@overlay, :isearch_open_invisible_temporary)
  end

  def isearch_open_invisible_temporary=(arg)
    if arg
      $el.overlay_put(@overlay, :isearch_open_invisible_temporary, true)
    else
      $el.overlay_put(@overlay, :isearch_open_invisible_temporary, nil)
    end
  end

  def []= key, val
    $el.overlay_put(@overlay, key, val)
  end

  def [] key
    $el.overlay_get(@overlay, key)
  end
end
