#!/usr/bin/env ruby
#
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
require 'optparse'
require 'ostruct'
require 'debugger'
require 'debugger/xml'

$stdout.sync = true

class RdebugIde

  def initialize
    check_argv!
    @proxy = DebuggerXml::DebuggerProxy.new
    @proxy.set_argv(ARGV.clone)
    @proxy.set_rdebug_script(rdebug_path)
    @proxy.set_prog_script(ARGV.shift)
    if options.int_handler
      install_interruption_handler
    end
    @proxy.tracing = options.tracing
    @proxy.printer = Printers::Xml.new
    @proxy.wait_connection = true
    DebuggerXml.wait_for_start = options.wait_for_start
    Debugger::Xml.logger = if options.debug_mode
      Debugger::Xml::Ide::Logger.new
    else
      Debugger::Xml::FakeLogger.new
    end
    init_multi_process_debug(options) if options.dispatcher_port
  end

  def run
    Debugger.start_remote_ide(options.host, options.port)
    bt = Debugger.debug_load(Debugger::PROG_SCRIPT, false, false)
    if bt
      print bt.backtrace.map{|l| "\t#{l}"}.join("\n"), "\n"
      print "Uncaught exception: #{bt}\n"
    end
  end

  private

  def init_multi_process_debug(options)
    ENV['IDE_PROCESS_DISPATCHER'] = options.dispatcher_port.to_s
    ENV['DEBUGGER_HOST'] = options.host.to_s
    ENV['DEBUGGER_DEBUG_MODE'] = options.debug_mode.to_s
    ENV['DEBUGGER_STORED_RUBYLIB'] = $LOAD_PATH.join(File::PATH_SEPARATOR)
    old_opts = ENV['RUBYOPT']
    ENV['RUBYOPT'] = "-r#{File.expand_path(File.dirname(__FILE__))}/../lib/debugger/xml/multiprocess/starter"
    ENV['RUBYOPT'] += " #{old_opts}" if old_opts
  end

  def check_argv!
      if ARGV.empty?
        puts opts
        puts
        puts "Must specify a script to run"
        exit(1)
      end
    end

    def install_interruption_handler
      trap('INT') { @proxy.interrupt_last }
    end

    def rdebug_path
      File.expand_path($0).tap do |path|
        if RUBY_PLATFORM =~ /mswin/
          rdebug_path << ".cmd" unless rdebug_path =~ /\.cmd$/i
        end
      end
    end

    def options
      opts
      @options
    end

    def opts
      @opts ||= begin
        @options = OpenStruct.new(
          host: "127.0.0.1", port: 12345, stop: false, tracing: false, wait_for_start: true,
          int_handler: true, debug_mode: false, dispatcher_port: nil
        )
        opts = OptionParser.new do |opts|
          opts.banner = %{
            Using rdebug-ide
            Usage: rdebug-ide is supposed to be called from RDT, NetBeans, RubyMine or
            vim-ruby-debugger. The command line interface to 'debugger' is rdebug.
          }.gsub(/^\s*/, '')
          opts.separator ""
          opts.separator "Options:"
          opts.on("-h", "--host HOST", "Host name used for remote debugging") { |host| @options.host = host }
          opts.on("-p", "--port PORT", Integer, "Port used for remote debugging") { |port| @options.port = port }
          opts.on('--dispatcher-port PORT', Integer, 'Port used for multi-process debugging dispatcher') do |dp|
            @options.dispatcher_port = dp
          end
          opts.on("-d", "--debug", "Enable debug mode") { |host| @options.debug_mode = true }
          opts.on("--wait", String, "Wait for 'start' command") do |bool|
            @options.wait_for_start = (bool == "false" ? false : true)
          end
          opts.on('--stop', 'stop when the script is loaded') { @options.stop = true }
          opts.on("-x", "--trace", "turn on line tracing") { @options.tracing = true }
          opts.on("-I", "--include PATH", String, "Add PATH to $LOAD_PATH") { |path| $LOAD_PATH.unshift(path) }
          opts.on('--disable-int-handler', 'Disables interrupt signal handler') { @options.int_handler = false }
        end
        opts.parse!
        opts
      end
    end

end

RdebugIde.new.run
