require 'fox16'

include Fox

class GemBrowser

  #
  # A reference to the currently selected Gem specification, or +nil+
  # if none is selected.
  #
  attr_accessor :currentSpec

  def initialize
    @currentSpec = nil
  end

  #
  # Install the Gem described by the named spec file, and return the
  # Specification for this Gem.
  #
  def install(filename)
    Gem::Installer.new(filename).install
    spec = nil
    cache.each do |n, s|
      spec = s if (File.basename(filename, ".gem") == s.full_name)
    end
    spec
  end

  #
  # Uninstall the Gem described by this specification.
  #
  def uninstall(spec)
    Gem::Uninstaller.new(spec.name).uninstall
  end

  # Return an array of the installed specifications.
  def cache
#   Gem.cache
    Gem::Cache.from_installed_gems
  end
end

class GemInfoPanel < FXVerticalFrame

  attr_reader :browser

  def initialize(p, b, opts)
    # Initialize base class
    super(p, opts)

    @browser = b

    self.backColor = FXRGB(255, 255, 255)

    File.open(File.join("icons", "gem_big.png"), "rb") do |f|
      @bigIcon = FXPNGIcon.new(getApp(), f.read)
    end

    nameLabel = FXLabel.new(self, "", @bigIcon, JUSTIFY_LEFT|JUSTIFY_TOP|ICON_BEFORE_TEXT|LAYOUT_FILL_X)
    nameLabel.backColor = FXRGB(255, 255, 255)
    nameLabel.connect(SEL_UPDATE) { |nameLabel, sel, ptr|
      if currentSpec
	nameLabel.text = specDescription(currentSpec)
	nameLabel.show
      else
	nameLabel.text = ""
	nameLabel.hide
      end
    }

    summaryText = FXText.new(self, nil, 0, TEXT_READONLY|TEXT_WORDWRAP|LAYOUT_FILL_X|LAYOUT_FILL_Y)
    summaryText.backColor = FXRGB(255, 255, 255)
    summaryText.connect(SEL_UPDATE) { |summaryText, sel, ptr|
      theText = currentSpec ? currentSpec.summary : nil
      summaryText.text = theText unless (summaryText.text == theText)
    }
  end

  #
  # Return a reference to the currently selected Gem specification,
  # or +nil+ if none is selected.
  #
  def currentSpec
    browser.currentSpec
  end

  # Return a multiline string (suitable for use with an FXLabel) describing this spec.
  def specDescription(spec)
    [spec.name, "version #{spec.version}", spec.author, spec.email, spec.homepage].join("\n")
  end

  def create
    super
    @bigIcon.create
  end
end

class GemsPanel < FXVerticalFrame

  attr_reader :browser

  def initialize(p, br, opts)
    # Initialize base class
    super(p, opts)
    
    # Save a reference to the browser
    @browser = br

    File.open(File.join("icons", "gem_big.png"), "rb") do |f|
      @bigIcon = FXPNGIcon.new(getApp(), f.read)
    end
    File.open(File.join("icons", "gem_small.png"), "rb") do |f|
      @smallIcon = FXPNGIcon.new(getApp(), f.read)
    end

    @iconList = FXIconList.new(self, nil, 0, LAYOUT_FILL_X|LAYOUT_FILL_Y|ICONLIST_MINI_ICONS|ICONLIST_BROWSESELECT)
    @iconList.connect(SEL_SELECTED) { |sender, sel, ptr|
      browser.currentSpec = itemSpec(ptr)
    }

    # Fill the list with installed specifications.
    browser.cache.each { |name, spec| appendItem(spec) }
    if numItems > 0
      selectItem(0)
      browser.currentSpec = itemSpec(0)
    end
  end

  def appendItem(spec)
    @iconList.appendItem(spec.name, @bigIcon, @smallIcon, spec)
    if numItems == 1
      selectItem(0)
      browser.currentSpec = itemSpec(0)
    end
  end

  def currentItem
    @iconList.currentItem
  end

  #
  # Return the specification associated with the item at _index_, or
  # +nil+ if no such item.
  #
  def itemSpec(index)
    (index >= 0) ? getItem(index).data : nil
  end

  def currentItemSpec
    itemSpec(currentItem)
  end

  def getItem(*args)
    @iconList.getItem(*args)
  end
  
  def selectItem(index)
    @iconList.selectItem(index, true)
  end
  
  def numItems
    @iconList.numItems
  end

  def create
    super
    @bigIcon.create
    @smallIcon.create
    self.width = @iconList.font.getTextWidth("MMMMMMMMMMMMM")
  end
end

class GemBrowserWindow < FXMainWindow

  attr_reader :browser

  def initialize(anApp)
    # Initialize base class first
    super(anApp, "Gem Browser", nil, nil, DECOR_ALL)

    # Menu bar
    menubar = FXMenuBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)

    # File menu
    filemenu = FXMenuPane.new(self)
    FXMenuTitle.new(menubar, "&File", nil, filemenu)

    installCmd = FXMenuCommand.new(filemenu, "Install a Gem...")
    installCmd.connect(SEL_COMMAND) { install }

    uninstallCmd = FXMenuCommand.new(filemenu, "Uninstall this Gem")
    uninstallCmd.connect(SEL_COMMAND) { uninstall }
    uninstallCmd.connect(SEL_UPDATE) do |sender, sel, ptr|
      spec = browser.currentSpec
      if spec
        sender.text = "Uninstall #{spec.name}"
        sender.enable
      else
        sender.text  = "Uninstall this Gem"
        sender.disable
      end
    end

    FXMenuCommand.new(filemenu, "&Quit\tCtl-Q").connect(SEL_COMMAND) { quit }

    # Main window interior
    contents = FXVerticalFrame.new(self, LAYOUT_FILL_X|LAYOUT_FILL_Y)
    
    @browser = GemBrowser.new

    # Horizontal splitter with info pane on the left, icon list on the right
    @splitter = FXSplitter.new(contents, LAYOUT_FILL_X|LAYOUT_FILL_Y|SPLITTER_HORIZONTAL|SPLITTER_REVERSED) do |splitter|
      infoPanel = GemInfoPanel.new(splitter, @browser, LAYOUT_FILL_X|LAYOUT_FILL_X|FRAME_SUNKEN|FRAME_THICK)
      @gemsPanel = GemsPanel.new(splitter, @browser, LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_SUNKEN|FRAME_THICK)
    end
    
    # Catch these too
    self.connect(SEL_CLOSE) { quit }
    anApp.addSignal("SIGINT") { quit }
  end

  def install
    FXFileDialog.new(self, "Install Gem") { |dlg|
      dlg.directory = @directory unless @directory.empty?
      dlg.patternList = ["Gem Files (*.gem)", "All Files (*)"]
      if dlg.execute != 0
        @directory = dlg.directory
        begin  
          spec = browser.install(dlg.filename)
	  @gemsPanel.appendItem(spec)
	rescue => ex
	  FXMessageBox.error(self, MBOX_OK, "Installation Failed", ex.to_s) 
	end
      end
    }
  end

  def uninstall
    begin
      browser.uninstall(browser.currentSpec)
    rescue => ex
      FXMessageBox.error(self, MBOX_OK, "Uninstall Failed", ex.to_s)
    end
  end

  def quit
    getApp().reg().writeIntEntry("SETTINGS", "x", x)
    getApp().reg().writeIntEntry("SETTINGS", "y", y)
    getApp().reg().writeIntEntry("SETTINGS", "width", width)
    getApp().reg().writeIntEntry("SETTINGS", "height", height)
    getApp().reg().writeIntEntry("SETTINGS", "listWidth", @splitter.first.next.width)
    getApp().reg().writeStringEntry("SETTINGS", "directory", @directory) if @directory
    getApp().exit(0)
  end

  def create
    # Do base class create
    super

    # Get size, etc. from registry
    xx = getApp().reg().readIntEntry("SETTINGS", "x", 0)
    yy = getApp().reg().readIntEntry("SETTINGS", "y", 0)
    ww = getApp().reg().readIntEntry("SETTINGS", "width", 450)
    hh = getApp().reg().readIntEntry("SETTINGS", "height", 300)
    position(xx, yy, ww, hh)

    # Splitter child sizes
    rightPaneSize = getApp().reg().readIntEntry("SETTINGS", "listWidth", -1)
    if rightPaneSize >= 0
      @splitter.first.next.width = rightPaneSize
    end
    
    # Last directory for Gem files
    @directory = getApp().reg().readStringEntry("SETTINGS", "directory", "")

    # Show it
    show
  end
end

if __FILE__ == $0
  FXApp.new("GemBrowser", "FXRuby") do |theApp|
    GemBrowserWindow.new(theApp)
    theApp.create
    theApp.run
  end
end

