#! /usr/bin/env ruby

require 'orogen'
require 'optparse'

# Disable typelib plugin loading
Typelib.load_type_plugins = false

DEFAULT_TRANSPORTS = %w{corba typelib}

verbosity = 0
extended_states = true
component = Orocos::Generation::Project.new

transports = []
no_transports = 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|
        component.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|
        Orocos::Generation.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|
        Orocos::Generation.typekit_slice_minimum = parallel_level
    end
    opt.on('--typekit-slice=NUMBER', Integer) do |slice_size|
        Orocos::Generation.typekit_slice = slice_size
    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

        Orocos::Generation.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|
        Orocos::Generation.default_type_export_policy = mode.to_sym
    end

    opt.on("-v", "--verbose") { Orocos::Generation.logger.level = Logger::INFO; verbosity = 1 }
    opt.on("-d", "--debug")   { Orocos::Generation.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
        Orocos::Generation.really_clean
        exit 0
    end
    opt.on("--clean", "removes all autogenerated files") do
        Orocos::Generation.clean
        exit 0
    end
    opt.on("--base-dir", "the directory where orogen lib part is installed") do
        puts Orocos::Generation.base_dir
        exit 0
    end

    opt.on("-V", "--version", "displays the current orogen version") do
        puts "orogen v#{Orocos::Generation::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) }
Orocos::Generation.generation_directory = Dir.pwd
Orocos::Generation.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

    name = files.last
    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 name
    Dir.glob(File.join(Orocos::Generation.base_dir, "..", "misc", "new_project", "*")) do |input_file|
        output_file = File.basename(input_file.gsub('NAME', name))
        output_file = File.join(name, 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 = Orocos::Generation::ConfigError
begin
    filename  = files.first
    begin
        component.enable_transports(*transports)
        component.load(filename, verbosity > 1)

    rescue Exception => e
        # Two options:
        #  * the first line of the backtrace is the orogen file
        #    => change it into a ConfigError. If, in addition, this is a
        #       NoMethodError then change it into a statement error
        #  * the second line of the backtrace is in the orogen file
        #    => most likely a bad argument, transform it into a ConfigError
        #       too
        #  * all other cases are reported as internal errors
        file_pattern = /#{Regexp.quote(File.basename(filename))}/
        if e.backtrace.first =~ file_pattern
            if e.kind_of?(NoMethodError) || e.kind_of?(NameError)
                e.message =~ /undefined (?:local variable or )?method `([^']+)'/
                method_name = $1
                raise ConfigError, "unknown statement '#{method_name}'", e.backtrace
            else
                raise ConfigError, e.message, e.backtrace
            end
        elsif (e.backtrace[1] =~ file_pattern) || e.kind_of?(ArgumentError)
            raise ConfigError, e.message, e.backtrace
        end
        raise
    end

    component.generate
rescue Orocos::Generation::ConfigError, ArgumentError => e
    raise if verbosity > 0
    # Find the first stack frame on the orogen file
    error_line = e.backtrace.find { |line| line =~ file_pattern }
    STDERR.puts "#{error_line}: #{e.message}"
    exit 1
rescue Exception => e
    STDERR.puts "===== Internal error ======="
    STDERR.puts "Please report the following error to the orogen developpers"
    raise
end

