#!/usr/bin/env ruby

=begin

  rbbr.rb - Ruby Meta-Level Information Browser

  Author: igarashi 
  Date: 2001/01/11 16:46:51

  Copyright (C) 2000-2001 Hiroshi Igarashi

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

=end


=begin

  meta/metainfo.rb - API for Meta-level Information

  Author:  igarashi 
  $Date: 2003-11-24 21:11:22 +0900 (月, 24 11 2003) $

  Copyright (C) 2000 Hiroshi Igarashi

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

=end

=begin
Object#ivar_get, ivar_set
=end
module Kernel
  def ivar_get(name)
    eval("@#{name.to_s}")
  end
  def ivar_set(name, val)
    eval("@#{name.to_s} = val")
  end
end

=begin
Module#inner_name, outer_name, outer
=end
class Module
  def inner_name
    name.split("::")[-1]
  end
  def outer_name
    #name.split("::")[0..-2].join("::")
    name.split("::")[0..-2]
  end
  def outer
    eval(name.split("::")[0..-2].join("::"))
  end
end

=begin
Module#constants_at, included_modules_at
=end
class Module

  unless Module.method_defined?(:constants_at)
    def constants_at
      cs = constants
      included_modules_at.each do |im|
	cs -= im.constants
      end
      cs
    end
  end
  unless Module.method_defined?(:included_modules_at)
    def included_modules_at
      ams = ancestors
      case ams.size # > 0
      when 1 # root modules
	[]
      when 2 # 2nd gen. modules and class Object
	[ams[1]]
      else   # ams.size>2  other modules and classes
	ims = ams
	i = 1
	while i < ams.size
	  m = ams[i]
	  if m.class == Class # [ap-dev:1366] 
	    ims -= m.ancestors
	    break
	  end
	  ims -= m.included_modules_at
	  i += 1
	end
	ims - [self]
      end
    end
  end

end

=begin
Class#constants_at
=end
class Class
  unless Module.method_defined?(:constants_at)
    def constants_at
      if self == Object
	constants
      else
	super - superclass.constants
      end
    end
  end
end

=begin
Module.update_metainfo, root_modules
Module#true_constants, sub_modules
=end
class Module

  class << self

    public
    def update_metainfo
      @modules = []      # [Module]
      @root_modules = [] # [Module]
      @dil = {}          # Module -> Library
      @dil_rev = {}      # Library -> [Module]
      @dim = {}          # Module -> Module  (not needed?)
      @dim_rev = {}      # Module -> [Module]
      @constants = {}    # Module -> [String](constant name)
      # @include = {}     # Module -> [Module] (eq. to Module#included_modules)
      @include_rev = {}  # Module -> [Module]
      # @inherit = {}     # Class -> Class (eq. to Class#superclass)
      @inherit_rev = {}  # Class -> [Class]

      ObjectSpace.each_object(Module) do |m|
	@modules << m
	@include_rev[m] = [] if @include_rev[m].nil?
	ims = m.included_modules_at
	ims.each do |im|
	  @include_rev[im] = [] if @include_rev[im].nil?
	  @include_rev[im] << m
	end
	  
	if Class === m
	  if m == Object
	  else
	    c, sc = m, m.superclass
	    @inherit_rev[c] = [] if @inherit_rev[c].nil?
	    @inherit_rev[sc] = [] if @inherit_rev[sc].nil?
	    @inherit_rev[sc] << c
	  end
	else # m is Module but Class
	  if ims.empty?
	    @root_modules << m
	  end
	end
	#__update_metainfo_constants(m)
      end

      nil
    end

    private
    def __update_metainfo_constants(m)
      @constants[m] = []
      @dim_rev[m] = [] if @dim_rev[m].nil?
      m.constants_at.each do |const_name|
	const = m.const_get(const_name)
	if Module === const
	  @dim[const] = m
	  @dim_rev[m] << const
	else
	  @constants[m] << const_name
	end
      end
      # Object is an exception
      if m == Object
	@dim_rev[m] -= [m]
      end
      nil
    end

    def __eusure_metainfo
      unless @metainfo_initialized
	update_metainfo
	@metainfo_initialized = true
      end
    end

    public
    def root_modules
      __eusure_metainfo
      @root_modules.freeze
      @root_modules
    end

    def __true_constants(m)
      __eusure_metainfo
      __update_metainfo_constants(m) if @constants[m].nil?
      ns = @constants[m]
      ns.freeze
      ns
    end

    def __sub_modules(m)
      __eusure_metainfo
      ms = @include_rev[m]
      ms.freeze
      ms
    end

    def __sub_classes(c)
      __eusure_metainfo
      cs = @inherit_rev[c]
      cs.freeze
      cs
    end

    def __dump_metainfo(port=STDOUT)
      __eusure_metainfo
      port.puts("*** modules ***")
      p(@modules.sort{|x, y| x.name <=> y.name})
      port.puts("*** dim ***")
      @dim_rev.keys.sort{|x, y| x.name <=> y.name}.each do |outer_module|
	inner_modules = @dim_rev[outer_module]
	inner_modules_rep = inner_modules.sort{|x, y| x.name <=> y.name}.inspect
	port.puts("#{outer_module}:: #{inner_modules_rep}")
      end
      port.puts("*** constants ***")
      @constants.keys.sort{|x, y| x.name <=> y.name}.each do |outer_module|
	constants = @constants[outer_module]
	constants_rep = constants.sort.inspect
	port.puts("#{outer_module}:: #{constants_rep}")
      end
      port.puts("*** inclusion ***")
      @include_rev.keys.sort{|x, y| x.name <=> y.name}.each do |included_module|
	including_modules = @include_rev[included_module]
	including_modules_rep =
	  including_modules.sort{|x, y| x.name <=> y.name}.inspect
	port.puts("#{included_module} > #{including_modules_rep}")
      end
      port.puts("*** inheritance ***")
      @inherit_rev.keys.sort{|x, y| x.name <=> y.name}.each do |super_class|
	sub_classes = @inherit_rev[super_class]
	sub_classes_rep = sub_classes.sort{|x, y| x.name <=> y.name}.inspect
	port.puts("#{super_class} > #{sub_classes_rep}")
      end
    end

    def __dump_metainfo2(root, port=STDOUT)
      m = root
      port.print("  " * m.outer_name.size)
      port.puts(m.inner_name)
      @dim_rev[m].sort{|x, y| x.name <=> y.name}.each do |im|
	dump2(im, port)
      end
    end

    private
    def __update_metainfo2
      print("  " * level)
      puts("enter: #{root_module}")
      root_module._constants.sort.collect { |constant_name|
	root_module.const_get(constant_name)
      }.each { |constant|
	if constant.is_a?(Module)
	  traverse(constant, level + 1)
	end
      }
      print("  " * level)
      puts("leave: #{root_module}")
    end

  end

  public
#  def modules_defined_in
#    metainfo[0]
#  end

  def true_constants
    Module.__true_constants(self)
  end

  def sub_modules
    Module.__sub_modules(self)
  end
end

=begin
Class#sub_classes
=end
class Class
  def sub_classes
    Module.__sub_classes(self)
  end
end
