#!/usr/bin/ruby

require 'phi'

module Phi

  class TreeNode

    def each_node
      arr = []
      ( 0..(self.count - 1) ).each do |num|
        arr.push( self[num] )
      end
      arr.each do |node|
        yield node
      end
    end

  end

  class TreeNodes

    def each_node
      arr = []
      ( 0..(self.count - 1) ).each do |num|
        arr.push(self[num])
      end
      arr.each do |node|
        yield node
      end
    end

  end

end


# 構築するタイミングをどうするか?
class DirTree < Phi::TreeView

  ROOT = '/'
  
  def initialize(form, com_name, root = ROOT)
    super(form, com_name)
    self.read_only = true

    # DOSの表記
    # C://// を C:/ にする
    # この場合はFile::expand_pathを使うとちょっとまずい
    if /([a-zA-Z]:\/)\/*/ =~ root 
      @root_path = $1
    else
      @root_path = File::expand_path(root)
    end

    @dot_dir_show = false
    @dir_hash = {}
    append_root_and_dirs_under_root(self.items, @root_path)

    self.on_expanding = proc do |tree, allow, expanding_node|
      self.items.update do 
        append_two_level_dirs(self.items, expanding_node)
      end
      allow
    end

  end

  def append_root_and_dirs_under_root(nodes, root_path)
    @root_node = nodes.add(nil, root_path)
    @root_node.data = root_path
    @dir_hash[root_path] = @root_node
    append_one_level_dirs(nodes, @root_node)
  end

  # parent_nodeの一つ下にノードがすでについている状態で
  # parent_nodeの二つ下のノードをつける
  def append_two_level_dirs(nodes, parent_node)
    parent_node.each_node do |child_node|
      child_path = child_node.data


      if has_subdir?(child_path) and child_node.count == 0
        append_one_level_dirs(nodes, child_node)
      else
        next
      end
    end
  end

  def append_one_level_dirs(nodes, parent_node)
    parent_path = parent_node.data
    Dir.entries(parent_path).sort!.each do |name|
      child_path = join_path(parent_path, name)
      if append_dir?(child_path)
        append_dir(nodes, parent_node, child_path)
      end
    end
  rescue
    return false
  end

  def readable_dir?(path)
    if FileTest::directory?(path) and FileTest::readable?(path)
      true
    else
      false
    end
  end

  def append_dir?(full_path)
    filename = File::basename(full_path)
    if @dot_dir_show
      if FileTest::directory?(full_path) and filename != "." and filename != ".." 
        true
      else
        false
      end
    else
      if FileTest::directory?(full_path) and
          filename != "." and filename != ".." and not(filename =~ /^\./) 
        true
      else
        false
      end
    end
  end

  def join_path(parent_path, filename)
    if parent_path == @root_path
      @root_path + filename
    else
      File::join(parent_path, filename)
    end
  end

  def append_dir(nodes, parent_node, child_path)
    child_node = nodes.add_child(parent_node, File::basename(child_path) )
    child_node.data = child_path
    @dir_hash[child_path] = child_node
    return child_node
  end
  
  def has_subdir?(path)
    if File.stat(path).nlink == 2
      false
    else
      true
    end
  end
  
  def root_node
    @root_node
  end

  def cd(path)
    full_path = File::expand_path(path)
    unless FileTest::directory?(full_path)
      return false
    end

    dir_arr = []
    dir_path = full_path
    until @dir_hash.key?(dir_path)
      dir_arr.push(dir_path)
      dir_path = File::dirname(dir_path)
    end
    dir_arr.push(File::dirname(dir_path))

    dir_arr.reverse_each do |path|
      expand(@dir_hash[path])
    end
    return true
  end

end


if $0 == __FILE__
  form = Phi::Form.new
  
  dirtree = DirTree.new(form, :dirtree1)
  dirtree.align = Phi::AL_CLIENT
  form.show
  Phi.mainloop
end
