#!/usr/bin/env ruby

class CmdStop < Exception
  attr_accessor :exit_status
  def initialize(exit_status = 0)
    @exit_status = exit_status
  end
end
dev_dir = nil
if _i = ARGV.index("--dev")
  dev_dir = ARGV[_i+1]
  ARGV.delete "--dev"
  ARGV.delete dev_dir
end

if dev_dir.nil? 
  _s = nil
  ARGV.each_with_index do |s,i|
    if s.match(/^--dev(?:=(.*))?/)
      dev_dir = $1
      _s = s
      next
    end
  end
  ARGV.delete _s if _s
end

if dev_dir
  Dir.glob(File.join(File.expand_path(dev_dir),'rbbt-*/lib')).each do |f|
    $LOAD_PATH.unshift f
  end
end

require 'rbbt'
require 'rbbt/util/simpleopt'
require 'rbbt/util/config'

Log.nocolor = true if ARGV.include? "--nocolor"

options = SOPT.setup <<EOF
Ruby bioinformatics toolkit

$ rbbt <command> <subcommand> ... -a --arg1 --arg2='value' --arg3 'another-value'


--log*                  #{Log.color :yellow, "Log level from 0 (debug) 6 (errors)"}
--log_file*             #{Log.color :yellow, "Divert all Rbbt logs from STDERR to the file"}
--dev*                  #{Log.color :yellow, "Find development libraries in the directory specified"}
-cd--command_dir*       #{Log.color :yellow, "Directory from where to load command scripts"}
--profile               #{Log.color :yellow, "Profile execution"}
--nocolor               #{Log.color :yellow, "Disable colored output"}
--nobar                 #{Log.color :yellow, "Disable progress report"}
--nostream              #{Log.color :yellow, "Disable persistance/job streaming"}
--update_persist        #{Log.color :yellow, "Update persisted TSV files if source has been updated"}
--locate_file           #{Log.color :yellow, "Report the location of the script instead of executing it"}
--dump_mem*             #{Log.color :yellow, "Dump strings in memory each second into file"}
-nolock--no_lock_id            #{Log.color :yellow, "Do not track lockfiles with ids (prevent stale file handlers for high-througput and high-concurrency)"}
-ji--jobname_show_inputs  #{Log.color :yellow, "Show inputs as part of the jobname in workflows instead of digesting them"}
-ck--config_keys*       #{Log.color :yellow, "Override some config keys"}
EOF

if options[:jobname_show_inputs]
  ENV["RBBT_INPUT_JOBNAME"] = "true"
end

locate = options.delete :locate_file

if options[:log_file]
  Log.logfile(options[:log_file])
end

Log.ignore_stderr do
  begin
    require "nokogiri"
  rescue
  end
end

if options[:log]
  Log.severity = options[:log].to_i
else
  global_severity = Log.get_level(Rbbt.etc.log_severity.read.strip) if Rbbt.etc.log_severity.exists?
  if ENV["RBBT_LOG"]
    Log.severity = ENV["RBBT_LOG"].to_i 
  else
    global_severity = Log.get_level(Rbbt.etc.log_severity.read.strip) if Rbbt.etc.log_severity.exists?
    Log.severity = global_severity.to_i if global_severity
  end
end

if options.delete(:no_lock_id)
  Misc.use_lock_id = false
end

if mem_dump = options.delete(:dump_mem)
  require 'rbbt/monitor'
  Rbbt.dump_memory(mem_dump, String)
end

if options[:config_keys]
  options[:config_keys].split(",").each do |config|
    if Misc.is_filename?(config) && File.exists?(config)
      Rbbt::Config.load_file(config)
    elsif Rbbt.etc.config_profile[config].exists?
      Rbbt::Config.load_file(Rbbt.etc.config_profile[config].find)
    else
      key, value, *tokens = config.split(/\s/)
      tokens = ['key:' << key << '::0'] if tokens.empty?
      tokens = tokens.collect do |tok|
        tok, _sep, prio = tok.partition("::")
        prio = "0" if prio.nil? or prio.empty?
        [tok, prio] * "::"
      end
      Rbbt::Config.set({key => value}, *tokens)
    end
  end
end

if options.delete(:update_persist)
  ENV["RBBT_UPDATE_TSV_PERSIST"] = "true"
end

if options.delete :nostream
  ENV["RBBT_NO_STREAM"] = "true"
end

if options.delete :nobar
  ENV["RBBT_NO_PROGRESS"] = "true"
end

if options[:command_dir]
  $rbbt_command_dir = Path.setup(options[:command_dir].dup)
else
  $rbbt_command_dir = Rbbt.share.rbbt_commands
end

SOPT.description =<<EOF
This command controls many aspects of the Rbbt framework, from configuration tasks to running applications. 

Commands are implemented in separate files under the Rbbt path '#{$rbbt_command_dir}'. 
Known locations are: #{([$rbbt_command_dir] + $rbbt_command_dir.find_all) * ", " }. 
You can place your own commads at #{$rbbt_command_dir.find(:user)}.
EOF

if options[:profile]
  require 'ruby-prof'
  RubyProf.start
end


def prev_dir(prev)
    rbbt_command_dir = $rbbt_command_dir

    prev.each do |previous_command|
        rbbt_command_dir = rbbt_command_dir[previous_command]
    end

    rbbt_command_dir
end

def commands(prev)
    rbbt_command_dir = prev_dir(prev)

    command_file_dirs = rbbt_command_dir.find_all
    command_files = command_file_dirs.collect{|d| d.glob('*') }.flatten
    command_files.collect{|p| File.basename(p) }.uniq.reject{|p| p =~ /\.desc$/}.sort
end

def rbbt_usage(prev = nil)
  puts
  puts SOPT.doc

  if prev
    puts
    puts Log.color :magenta, "## COMMANDS"
    puts
    puts Log.color :magenta, "Command:"
    puts 
    puts "  #{File.basename($0)} #{prev * " "}"
    puts 
    puts Log.color :magenta, "Subcommands:"
    puts 
    prev_dir = prev_dir(prev)
    commands(prev).each do |command|
      directory = File.directory? prev_dir[command].find
      if directory
        puts "  " << Log.color(:blue, command)
      else
        puts "  " << command
      end
    end
  end
  puts
  true
end

alias usage rbbt_usage 

def print_error(error, backtrace = nil)
  puts Log.color :magenta, "## ERROR"
  puts
  if backtrace
    puts Log.color :red, "Backtrace: "
    puts
    puts Log.color_stack(backtrace.reverse) * "\n"
    puts
  end
  puts Log.color :red, error
  puts
end

def aliases
  @aliases ||= Rbbt.etc.cmd_alias.exists? ? Rbbt.etc.cmd_alias.yaml : {}
end

def cmd_alias
  while aliases.include? ARGV[0]
    ARGV.replace Misc.parse_cmd_params(aliases[ARGV[0]]) + ARGV[1..-1]
  end
end
    
dir = $rbbt_command_dir
$previous_commands = []

cmd_alias

exit_status = 0
begin
  while ARGV.any?
    $command = ARGV.shift
    case
    when File.directory?(dir[$command].find)
      $previous_commands << $command
      dir = dir[$command]
    when dir[$command].exists?
      if locate
        puts dir[$command].find
        exit_status = 0
        exit exit_status
      else
        load dir[$command].find
        exit_status = 0
        exit exit_status
      end
    when File.exist?($command)
      load $command
      exit_status = 0
      exit exit_status
    else
      error = "Command '#{$command }' not understood"
      rbbt_usage($previous_commands)
      print_error(error)
      exit_status = -1
      exit exit_status
    end
  end

  rbbt_usage($previous_commands)
  exit_status = 0
  exit exit_status

rescue ParameterException
  puts
  rbbt_usage
  print_error($!.message, $!.backtrace)
  puts
  exit_status = -1
  exit exit_status
rescue SystemExit
rescue CmdStop
  exit_status = $!.exit_status
  exit exit_status
rescue Exception
  Log.exception $!
  exit_status = -1
  exit exit_status
ensure
  if options[:profile]
    result = RubyProf.stop
    printer = RubyProf::FlatPrinter.new(result)
    printer.print(STDOUT, :min_percent => 10)
  end
end

exit exit_status