#! /usr/bin/env ruby

require 'orogen'
require 'orogen/gen'
OroGen::Gen::RTT_CPP.enable
require 'optparse'

# Disable typelib plugin loading
Typelib.load_type_plugins = false

DEFAULT_TRANSPORTS = %w{corba typelib mqueue}
DEFAULT_EXTENSIONS = %w{}

rtt_cpp = OroGen::Gen::RTT_CPP

verbosity = 0
extended_states = true
project = rtt_cpp::Project.new
OroGen::TypekitMarshallers::TypeInfo::Plugin.rtt_scripting = true

imported_typekits = []
transports = []
extensions = []
no_transports = false
explicit_typekit_slice = false

parser = OptionParser.new do |opt|
    opt.banner = "Usage: orogen [options] <template file>"

    opt.on("--[no-]corba", "enables/disables corba support (deprecated, use --transports instead)") do |flag|
        project.enable_transports('corba')
    end
    opt.on("--[no-]extended-states", "disable or enable extended states for all tasks defined in this project (enabled by default)") do |flag|
        project.extended_states = flag
    end

    opt.on('--no-transports', "disables all transports (by default, #{DEFAULT_TRANSPORTS.join(", ")} are enabled)") do |transport_names|
        no_transports = true
    end
    opt.on('--parallel-build=NUMBER', Integer, "optimize generated code for parallel builds") do |parallel_level|
        rtt_cpp.typekit_slice_minimum = parallel_level
	if !explicit_typekit_slice && parallel_level > 2
	    rtt_cpp.typekit_slice = 1
	end
    end
    opt.on('--typekit-slice=NUMBER', Integer) do |slice_size|
	explicit_typekit_slice = true
        rtt_cpp.typekit_slice = slice_size
    end
    
    opt.on('--extensions=NAME[,NAME]', "selects the extensions that should be enabled (by default, #{DEFAULT_EXTENSIONS.join(", ")} are enabled)") do |v|
        v.split(',').each do |name|
            extensions << name
        end
    end

    opt.on('--transports=NAME[,NAME]', "selects the transports that should be enabled (by default, #{DEFAULT_TRANSPORTS.join(", ")} are enabled)") do |transport_names|
        transport_names.split(',').each do |name|
            transports << name
        end
    end

    opt.on("--target=TARGET", "set the orocos build target (gnulinux or xenomai)") do |target|
        target = target.to_s
        if target !~ /^(gnulinux|xenomai)$/
            STDERR.puts "unknown target '#{target}', possible values are gnulinux and xenomai"
            exit 1
        end

        rtt_cpp.orocos_target = target.to_s
    end

    opt.on('--type-export-policy=STRING", "sets the default type export policy. Must be either all or used') do |mode|
        rtt_cpp.default_type_export_policy = mode.to_sym
    end
    opt.on('--import=NAME', 'import types from this typekit') do |tk_name|
        imported_typekits << tk_name
    end

    opt.on('--[no-]rtt-scripting', 'when generating the typekit, remove features needed for RTT scripting (speeds up compilation and reduces code size)') do |value|
        OroGen::TypekitMarshallers::TypeInfo::Plugin.rtt_scripting = value
    end

    opt.on("-v", "--verbose") { rtt_cpp.logger.level = Logger::INFO; verbosity = 1 }
    opt.on("-d", "--debug")   { rtt_cpp.logger.level = Logger::DEBUG; verbosity = 2 }
    opt.on("-h", "--help", "this help message") do
        puts opt
        exit 0
    end
    opt.separator ""
    opt.on("--really-clean", "removes all autogenerated files, and user-side part that are identical to the templates") do
        rtt_cpp.really_clean
        exit 0
    end
    opt.on("--clean", "removes all autogenerated files") do
        rtt_cpp.clean
        exit 0
    end
    opt.on("--base-dir", "the directory where orogen lib part is installed") do
        puts rtt_cpp.base_dir
        exit 0
    end

    opt.on("-V", "--version", "displays the current orogen version") do
        puts "orogen v#{rtt_cpp::VERSION}"
        puts "Copyright 2008-2010 DFKI"
        STDOUT.flush
    end
end

original_options = ARGV.dup
begin
    files = parser.parse(ARGV)
rescue OptionParser::ParseError => e
    puts e.message
    exit(1)
end
if !no_transports && transports.empty?
    transports = DEFAULT_TRANSPORTS.dup.to_set
end
files.each { |path| original_options.delete(path) }
rtt_cpp.generation_directory = Dir.pwd
rtt_cpp.command_line_options = original_options

if files.first == "create"
    if files.size != 2
        STDERR.puts "the 'create' command expects only the project name as argument"
        exit(1)
    end

    path = files.last
    name = File.basename(path)
    if name !~ /^[a-z][a-z_0-9]+$/
        STDERR.puts "invalid name '#{name}': names must be all lowercase, can contain alphanumeric characters and underscores and start with a letter"
    end
    FileUtils.mkdir_p path
    Dir.glob(File.join(rtt_cpp.base_dir, "..", "misc", "new_project", "*")) do |input_file|
        output_file = File.basename(input_file.gsub('NAME', name))
        output_file = File.join(path, output_file)
        
        content = File.read(input_file).gsub(/NAME/, name)
        File.open(output_file, 'w') do |io|
            io.write content
        end
    end
    exit 0
elsif files.size > 1
    STDERR.puts "too many arguments on the command line"
    exit 1
elsif files.empty?
    STDERR.puts parser
    exit 0
end

ConfigError = OroGen::ConfigError
begin
    filename  = files.first
    OroGen.beautify_loading_errors(filename) do
        project.enable_transports(*transports)
        project.enable_extension(extensions)
        imported_typekits.each do |tk_name|
            begin
                project.using_typekit tk_name
            rescue LoadError
                raise ArgumentError, "#{tk_name}, which was given to the --import option, does not seem to be a typekit name"
            end
        end
        project.load(filename, verbosity > 1)
    end

    project.generate
rescue LoadError, ConfigError, ArgumentError => e
    raise if verbosity > 0
    # Find the first stack frame on the orogen file
    file_pattern = /#{Regexp.quote(File.basename(filename))}/
    error_line = e.backtrace.find { |line| line =~ file_pattern } || filename 
    STDERR.puts "#{error_line}: #{e.message} (#{e.class})"
    exit 1
rescue Exception => e
    STDERR.puts "===== Internal error ======="
    STDERR.puts "Please report the following error to the orogen developers"
    raise
end

