#!/usr/bin/ruby -wU
#
# mailshears, to mangle your mail garden
#

# Load all of our lib/ code.
require 'mailshears'

# Define a usage string using the program name.
program_name = File.basename($PROGRAM_NAME)

# Defaults
mode_name = 'prune'
mode = :prune

# Before doing anything else, check for "-h" and "--help" in the args,
# because those should cause us to dump usage info and bail out.
if ARGV.include?('-h') or ARGV.include?('--help') then
  puts "Usage: #{UserInterface.usage(program_name)}"
  Kernel.exit(ExitCodes::SUCCESS)
end


# If a mode was supplied, it should be in ARGV[0].
if ARGV.length() > 0
  mode_names = ['prune', 'rm', 'mv']
  if mode_names.include?(ARGV.first().downcase()) then
    # Peel the mode name off the head of the list.
    mode_name = ARGV.shift()
  end
end

# Determine the mode from its name.
if mode_name == 'rm' then
  mode = :rm
elsif mode_name == 'mv' then
  mode = :mv
end

# Since we removed the mode name (if it existed) from ARGV, what
# remains should be the required arguments. Figure out if we have the
# wrong number of arguments, and store the associated error message in
# args_error_message if necessary.
args_error_message = nil

if mode == :prune and ARGV.length() != 0 then
  args_error_message = "ERROR: prune mode takes no additional arguments."
elsif mode == :rm and ARGV.length() < 1 then
  args_error_message = "ERROR: rm mode takes two or more user arguments."
elsif mode == :mv and ARGV.length() != 2 then
  args_error_message = "ERROR: mv mode takes exactly two user arguments."
end

# If we got the wrong number of arguments, we'll have an error message
# here. Report it and exit with a failure code.
if not args_error_message.nil? then
  STDERR.puts args_error_message
  puts "Usage: #{UserInterface.usage(program_name)}"
  Kernel.exit(ExitCodes::BAD_COMMAND_LINE)
end


# Load each of the plugins that we'll need.
cfg = Configuration.new()

cfg.plugins.each do |plugin_file|
  require "#{mode_name}/plugins/#{plugin_file}"
end

# And the runners.
require "#{mode_name}/#{mode_name}_runner"
require "#{mode_name}/#{mode_name}_dummy_runner"

# Now we figure out which plugin module to use based on our mode.
plugin_module = nil

if mode == :rm then
  plugin_module = RmPlugin
elsif mode == :mv then
  plugin_module = MvPlugin
else
  # Safe, catch-all default
  plugin_module = PrunePlugin
end

# Parse the remaining arguments as User/Domain objects. If we get some
# other argument that isn't one of those, it's an error.
parsed_args = []

ARGV.each do |arg|
  begin
    u = User.new(arg)
    parsed_args << u
  rescue InvalidUserError
    begin
      d = Domain.new(arg)
      parsed_args << d
    rescue InvalidDomainError
      STDERR.puts "ERROR: invalid user/domain argument #{arg}"
      Kernel.exit(ExitCodes::BAD_COMMAND_LINE)
    end
  end
end


# Buffer the output so that we can avoid printing the informational
# header when no plugins produce output.
require 'stringio'
output_buffer = StringIO.new()
$stdout = output_buffer

begin
  plugin_module.run(cfg, *parsed_args)
ensure
  # Now restore stdout, and print the header plus whatever the plugins
  # produced (if they produced anything). If they didn't produce any
  # output, then we avoid printing the header.
  #
  # This gets wrapped in an "ensure" block because otherwise, if
  # plugin_module.run() crashes, the traceback will get stored in
  # output_buffer and never get printed.
  $stdout = STDOUT

  if output_buffer.size > 0 then
    puts UserInterface.make_header(program_name, plugin_module.to_s())
    puts output_buffer.string()
  end
end

# If we made it here without crashing, well that sounds pretty
# successful to me.
Kernel.exit(ExitCodes::SUCCESS)
