class Protobuf::CLI

def configure_deprecation_warnings

Tell protobuf how to handle the printing of deprecated field usage.
def configure_deprecation_warnings
  if options.print_deprecation_warnings.nil?
    ::Protobuf.print_deprecation_warnings = !ENV.key?("PB_IGNORE_DEPRECATIONS")
  else
    ::Protobuf.print_deprecation_warnings = options.print_deprecation_warnings?
  end
end

def configure_gc

If we pause during request we don't need to pause in serialization
def configure_gc
  debug_say('Configuring gc')
  if defined?(JRUBY_VERSION)
    # GC.enable/disable are noop's on Jruby
    ::Protobuf.gc_pause_server_request = false
  else
    ::Protobuf.gc_pause_server_request = options.gc_pause_request?
  end
end

def configure_logger

Setup the protobuf logger.
def configure_logger
  debug_say('Configuring logger')
  log_level = options.debug? ? ::Logger::DEBUG : options.level
  ::Protobuf::Logging.initialize_logger(options.log, log_level)
  # Debug output the server options to the log file.
  logger.debug { 'Debugging options:' }
  logger.debug { options.inspect }
end

def configure_process_name(app_file)

Re-write the $0 var to have a nice process name in ps.
def configure_process_name(app_file)
  debug_say('Configuring process name')
  $0 = "rpc_server --#{mode} #{options.host}:#{options.port} #{app_file}"
end

def configure_runner_mode

Configure the mode of the server and the runner class.
def configure_runner_mode
  debug_say('Configuring runner mode')
  self.mode = if multi_mode?
    say('WARNING: You have provided multiple mode options. Defaulting to socket mode.', :yellow)
    :socket
  elsif options.zmq?
    :zmq
  else # rubocop:disable Style/ElseAlignment
    # above: https://github.com/bbatsov/rubocop/pull/1470/files
    case server_type = ENV["PB_SERVER_TYPE"]
    when nil, /socket/i
      :socket
    when /zmq/i
      :zmq
    else
      say "WARNING: You have provided incorrect option 'PB_SERVER_TYPE=#{server_type}'. Defaulting to socket mode.", :yellow
      :socket
    end
  end
end

def configure_traps

TODO: add signal handling for hot-reloading the application.
Configure signal traps.
def configure_traps
  debug_say('Configuring traps')
  exit_signals = [:INT, :TERM]
  exit_signals << :QUIT unless defined?(JRUBY_VERSION)
  exit_signals.each do |signal|
    debug_say("Registering trap for exit signal #{signal}", :blue)
    trap(signal) do
      self.exit_requested = true
      shutdown_server
    end
  end
end

def create_runner

Create the runner for the configured mode
def create_runner
  debug_say("Creating #{mode} runner")
  self.runner = case mode
  when :zmq
    create_zmq_runner
  when :socket
    create_socket_runner
  else
    say_and_exit("Unknown runner mode: #{mode}")
  end
end

def create_socket_runner

def create_socket_runner
  require 'protobuf/socket'
  self.runner = ::Protobuf::Rpc::SocketRunner.new(runner_options)
end

def create_zmq_runner

def create_zmq_runner
  require 'protobuf/zmq'
  self.runner = ::Protobuf::Rpc::ZmqRunner.new(runner_options)
end

def debug_say(message, color = :yellow)

Say something if we're in debug mode.
def debug_say(message, color = :yellow)
  say(message, color) if options.debug?
end

def multi_mode?

Internal helper to determine if the modes are multi-set which is not valid.
def multi_mode?
  options.zmq? && options.socket?
end

def require_application(app_file)

Require the application file given, exiting if the file doesn't exist.
def require_application(app_file)
  debug_say('Requiring app file')
  require app_file
rescue LoadError => e
  say_and_exit("Failed to load application file #{app_file}", e)
end

def runner_options

def runner_options
  opt = options.symbolize_keys
  opt[:workers_only] = (!!ENV['PB_WORKERS_ONLY']) || options.workers_only
  opt
end

def say_and_exit(message, exception = nil)

def say_and_exit(message, exception = nil)
  message = set_color(message, :red) if options.log == STDOUT
  logger.error { message }
  if exception
    $stderr.puts "[#{exception.class.name}] #{exception.message}"
    $stderr.puts exception.backtrace.join("\n")
    logger.error { "[#{exception.class.name}] #{exception.message}" }
    logger.debug { exception.backtrace.join("\n") }
  end
  exit(1)
end

def shutdown_server

def shutdown_server
  logger.info { 'RPC Server shutting down...' }
  runner.stop
  ::Protobuf::Rpc::ServiceDirectory.instance.stop
end

def start(app_file)

def start(app_file)
  debug_say('Configuring the rpc_server process')
  configure_logger
  configure_traps
  configure_runner_mode
  create_runner
  configure_process_name(app_file)
  configure_gc
  configure_deprecation_warnings
  require_application(app_file) unless exit_requested?
  start_server unless exit_requested?
rescue => e
  say_and_exit('ERROR: RPC Server failed to start.', e)
end

def start_server

Start the runner and log the relevant options.
def start_server
  debug_say('Running server')
  runner.run do
    logger.info do
      "pid #{::Process.pid} -- #{mode} RPC Server listening at #{options.host}:#{options.port}"
    end
    ::ActiveSupport::Notifications.instrument("after_server_bind")
  end
  logger.info { 'Shutdown complete' }
end

def version

def version
  say("Ruby Protobuf v#{::Protobuf::VERSION}")
end