#!/usr/local/bin/ruby

require 'rubygems'
require 'fox14'
require 'find'
require "yaml"
include Fox



class ProjWindow < FXMainWindow

  # ignore files matching these patterns by default
  $def_exclude= [ /\.obj$/i, /\.o$/i, /\.dll$/i, /\.so$/i, /\.bak$/i, /\.asv$/i, /\.class/ ]
  $def_exclude_dirs= [ /^(CVS|\.svn)$/ ]

  def excluded(f, exc)
    exc.each do |r|
      return true if r.match(f)
    end

    $def_exclude.each do |r|
      return true if r.match(f)
    end
    return nil
  end

  def excluded_dirs(d, exc)
    exc.each do |r|
      return true if r.match(d)
    end

    $def_exclude_dirs.each do |r|
      return true if r.match(d)
    end
    return nil
  end

  # load project specific excludes
  def get_local_exclude(cd)
    ex= {}
    exf= File.join(cd, ".proj_exclude.yaml")
    if File.exists?(exf)
      ex= YAML.load_file(exf)
    end
    exr= {}
    exr[:files]= []
    exr[:dirs]= []
    unless ex[:files].nil?
      ex[:files].each do |r|
        exr[:files] << Regexp.new(r)
      end
    end

    unless ex[:dirs].nil?
      ex[:dirs].each do |r|
        exr[:dirs] << Regexp.new(r)
      end
    end

    puts "Excludes: "
    p exr
    return exr
  end


    def create
        super

        # make sure the icons are created on the server-side
        @folder_open.create
        @folder_closed.create

        show(PLACEMENT_SCREEN)
    end

    def initialize(app)
        @running= false
        @tabs= []
        # Invoke base class initializer first
        super(app, "Project Browser", nil, nil, DECOR_ALL, 0, 0, 350, 1024)

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

        # Construct a horizontal frame to hold the main window's contents
        @contents = FXVerticalFrame.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y, 0, 0, 0, 0, 0, 0, 0, 0)
        @tab_frame= FXTabBook.new(@contents, nil, 0,  LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_RIGHT)
        @tab_frame.connect(SEL_COMMAND) do |sender, sel, ptr|
            #puts "selected tab: #{ptr}"
            @curtree, @curpath= @tabs[ptr]
        end

        button_frame= FXHorizontalFrame.new(@contents, LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|FRAME_THICK|FRAME_RAISED)
        FXButton.new(button_frame, "&Refresh", nil, nil, 0,
            FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT,
            0, 0, 0, 0, 10, 10, 5, 5).connect(SEL_COMMAND) do
                show_project_list(@curpath, true)
            end

        # Exit button
        FXButton.new(button_frame, "&Exit", nil, app, FXApp::ID_QUIT,
            FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_RIGHT,
            0, 0, 0, 0, 10, 10, 5, 5)

        # Construct some icons we'll use
        @folder_open   = makeIcon("minifolderopen.png")
        @folder_closed = makeIcon("minifolder.png")
        @icons= { # Map filetype to icon
            :ruby_file =>  makeIcon("ruby_file.png"),
            :normal => makeIcon("normal_file.png")
        }

        @ntabs= 0
        ARGV.each do |path|
            if File.directory?(path)
                @curpath= path
                show_project_list(@curpath)
            else
                puts "#{path} is not a directory"
            end
        end
        @running= true
    end

    # Convenience function to load & construct an icon
    def makeIcon(filename)
        begin
            filename= File.join( File.dirname(__FILE__), 'icons', filename)
            icon = nil
            File.open(filename, "rb") { |f|
                icon = FXPNGIcon.new(getApp(), f.read)
            }
            icon
        rescue
            raise RuntimeError, "Couldn't load icon: #{filename}"
        end
    end


    def show_project_list(projdir, refresh=false)
        unless refresh
            tab= FXTabItem.new(@tab_frame, "&#{File.basename(File.expand_path(projdir))}", nil)
            frame= FXHorizontalFrame.new(@tab_frame, FRAME_THICK|FRAME_RAISED)
            project_tree = FXTreeList.new(frame, nil, 0, LAYOUT_FILL_X|LAYOUT_FILL_Y|TREELIST_SHOWS_LINES|TREELIST_SHOWS_BOXES|TREELIST_ROOT_BOXES )

            if @running
                tab.create
                frame.create
                project_tree.create
                @tab_frame.setCurrent(@ntabs)
            end

            project_tree.connect(SEL_COMMAND) do |sender, sel, ptr|
                r= ptr.data
                #puts "clicked on path item: #{r.to_s}"
                system("epsilon -add #{r.to_s} > /dev/null")
            end
            @curtree= project_tree
            @tabs[@ntabs]= [project_tree, projdir]
            @ntabs += 1
        else
            project_tree= @curtree
        end

        if show_proj_tree(project_tree, projdir) > 0
          project_tree.expandTree(project_tree.firstItem)
          project_tree.sortItems
        else
          puts "No files found"
          exit(1)
        end
    end

    def show_proj_tree(treewidget, projdir)
      treewidget.clearItems()
      dir= {}
      cnt= 0
      local_exclude= get_local_exclude(projdir)
      Find.find(projdir) do |f|
        dirname = File.dirname(f)
        basename = File.basename(f)
        #puts "#{dirname}: #{basename}"
        Find.prune if excluded_dirs(basename, local_exclude[:dirs])
        if File.directory? f
          cnt+=1
          dir[f]= treewidget.appendItem(dir[dirname], basename, @folder_open, @folder_closed, nil)
          if basename =~ /app|plugins/
            treewidget.expandTree(dir[f])
          end
        else
          next if excluded(f, local_exclude[:files])
          if basename =~ /\.rb$/
            ico= @icons[:ruby_file]
          else
            ico= @icons[:normal]
          end
          treewidget.appendItem(dir[dirname], basename, ico, ico, f)
          cnt+=1
        end
      end
      cnt
    end


    def setupMenu(menubar)
        # File menu
        filemenu = FXMenuPane.new(self)
        FXMenuCommand.new(filemenu, "New\tCtl-N", nil).connect(SEL_COMMAND) {
            dirDiag = FXDirDialog.new(self, "New Project Directory")
            dirDiag.setDirectory(@curpath)
            dirDiag.forceRefresh()
            if (dirDiag.execute != 0)
                @curpath = dirDiag.getDirectory
                show_project_list(@curpath)
            end
        }
        FXMenuCommand.new(filemenu, "Quit\tCtl-Q", nil, getApp(), FXApp::ID_QUIT)
        FXMenuTitle.new(menubar, "&File", nil, filemenu)
    end

end

# Construct a FOX application object
$application = FXApp.new("project_browser", "project_browser")

# Construct the main window
ProjWindow.new($application)

# Create all the windows
$application.create

# Run the application
$application.run
