#!/usr/local/bin/ruby -Ks

def detab(line, tabwidth = 8)
  line = line.dup
  while line.sub!(/\t/) { " " * (tabwidth - $~.begin(0) % tabwidth) }
  end
  line
end


def get_methods(file)
  abs_name = nil
  classes = []
  classInfo = {}
  stack = []

  linum = 0
  open(file) do |s|
    s.each do |line|
      linum += 1
      line.chop!
      line = detab(line)
      if line =~ /^if/
	break
      elsif line =~ /^(\s*)(class|module)\s+([\w_]+)\b(?:\s*<\s*([\w_]+))?/
	space, kmode, name, sname = $1, $2, $3, $4||"Object"
	stack.push(name)
	abs_name = stack.join("::")
	classes.push(abs_name)
	classInfo[abs_name] = Struct.new(
					 :abs_name,
					 :spacesize,
					 :kmode,
					 :name,
					 :sname,
					 :class_methods,
					 :methods,
					 :included,
					 :extended
					 )[
	  abs_name, space.size, kmode, name, sname, [], [], [], []
	]
      elsif line =~ /^(\s*)end\b/
	n = detab($1).size
	m = classInfo[abs_name].spacesize
	if n > m
	elsif n == m
	  unless stack.pop
	    raise "format error (shallow) :#{linum}"
	  else
	    abs_name = stack.join("::")
	  end
	else
	  raise "format error :#{linum} #{n}, #{m}, #{abs_name}"
	end
      elsif /^\s*(?:def|alias)\s+(([\w_?!\[\]+\-*<=>\/%@~^&|.`~]+).*)/ =~ line #/
	line, method_name = $1, $2
	if method_name =~ /\./
	  classInfo[abs_name].class_methods.push [method_name, line]
	else
	  classInfo[abs_name].methods.push [method_name, line]
	end
      elsif /^\s*include\s+([\w_]+)/ =~ line
	classInfo[abs_name].included.push $1
      elsif /^\s*extend\s+([\w_]+)/ =~ line
	classInfo[abs_name].extended.push $1
      end
    end
  end

  if stack.size > 0
    raise "format error (deep) #{stack.inspect} :#{linum}"
  end
  [classes, classInfo]
end

def mkout(filename, o, classes, classinfo)
  s = ""
  classes.each do |abs_name|
    next if abs_name == "Algebra"

    info = classinfo[abs_name]
    s << "= " << info.abs_name << N << N
    s << o.file << N
    s << "* ((|#{File.basename(filename)}|))" << N << N
    
    s << o.superklass << N
    s << "* ((|#{info.sname}|))" << N << N
    
    s << o.included << N << N
    if info.included.empty?
      s << o.none << N
    else
      info.included.each do |m|
	s << "* ((|#{m}|))" << N
      end
    end
    s << N

    s << o.extended << N << N
    if info.extended.empty?
      s << o.none << N
    else
      info.extended.each do |m|
	s << "* ((|#{m}|))" << N
      end
    end
    s << N

    s << o.assoc << N << N
    
    s << o.classmeth << N << N

    if info.methods.empty?
      s << o.none << N
    else
      info.class_methods.each do |name, line|
	u =<<-__EOS__
--- #{line}
    
#{o.example}
    __EOS__
	s << u << "\n"
      end
    end

    s << o.meth << N << N
    if info.methods.empty?
      s << o.none << N
    else
      info.methods.each do |name, line|
	u =<<-__EOS__
--- #{line}

#{o.example}
    __EOS__
	s << u << "\n"
      end
    end
  end
  s << N << N
end

filename = ARGV.shift
tdir = ARGV.shift.sub(/\/$/, '')#/
ja = tdir =~ /-ja$/ ? "ja" : nil

fname = File.basename(filename)
outputfile = tdir + "/" + fname.sub(/\.rb/, ja ? "-ja.rd" : ".rd") #/
indexfile = "index" + (ja ? "-ja" : "")
indexhtml = indexfile + ".html"

if File.exist?(outputfile)
  puts "#{outputfile} is exist. (push RETURN to go or ^C for abort)"
  gets
end

str_fields =<<__EOS__
== File Name:
== SuperClass:
== Included Module:
== Extended Module:
== Associated Functions:
== Class Methods:
== Methods:
    Example: 
(None)
__EOS__

str_fields_ja =<<__EOS__
== ファイル名:
== スーパークラス:
== インクルードしているモジュール:
== エクステンドしているモジュール:
== 関連する関数:
== クラスメソッド:
== メソッド:
    例: 
なし
__EOS__

fields = (ja ? str_fields_ja : str_fields).collect{|s| s.chop}

classes, classinfo = get_methods(filename)

o = Struct.new(:file, :superklass, :included, :extended, :assoc,
	       :classmeth, :meth, :example, :none)[* fields]

N = "\n"
n0 = 70

s = "#" + "#" * n0 + "#\n"
s << "#" + " " * n0 + "#\n"
s << "#" + (k = "   #{filename}") + " "*(n0-k.size) + "#\n"
s << "#" + " " * n0 + "#\n"
s << "#" + "#" * n0 + "#\n"
s << <<__EOS__
=begin
__EOS__

s << "[((<#{indexfile}|URL:#{indexhtml}>))]" << " " << N
s << classes.find_all{|name| name != "Algebra"}.collect{|name|
"((<#{name}>))"}.join("\n/\n")
s << N << N

s << mkout(filename, o, classes, classinfo)
s << "=end" << N << N

puts "create #{outputfile}"
open(outputfile, "w") do |t|  t.puts s; end
