#!/usr/bin/env ruby
#
# create built-in class table.
#
#  Copyright (C) 2015- Kyushu Institute of Technology.
#  Copyright (C) 2015- Shimane IT Open-Innovation Center.
#
#  This file is distributed under BSD 3-Clause License.
#
# (usage)
# ruby make_class_table.rb [option] src_files...
#
#  -o output filename.
#  -v verbose
#

require "optparse"
require_relative "common_sub"


##
# verbose print
#
def vp( s, level = 1 )
  STDERR.puts s  if $opts[:v] >= level
end


##
# parse command line option
#
def get_options
  ret = {:v=>0}
  opt = OptionParser.new

  opt.banner = "Usage: #{File.basename($0)} [options] src_files..."
  opt.on("-o output file") {|v| ret[:out_file] = v }
  opt.on("-v", "--verbose", "verbose mode") {|v| ret[:v] += 1 }

  opt.parse!(ARGV)
  return ret

rescue OptionParser::MissingArgument =>ex
  STDERR.puts ex.message
  return nil
end


##
# read *.c file and extract classes
#
def fetch_builtin_classes( filename )
  ret = []
  vp("Process '#{filename}'")

  File.open( filename ) {|file|
    while src = get_method_table_source( file )
      param = parse_source_string( src )
      exit 1 if !param

      param[:classes].each {|cls|
        ret << {
          :class => [ cls[:class], cls[:module] ].compact[0],
          :struct_name => cls[:methods] ? "RBuiltinClass" : "RBuiltinNoMethodClass",
          :super => cls[:super],
        }
      }
    end
  }

  return ret
end


##
# write builtin class table to file.
#
def write_file( all_classes )
  vp("Output file '#{$opts[:out_file] || "STDOUT"}'")
  begin
    file = $opts[:out_file] ? File.open( $opts[:out_file], "w" ) : $stdout
  rescue Errno::ENOENT
    puts "File can't open. #{output_filename}"
    exit 1
  end

  file.puts "/* Auto generated by make_class_table.rb */\n\n"

  all_classes.each {|cls|
    file.puts "extern struct #{cls[:struct_name]} mrbc_class_#{cls[:class]};"
  }

  file.puts "\n#if defined(MRBC_DEFINE_BUILTIN_CLASS_TABLE)
static const struct MRBC_BuiltinClass {
  mrbc_class *cls;
  mrbc_class *super;
} MRBC_BuiltinClass[] = {"

  all_classes.each {|cls|
    cls_name = "MRBC_CLASS(#{cls[:class]})"
    cls_super = cls[:super] == "0" ? "0" : "MRBC_CLASS(#{cls[:super]})"
    case cls[:class]
    when "Float", "String", "Math"
      file.puts "#if MRBC_USE_#{cls[:class].upcase}"
      file.puts "  { #{cls_name}, #{cls_super} },"
      file.puts "#endif"
    else
      file.puts "  { #{cls_name}, #{cls_super} },"
    end
  }
  file.puts "};"

  file.puts "#endif"
  file.close  if $opts[:out_file]
end


##
# main
#
$opts = get_options()
exit if !$opts

if ARGV.empty?
  STDERR.puts "File not given."
  exit 1
end

all_classes = []

ARGV.each {|filename|
  all_classes.concat( fetch_builtin_classes( filename ))
}

write_file( all_classes )

vp("Done")
