package main;
BEGIN {$Quit = 'no_cleanup'}
use constant GC_DEBUG => 0;

use 5.004;
use strict;

use vars qw(%Options %Config $Quit);

use File::Spec;
use lib 'engine';
use MCoreTools;

# 5.004 doesn't like this. Unbalanced string table refcount: (1) for "<an env var>" during global destruction.
#%ENV = (); 

foreach (keys %ENV) {delete $ENV{$_}} # for security

### Command line parsing ######################################################

use Getopt::Long ();

Getopt::Long::config(qw(no_getopt_compat bundling));
my $optret = Getopt::Long::GetOptions(\%Options,
  'help|h',
  'modules|m=s',
  'world|w=s',
  'std|s',
);
(pop @ARGV || '') =~ /^(.*)$/;
my $config_arg = $1;

if (!$optret or $Options{help}) {print <<"EOT"; exit}
mpMUD Server - Usage: $0 [options] [config-file-name]
  -h --help                 : display this message
  -m<path> --modules=<path> : directory to load modules from
  -w<path> --world=<path>   : directory to load world, accounts, etc. from
  -s --std                  : use standard input/output as a connection
  The -m and -w options override paths set in the configuration file.
  Pathnames for -m and -w must be relative to the current directory
and use slashes as separators, irrespective of the OS's normal pathname
syntax.
EOT

for my $opt (grep defined, $Options{world}, $Options{modules}) {
  $opt =~ s#/$##;
  $_ = "$opt/";
  next if !$_ or -e;
  print "$_ doesn't exist!";
  exit;
}

### Initialization ############################################################

{
  local ($@, $!);
  my $config_path = $config_arg || rfile('config/config.pl');
  %Config = %{do($config_path)
    or print("Couldn't ".($@?'compile':'open')." config file $config_path: ".($@ || $!)."\n"), exit};
}

$Quit = 'problem during startup' unless $Quit ne 'no_cleanup';
print "mpMUD starting up...\n";

MCoreTools->install_signal_handlers;

$Config{mod_path} = $Options{modules} if $Options{modules};
$Config{db_path} = $Options{world} if $Options{world};

MCoreTools->open_log;

MCoreTools->init_hooks;
require MConnection;         initialize MConnection;
require MConnection::Telnet; initialize MConnection::Telnet;
require MConnection::Std;    initialize MConnection::Std;
MConnection->listen_start;

require MModules;            initialize MModules;
MModules->load_modules;

MConnection::Std->new if $Options{std} || $Config{use_std};

### Main loop ############################################################################################

$Quit = '';
MScheduler->run;

### Shutdown ###############################################################################################

END {
  # Cause of exit indicated by $Quit:
  #   false: crash
  #   'normal': admin shutdown command
  #   'no_cleanup': this is a child of fork() so don't write data files, log messages, etc.
  #   anything else: string describing reason

  my $notify_string;
  if (!$Quit) {
    mudlog "Exiting due to crash.";
    $notify_string = "Sorry, but $Config{name} just crashed.";
    $? = 1;
  } elsif ($Quit eq 'normal') {
    mudlog "Exiting normally.";
    $notify_string = "$Config{name} is shutting down.";
    $? = 0;
  } elsif ($Quit eq 'restart') {
    mudlog "Exiting for restart.";
    $notify_string = "$Config{name} is restarting.";
    $? = 0;
  } elsif ($Quit eq 'no_cleanup') {
    $? = 0;
    return; # note that END {} is a subroutine
  } else {
    mudlog "Exiting due to $Quit.";          
    $notify_string = "$Config{name} is shutting down.";
    $? = 1;    
  }

  if ($MConnection::_initialized) {
    mudlog "Notifying connections...";
    MConnection->all->send("[*** $notify_string ***]");

    mudlog "Closing listeners...";
    MConnection->listen_stop;
    
    mudlog "Closing connections...";
    MConnection->all->disconnect($Quit ? 'normal shutdown' : 'shutdown');
  }  
  
  # close database AFTER close connections so that any stuff that connections need
  # to do (e.g. saving values in user data) gets written
  if ($MObjectDB::_initialized) {
    mudlog "Closing database...";
    MObjectDB->close;
  }
  
  mudlog "Exit cleanup complete.";

  if ($Quit and $Quit eq 'restart') {
    if (IS_MACOS) {
      mudlog "Sending event to the Finder to restart.";
      # this is ugly
      MacPerl::DoAppleScript(do { $_ = <<'      EOSC'; s/THEPATH/"$0"/; $_ });
        ignoring application responses
          tell app "Finder"
            open file THEPATH
          end tell
        end ignoring
      EOSC
    } else {
      mudlog "Calling exec() to restart.";
      exec ('/usr/bin/perl', '-wT', rfile('engine/mpMUD.pl')) or die "Couldn't re-exec self: $!";
    }
  }
}
1;

__END__
