class IDL::Engine

def backend

def backend
  @backend
end

def collect_input(argv)

def collect_input(argv)
  ## collect input files from commandline
  argv.each do |_arg|
    _opts = options.dup
    _opts[:idlfile] = _arg
    if _opts[:search_incpath]
      _fname = _arg
      _fpath = if File.file?(_fname) && File.readable?(_fname)
                 _fname
               else
                 _fp = _opts[:includepaths].find do |_p|
                   _f = _p + "/" + _fname
                   File.file?(_f) && File.readable?(_f)
                 end
                 _opts[:outputdir] = _fp unless _fp.nil? || !_opts[:outputdir].nil?
                 _fp += '/' + _fname unless _fp.nil?
                 _fp
               end
      _arg = _fpath unless _fpath.nil?
    end
    _opts[:xincludepaths] << File.dirname(_arg)
    _opts[:outputdir] ||= '.'
    push_input(_arg, _opts)
  end
  ## if no IDL input file specified read from STDIN
  unless has_input?
    _opts = options.dup
    _opts[:outputdir] ||= '.'
    push_input($stdin, _opts)
  end
end

def has_input?

def has_input?
  !@inputstack.empty?
end

def init_optparser

def init_optparser
  script_name = File.basename($0, '.*')
  if not script_name =~ /ridlc/
    script_name = 'ruby '+$0
  end
  # set up option parser with common options
  opts = OptionParser.new
  opts.banner = "Usage: #{script_name} [:backend] [options] [<idlfile> [<idlfile> ...]]\n\n" +
      "    backend\t\tSpecifies the IDL language mapping backend to use.\n"+
      "           \t\tDefault = :null\n\n"+
      "    Active language mapping = :#{backend.name}"
  opts.separator ""
  opts.on("-I PATH", "--include=PATH", String,
          "Adds include searchpath.",
          "Default: nil") { |v| @options[:includepaths] << v }
  opts.on('-Dmacro=[value]', String, 'defines preprocessor macro') { |v|
    name, value = v.split('=')
    @options[:macros][name] = (value ? value : true)
  }
  opts.on("-n NAMESPACE", "--namespace=NAMESPACE", String,
          "Defines rootlevel enclosing namespace.",
          "Default: nil") { |v| @options[:namespace]=v }
  opts.on("-v", "--verbose",
          "Set verbosity level. Repeat to increment.",
          "Default: 0") { |v| @options[:verbose] += 1 }
  opts.on("--debug",
          "Set parser debug mode. Don't do this at home!",
          "Default: off") { |v| @options[:debug] = true }
  opts.on('--stdidl',
          'Adds include path to standard IDL files provided with RIDL.',
          'Default: not set') { |v|
    @options[:includepaths] << File.expand_path(File.join(File.dirname(__FILE__), '..', 'idl'))
  }
  opts.on("--search-includepath",
          "Use include paths to find main IDL source.",
          "Default: off") { |v| @options[:search_incpath]=v }
  if @initopts[:preprocess]
    opts.on("--output=FILE", String,
            "Specifies filename to generate output in.",
            "Default: basename(idlfile)-'.idl'+<postfix>+<ext>") { |v| @options[:output]=v }
  end
  # setup language mapping specific options
  be_options = OptionList.new
  @backend.setup_be(be_options, @initopts)
  be_options.to_option_parser(opts, self)
  opts.on('-V', '--version',
          'Show version information and exit.') {
    puts "RIDL compiler #{@options[:macros][:__RIDL__]}"
    puts RIDL_COPYRIGHT
    puts '---'
    @backend.print_version
    exit
  }
  opts.separator ""
  opts.on('-h', '--help',
          'Show this help message.') { puts opts; puts; exit }
  opts
end

def initialize(backend, options)

def initialize(backend, options)
  @backend = backend ? Backend.load(backend) : Backend.null_be
  @initopts = options.merge({
    backend: @backend.name,
    macros: options[:macros].merge({
       __RIDLBE__: @backend.name.to_s,
       __RIDLBE_VER__: @backend.version
    })
  })
  @optparser = init_optparser
  @inputstack = []
  @options = nil
end

def options

def options
  @options || @initopts
end

def parse(io, opts)

def parse(io, opts)
  # parse IDL source
  _parser = ::IDL::Parser.new(opts)
  _parser.yydebug = opts[:debug]
  begin
    _parser.parse(io)
  rescue => ex
    IDL.error(ex.inspect)
    IDL.error(ex.backtrace.join("\n")) unless ex.is_a? IDL::ParseError
    return nil
  ensure
    io.close unless String === io || io == $stdin
  end
  _parser
end

def peek_input

def peek_input
  @inputstack.first
end

def pop_input

def pop_input
  @inputstack.shift
end

def push_input(idlfile, opts)

def push_input(idlfile, opts)
  @inputstack << [idlfile, opts]
end

def run(argv, runopts = {})

def run(argv, runopts = {})
  # initialize options
  @options = @initopts.merge(runopts)
  # backup current engine (if any)
  cur_engine = Thread.current[:ridl_engine]
  # store currently running engine for current thread
  Thread.current[:ridl_engine] = self
  begin
    # parse arguments
    begin
      @optparser.parse!(argv)
    rescue ArgumentError => ex
      IDL.error(ex.inspect)
      IDL.error(ex.backtrace.join("\n")) if IDL.verbose_level>0
      return false
    end
    if options[:preprocess]
      ## PREPROCESSING
      o = if options[:output].nil?
            $stdout
          else
            File.open(options[:output], 'w+')
          end
      options[:output] = o
      input_base = File.basename(argv.first)
      if (input_base != argv.first)
        options[:xincludepaths] << File.dirname(argv.first)
      end
      return !parse("#include \"#{input_base}\"", options).nil?
    else
      ## collect input files from commandline
      collect_input(argv)
      ## CODE GENERATION
      while has_input?
        # get input from stack
        _idlfile, _opts = pop_input
        _fio = if IO === _idlfile || StringIO === _idlfile
                 _idlfile
               else
                 File.open(_idlfile, 'r')
               end
        raise RuntimeError, 'cannot read from STDOUT' if $stdout == _fio
        # parse IDL source
        IDL.log(1, "RIDL - parsing #{IO === _idlfile ? 'from STDIN': (StringIO === _idlfile ? 'from string' : _idlfile)}")
        unless _parser = parse(_fio, _opts)
          return false
        end
        # process parse result -> code generation
        IDL.log(2, 'RIDL - starting code generation')
        GenFile.transaction  do
          begin
            backend.process_input(_parser, _opts)
          rescue Backend::ProcessStop
            IDL.log(2, "RIDL - processing #{IO === _idlfile ? 'from STDIN': (StringIO === _idlfile ? 'from string' : _idlfile)} stopped with \"#{$!.message}\"")
          end
        end
      end
    end
  ensure
    # restore previous state
    Thread.current[:ridl_engine] = cur_engine
  end
  true
end

def verbose_level

def verbose_level
  options[:verbose]
end

def verbose_level=(l)

def verbose_level=(l)
  options[:verbose] = l
end