=begin
  $Author: moriq $
  $Date: 2003-07-23 10:51:28 +0900 (豌ｴ, 23 7 2003) $

  This program is free software.
  You can distribute/modify this program under
  the terms of the Ruby Distribute License.

=end

=begin
modulはModuleかClassオブジェクトの事
klassはClassオブジェクトの事 

APOLLO_REGEXPに一致するクラス
(Phi::のようになっている)を
Apollo関連のクラスとみなします。
下の場合、正規表現は /^Phi::|^RDB::|^Direct::|^RGUI::/ です。

階層構造を取得するために
Objectの直下にあるApollo関連のクラス
だけを取り出して、PhiHierarchy#klass_relation 
に入れます。

Objectの直下にあるApolloのクラスを継承した
クラスは当然Apolloのクラスなので、
PhiHierarchy#klass_relation にはいれてません。
(それらのクラスは Phi::TreeView の on_expanding の
イベントハンドラでノードを付加します)

Objectの直下にないApolloのクラスはそのクラスまでの
親子関係を PhiHierarchy#klass_relation に加えています。

継承の関係をすべてHashにすることもできましたが、
Hashのサイズをおさえたかったので、やりませんでした。
=end

begin
  require 'rbbr-lib'
rescue LoadError
  require 'lib/rbbr-lib'
end

class PhiHierarchy
  
  unless PhiHierarchy.const_defined?(:APOLLO_REGEXP)
    APOLLO_REGEXP = /^Phi::|^RDB::|^Direct::|^RGUI::/
  end
  
  def initialize
    @klass_relation = {}
    @apollo_modul_regexp = APOLLO_REGEXP
    apollo_klasses_under_Object, apollo_klasses_not_under_Object = classify_moduls(get_apollo_moduls)
    @klass_relation[Object] = apollo_klasses_under_Object
    
    apollo_klasses_not_under_Object.each do |klass|
      ancestor_klasses = get_super_klasses(klass)
      
      # ancestor_klasses はObjectからklassまでの
      # クラスが順番に入っている。
      # Fileだと[File,IO,Object]のような感じ
      if ancestor_klasses.size != 0 and not( has_apollo_modul_in_super_moduls?(ancestor_klasses[1..-1]) )
	add_klasses_to_relation(@klass_relation, ancestor_klasses)
      end
    end
  end
  
  attr_reader :klass_relation, :apollo_modul_regexp
  
  def get_apollo_moduls
    moduls = []
    ObjectSpace::each_object(Module) do |modul|
      if @apollo_modul_regexp =~ modul.name 
	moduls.push(modul)
      end
    end
    moduls
  end    
  
  # Objectの直下にあるクラスとそうでないクラス(モジュールを含む)に分ける
  def classify_moduls(apollo_moduls)
    object_subklasses = Object.sub_classes
    
    klasses_under_Objcet = apollo_moduls & object_subklasses
    klasses_not_under_Object = 
      (apollo_moduls - object_subklasses).delete_if{|x| Module == x}
    return klasses_under_Objcet, klasses_not_under_Object
  end
  
  def get_super_klasses(klass)
    klass.ancestors - klass.included_modules
  end
  
  # スーパークラスにPhi関連のクラス or モジュールがあるか調べる
  def has_apollo_modul_in_super_moduls?(ancestor_moduls)
    ancestor_moduls.each do |modul|
      if @apollo_modul_regexp =~ modul.name
	return true
      end
    end
    false
  end
  
  def add_klasses_to_relation(relation, klass_arr)
    ( 0..(klass_arr.size-2) ).each do |i|
      parent_klass = klass_arr[i+1]
      child_klass = klass_arr[i]
      if relation[parent_klass].nil?
	relation[parent_klass] = [child_klass]
      else
	unless relation[parent_klass].include?(child_klass)
	  relation[parent_klass].push(child_klass)
	end
      end
    end
  end
  
end




# テスト用
if $0 == __FILE__ 

  def require_library
    libs = [
      'phi',
      'ini',
      'rgui/ui',
      'bitbtn',
      'rdb',
      'comm',
      'jpeg',
      'mci',
      'ole',
      'direct',
      'dialogs',
      'com',
      'prev',
      'editor',
      'internet',
      'fastnet/ftp',
      'fastnet/http',
      'fastnet/socket',
    ]
    
    error_msg = []
    
    libs.each do |name|
      begin
	require name
      rescue LoadError
	error_msg.push($!)
      end
    end
    
    error_msg
  end
  
  msg = require_library
  print msg
  k1 = nil
  require '../bench'
  benchmark('hoge', 10){
  k1 = PhiHierarchy.new
  }
  k1.klass_relation.each do |k,v|
    if k == Object
      #p k
      #p v.sort{|x,y| x.name <=> y.name}
      #print "\n"
      #v.sort{|x,y| x.name <=> y.name}.each do |klass|
	#p klass
      #end
    else
      p k
      p v
    end
  end
end
