#!/usr/bin/ruby

###
### mkmethod - compile template and define a method to print it.
###

require 'kwartz'

$command = File.basename($0)
$usage = <<END
Usage: #{$command} [-svh] [-p file] [-l lang] [-M module] [-m method] file.html
  -p file            : presentation logic file
  -l lang            : ruby/ruby2/eruby/erb/php/php2 (default 'ruby2')
  -M module/class    : module/class name (default none)
  -m method          : method name (default 'expand_' + file)
  -A arg1,arg2,...   : method arguments (default nil)
  -s                 : sanitizing (equals to '--escape=true')
  -h, --help         : print help and exit
  -v                 : print version and exit
  --optname=value    : options for kwartz
END

id_str     = '$Id: mkmethod,v 0.8 2004/04/26 20:20:36 kwatch Exp kwatch $'
lastupdate = (id_str =~ /(\d\d\d\d[-\/]\d\d[-\/]\d\d)/ && $1)
revision   = (id_str =~ /(\d+(?:\.\d+)+)/ && $1)
$version = <<END
#{$command} - #{revision} (#{lastupdate}), kwartz.rb - #{Kwartz::VERSION} (#{Kwartz::LAST_UPDATE})
END

def main(argv=ARGV)
    lang            = 'ruby2'
    plogic_filename = nil
    module_name     = nil
    method_name     = nil
    method_args     = nil
    flag_ob         = false
    
    begin
	options, toppings = Kwartz::Util.parse_argv(argv, "hvs", "plmMA")
    rescue Kwartz::Util::CommandOptionError => ex
	$stderr.print "#{$command}: #{ex.message}\n"
	exit 1
    end
    
    if options[?l] then
	case options[?l]
	when 'ruby', 'ruby2', 'eruby', 'erb', 'php'
	    lang = options[?l]
	when 'php2'
	    lang = 'php'
	    flag_ob = true
	else
	    raise "lang '#{options[?l]}' is not supported."
	end
    end

    ## command line options
    if options[?h] || options[?v] then
	$stdout.print $version if options[?v]
	$stdout.print $usage   if options[?h]
	exit 0
    end
    module_name = options[?M]     if options[?M]
    method_name = options[?m]     if options[?m]
    method_args = options[?A]     if options[?A]
    plogic_filename = options[?p] if options[?p]

    ## toppings
    toppings[:escape] = true if options[?s]
    toppings[:lang]   = lang if toppings[:enable_eruby]
    if value = toppings[:include_path] then
	toppings[:include_path] = value.split(/,/)
    end
    if value = toppings[:load_path] then
	toppings[:load_path] = value.split(/,/)
    end

    ## compile
    argv0  = ARGV[0]
    pdata  = ARGF.read
    plogic = plogic_filename ? File.open(plogic_filename) { |f| f.read } : ''
    output, global_vars = compile(lang, pdata, plogic, toppings, method_args==nil)

    ## set values
    if !method_name
	basename = File.basename(argv0, '.*')
	method_name = "expand_#{basename}"
    end
    case lang
    when 'ruby'
	method_body = output
    when 'ruby2'
    	method_body = "_s = ''\n"
	method_body << output
	method_body << "return _s\n"
    when 'erb'
	require 'erb'
	erb = ERB.new(output, $SAFE, '%')
	method_body = erb.src
    when 'eruby'
	require 'eruby'
	compiler = ERuby::Compiler.new
	method_body = compiler.compile_string(output)
    when 'php'
	method_body = output
	lang = 'php2' if flag_ob
    else
	Kwartz::assert()
    end

    ## print definition
    hash = {
	:method_name => method_name,
	:method_body => method_body,
	:method_args => method_args,
	:global_vars => global_vars,
	:module_name => module_name,
	:lang        => lang,
	:flag_escape => toppings[:escape],
    }
    definition = create_method_definition(hash)
    print definition
end

def compile(lang, pdata, plogic, options, flag_analyze)
    converter  = Kwartz::DefaultConverter.new(pdata, options)
    scanner    = Kwartz::Scanner.new(converter.convert() + plogic, options)
    parser     = Kwartz::Parser.new(scanner, options)
    nodelist   = parser.parse()
    translator = Kwartz::Translator.instance(lang, options)
    output     = translator.translate_all(nodelist.deepcopy)
    global_vars = []
    if flag_analyze then
	analyzer = Kwartz::Analyzer.new(nodelist)
	analyzer.analyze
	global_vars = analyzer.globals
    end
    return output, global_vars
end

def create_method_definition(hash)
    method_name = hash[:method_name]
    method_body = hash[:method_body]
    method_args = hash[:method_args]
    global_vars = hash[:global_vars]
    module_name = hash[:module_name]
    lang        = hash[:lang]
    flag_escape = hash[:flag_escape]

    variables = global_vars || []
    s = ''
    case lang

    when 'ruby', 'ruby2', 'eruby', 'erb'
	prefix = module_name ? 'self.' : ''
	if method_args then
	    variables = method_args.split(/,/).collect {|var| var.strip}
	    method_args = variables.join(', ')
	end
	s = ''
	if flag_escape then
	    s << "require 'cgi'\n"			if lang != 'erb'
	    s << "require 'erb'\n"			if lang == 'erb'
	    s << "include ERB::Util\n"			if lang == 'erb'
	end
	s <<     "module #{module_name}\n"		if module_name
	s <<     "  def #{prefix}#{method_name}(_args)\n"
	variables.each do |var|
	    s << "    #{var} = _args[:#{var}]\n"
	end
	if method_args then
	    s << "    return #{prefix}_#{method_name}(#{method_args})\n"
	    s << "  end\n"
	    s << "  def #{prefix}_#{method_name}(#{method_args})\n"
	end
	s <<     method_body.gsub(/^/, '    ')
	s <<     "\n" if s[-1] != ?\n
	s <<     "  end\n"
	s <<     "end\n"				if module_name
	#s <<     "\n"

    when 'php', 'php2'
	if method_args then
	    variables = method_args.split(/,/).collect {|var| var.strip}
	    method_args  = variables.collect{|var| '$'  + var}.join(', ')
	    method_args2 = variables.collect{|var| '&$' + var}.join(', ')
	end
	flag_ob = lang == 'php2'
	s <<   "<?php\n"
	s <<   "class #{module_name} {\n"		if module_name
	s <<   "	function #{method_name}(&$_args) {\n"
	variables.each do |var|
	   s<< "		$#{var} = &$_args['#{var}'];\n"
	end
	if method_args then
	   prefix = module_name ? "#{module_name}::" : ''
	   s<< "		return #{prefix}_#{method_name}(#{method_args});\n"
	   s<< "	} // function end\n"
	   s<< "	function _#{method_name}(#{method_args2}) {\n"
	end
	s <<   "		ob_start();\n"          if flag_ob
	s <<   "?>\n"
	s <<   method_body
	s <<   "<?php\n"
	s <<   "		$_s = ob_get_contents();\n" if flag_ob
	s <<   "		ob_end_clean();\n"      if flag_ob
	s <<   "		return $_s;\n"          if flag_ob
	s <<   "	} // function end\n"
	s <<   "} // class end\n"			if module_name
	s <<   "?>\n"

    end
    return s
end

if __FILE__ == $0
    main(ARGV)
end

#    TEMPLATE_RUBY = <<'END'
#<span id="if:flag_escape==true">
#<span id="if:lang!='erb'">
#require 'cgi'
#</span>
#<span id="if:lang=='erb'">
#require 'erb'
#include ERB::Util
#</span>
#</span>
#<span id="mark:begin_module">
#module #{module_name}#
#</span>
#  def #{method_name}#(_args)
#<span id="variables">
#    #{var}# = _args[:#{var}#]
#</span>
#<span id="if:method_args!=null">
#    return #{method_name}#(#{method_args}#)
#  end
#  def #{method_name}#!(#{method_args}#)
#</span>
##{body}#
#  end
#<span id="mark:end_module">
#end
#</span>
#END
#
#    TEMPLATE_PHP = <<'END'
#<?php
#<span id="mark:begin_module">
#    class #{module_name}# {
#</span>
#	function #{method_name}#(&$_args) {
#<span id="mark:variables">
#	    $#{var}# = $_args['#{var}#'];
#</span>
#<span id="if:method_args!=null">
#	    return #{method_name}#_(#{method_args}#);
#	}
#	function #{method_name}#_(#{method_args}#) {
#</span>
#?>
##{body}#<?php
#	}
#<span id="mark:end_module">
#    }
#</span>
#END
#
#    plogic = <<'END'
#:elem(begin_module)
#  :if(method_args!=empty)
#    @stag
#    @cont
#    @etag
#  :end
#:end
#:elem(end_module)
#  :if(method_args!=empty)
#    @stag
#    @cont
#    @etag
#  :end
#:end
#:elem(variables)
#  :foreach(var=variables)
#    @stag
#    @cont
#    @etag
#  :end
#:end
#END
