#!/usr/bin/ruby

=begin
TreeNode#data 
に別のオブジェクトを代入する?

data -- path
     -- flag 一度expandしたかどうか

普通にやるとdeleteの関係上ヤバイかも
update_whole_tree 
  存在しなくなったディレクトリを削除
  新たにできたディレクトリを追加


update_root
  rootを削除してrootだけ追加
=end

require 'phi'
require 'jpeg'

def windows?
  if PLATFORM =~ /mswin/
    return true
  else
    return false
  end
end

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
    @root_path = ''
    @dot_dir_show = false
    @dir2node = {}

    update_root(root)

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

  end

  def append_root_and_dirs_under_root(nodes, root_path)
    @root_node = nodes.add(nil, root_path)
    @root_node.data = root_path
    @dir2node[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
    unless readable_dir?(parent_path)
      return false
    end

    if windows?
      dir_paths = win_get_subdir(parent_path)
    else
      dir_paths = unix_get_subdir(parent_path)
    end

    dir_paths.sort.each do |path|
      append_dir(nodes, parent_node, path)
    end
  end

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

  def unix_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 unix_get_subdir(parent_path)
    dir_paths = []
    
    Dir.entries(parent_path).each do |name|
      child_path = File::join(parent_path, name)
      if unix_append_dir?(child_path)
	dir_paths.push(child_path)
      end
    end
    dir_paths
  end

  def win_append_dir?(stat)
    unless stat.directory?
      return false
    else
      full_path = stat.name
      filename = File::basename(full_path)
      if $dot_dir_show
	if filename != "." and filename != ".." 
	  true
	else
	  false
	end
      else
	if filename != "." and filename != ".." and not(filename =~ /^\./) 
	  true
	else
	  false
	end
      end
    end
  end

  def win_get_subdir(parent_path)
    dir_paths = []

    Phi::dos_dir( File::join(parent_path, "*.*") ).each do |stat|
      if win_append_dir?(stat)
	dir_paths.push( File::join(parent_path, stat.name) )
      end
    end
    return dir_paths
  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
    @dir2node[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)
    # windowsでは @root_path が '/' の時に問題がある
    if not( FileTest::directory?(full_path) )
      return false
    elsif (full_path[0..0] != @root_path[0..0]) and windows?
      # ドライブレターが一致しないとき
      return false
    end

    dir_arr = []
    dir_path = full_path

    # すべての場合でこれで良いのか疑問
    until dir_path == @root_path or dir_path == "."
      dir_arr.push(dir_path)
      dir_path = File::dirname(dir_path)
    end

    # 最悪の場合に対する対処
    if dir_path == '.'
      return false
    else
      dir_arr.push(@root_path)
    end

    self.items.update{
      # 配列の先頭のパスが表すノード以外をexpand
      ( 0..(dir_arr.size - 2) ).each do |i|
	num = dir_arr.size - 1 - i
	@dir2node[ dir_arr[num] ].expand(false)
      end
      self.selected = @dir2node[ dir_arr[0] ]
    }
    return true
  end

  def update_root(root = ROOT)
    path = File::expand_path(root)
    unless File::directory?(path)
      @root_path = ROOT
    else
      @root_path = path
    end
    @dir2node.clear
    self.items.update{
      self.items.clear
      append_root_and_dirs_under_root(self.items, @root_path)
    }
  end

  def update_whole_tree

  end

end

class ImageFileList < Phi::ListView

  IMAGE = { 
    "bmp" => "Bitmap",
    "ico" => "Icon",
    "wmf" => "Metafile",
    "jpeg" => "Jpeg",
    "jpg" => "Jpeg",
  }

  def initialize(form, com_name)
    super(form, com_name)
    self.view_style = Phi::VS_REPORT
    self.read_only = true
    
    new_column = self.columns.add
    new_column.caption = 'name'
    new_column.width = 300

    new_column = self.columns.add
    new_column.caption = 'size(byte)'
    new_column.width = 100

    tmp_arr = []
    IMAGE.keys.each do |ext|
      tmp_arr.push('\.' + ext + %q($))
    end
    tmp_str = tmp_arr.join('|')

    @image_ext_regexp = Regexp.new(tmp_str, true)
  end

  def append_by_path(path)
    unless File::directory?(path) and File::readable?(path)
      return false
    end
    
    self.items.update{
      if windows?
	win_append_image_item(path)
      else
	unix_append_image_item(path)
      end
    }
    true
  end
  
  def append(arr)
    unless arr.size == 2
      return false
    end
    
    new_item = self.items.add
    new_item.caption = arr[0]
    new_item.sub_items.add(arr[1])
    new_item
  end

  def unix_append_image_item(path)
    Dir.entries(path).sort!.each do |name|
      child_path = File::join(path, name)
      if FileTest::file?(child_path) and FileTest::readable?(child_path) and @image_ext_regexp =~ name
	item = append([name, File::size(child_path).to_s])
	item.data = child_path
      end
    end
  end

  def win_append_image_item(path)
    name2stat = {}

    Phi::dos_dir( File::join(path, "*.*") ).each do |stat|
      image_path = File::join(path, stat.name)
      if stat.file? and FileTest::readable?(image_path) and @image_ext_regexp =~ stat.name
	name2stat[stat.name] = stat
      end
    end

    self.items.update{
      self.items.clear
      name2stat.keys.sort.each do |name|
	stat = name2stat[name]
	item = append([stat.name, stat.size.to_s])
	item.data = File::join(path, stat.name)
      end
    }
  end

end


class ImageBox < Phi::ScrollBox

  def initialize(form, com_name)
    super(form, com_name)
    @image = Phi::Image.new(self, :image1)
    @image.align = Phi::AL_CLIENT
  end

  attr_reader :image

end


class DriveList < Phi::ComboBox

  attr_accessor :letter, :lists
  
  def initialize(pr, drv)
    super
    @letter = "c"
    @lists = Phi::StringList.new
    @lists.add('a:')

    while @letter.length == 1
      @root = @letter + ":/"
      if File.exists?(@root)
	@lists.add(@letter + ":")
      end
      @letter.succ!
    end
    self.items = @lists
    self.text = "c:"
  end

end


#
# main
#
form = Phi::Form.new
form.width = 600
form.height = 500

dir_panel = Phi::Panel.new(form, :dir_panel)
dir_panel.align = Phi::AL_LEFT
dir_panel.width = 150
drv_list = DriveList.new(dir_panel, :dir_list1)
drv_list.align = Phi::AL_TOP
unless windows?
  drv.enabled = false
end
dirtree = DirTree.new(dir_panel, :dirtree1, 'c:/')
#dirtree = DirTree.new(dir_panel, :dirtree1, Dir.pwd)
dirtree.align = Phi::AL_CLIENT

sep = Phi::Splitter.new(form)
sep.left = dirtree.left + dirtree.width + 1

panel = Phi::Panel.new(form, :panel1)
panel.align = Phi::AL_CLIENT


image_list = ImageFileList.new(panel, :image_list1)
image_list.align = Phi::AL_TOP
sep2 = Phi::Splitter.new(panel)
sep2.align = Phi::AL_TOP
sep2.top = image_list.top + image_list.height + 1
image_box = ImageBox.new(panel, :image_panel1)
image_box.align = Phi::AL_CLIENT


dirtree.on_click = proc do
  unless dirtree.selected.nil?
    image_list.append_by_path(dirtree.selected.data)
  end
end

image_list.on_click = proc do
  unless image_list.selected.nil?
    image_box.image.picture.load(image_list.selected.data)
  end
end

drv_list.on_click = proc do
  root = drv_list.text + '/'
  if FileTest::exist?(root)
    dirtree.update_root(root)
  end
end

form.show
#dirtree.cd("d:/docu/picture")
Phi.mainloop
