package MLoaders;
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(
  load_fileref
  load_world

  load_texts
  load_help
  load_rooms_zone
  save_rooms
  save_objects
);

use strict;
use MGlobals;
use File::Path;

sub load_texts {
  local *TFIL;
  open TFIL, ":texts:splash" or die $!;
  local $/;
  $::Misc{splash} = <TFIL>;
  close TFIL;
}

sub load_fileref {
  my ($ref, $opt) = @_;

  my ($type, $path, $filepath) = split_file_ref($ref);

  if (-e $filepath) {
    syslog "Loading $type:$path";

    if ($type eq 'obj') {
      MObject->load_proto_zone($ref);
    } elsif ($type eq 'room') {
      load_rooms_zone($ref);
    } else {
      syslog "Bad fileref type in load_fileref: '$type'";
      return 0;
    }
  } else {
    syslog "File not found: $type:$path ($filepath)" unless $opt and $opt eq 'nofnf';
    return 0;
  }    
  return 1;
}

sub load_help {
  local *HDIR;
  opendir HDIR, ":texts:help" or die $!;
  while (defined (my $f = readdir HDIR)) {
    next unless $f =~ /\.help$/;
    proc_help_file(":texts:help:$f", \%::Help);
  }
  closedir HDIR;
  opendir HDIR, ":texts:help:!immortal" or die $!;
  while (defined (my $f = readdir HDIR)) {
    next unless $f =~ /\.help$/;
    proc_help_file(":texts:help:!immortal:$f", \%::ImmHelp);
  }
  closedir HDIR;
}

sub proc_help_file {
  my ($file, $table) = @_;

  local *HELP;
  open HELP, $file or die "$file: $!";
  my $buf = <HELP>;
  my @keywords = split /\s+/, $buf;
  $buf = "&c;$buf&n;";
  my $line;
  $buf .= $line while defined($line = <HELP>);
  foreach (@keywords) {
    $table->{lc $_} = \$buf;
  }
  close HELP;
}

sub load_world {
  local *lw_dir = sub {
    my ($dir) = @_;
    #syslog "Scanning :world$dir";
    local *WORLD;
    opendir WORLD, ":world$dir" or die $!;
    while (defined (my $f = readdir WORLD)) {
      next unless -d ":world$dir:$f";
      next unless $f =~ /^\w+$/;
      my $ref = "$dir/$f";
      $ref =~ tr#:#/#;
      load_fileref("room:$ref", 'nofnf');
      load_fileref("obj:$ref", 'nofnf') unless $f eq 'core' and $dir eq '';
      lw_dir("$dir:$f");
    }
    closedir WORLD;
  };

  load_fileref('obj:/core') or die;
  lw_dir('');
}


sub load_rooms_zone {
  my ($ref) = @_;

  my ($type, $path, $filepath) = split_file_ref($ref, 'room');
  local *ROOMF;
  open ROOMF, "$filepath" or do {
    syslog("$type:$path - room file not found");
    return;
  };
  my ($oname, $name, %params, $exits, $desc);
  local $_;
  local $/ = "\n";
  while (<ROOMF>) {
    #s/^\s+//;
    s/\s+$//;
    /^(\w+)\s*(.*)$/ and !$oname and do {($oname, $name) = ($1, $2); next};
    /^(\w+)\s*=\s*(.*)$/ and do {$params{$1} = $2; next};
    /
      ^exit               # exit record
      \s*
      (\w+)               # direction
      \s*
      ([\w\/]+)               # destination
      \s*
      (?:\[               # door info in brackets
        \s*
        ([^\/]+)             # door flags
        \s*
        ([\w\/]+)             # key object
        \s*
        {(.*?)}           # door keywords
        \s*
      ])?
      \s*
      (?:{(.*)})?             # exit desc
      $
    /ix and do {
      $exits->{$1} = {dir => $1, to => $2, flags => parse_flags($3, \%EXITFLAGS), desc => $6, key => $4, keywords => $5};
      next;
    };
    /^~$/ and do {
      my ($t, @c);
      if ($t = $::Rooms{"$path/$oname"}) {
        @c = @{$t->contents};
        foreach my $o (@c) {
          $o->_gsend_local("&fm;&sb;the world shifts around you.&n;");
        }
        $t->contents([]); # Must remove objects before disposing, or the objects
                          # will get destroyed with the room.
        $t->dispose;
      }
      $t = $::Rooms{"$path/$oname"} = new MObject (
        'prototype' => ROOM_PROTO,
        name => (defined $name ? $name : "UNDEFINED ROOM NAME"),
        terrain => (defined $params{terrain} ? $TERRAINS{$params{terrain}} : 1),
        roomflags => (defined $params{flags} ? parse_flags($params{flags}, \%ROOMFLAGS) : 0),
        idesc => $desc,
        exits => $exits,
        roomname => "$path/$oname",
        aliases => 'room ' . ($name || ''),
      );
      $t->add_contents(@c);
      foreach ($oname, $name, $exits, $desc) {undef $_}
      %params = ();
      next;
    };
    $desc .= "$_\n" if $_;
  }
  close ROOMF;
  delete $::DirtyFiles{"$type:$path"};
}

sub save_rooms {
  my ($ref) = @_;
  my ($type, $path, $filepath) = split_file_ref($ref, 'room');

  my ($dir) = $filepath =~ m#^(.*?)[\w.]*$#;
  mkpath($dir);
  local *ZOUT;
  open ZOUT, "> $filepath" or die $!;
  for my $opath (sort grep m#^$path/[^/]+$#, keys %::Rooms) {
    (my $lname) = $opath =~ m#\/([^/]+)$#;
    my $obj = $::Rooms{$opath};
    my $fieldstr = '';
    $fieldstr .= "terrain=" . $TERRAINS[$obj->terrain] . "\n";
    $fieldstr .= "flags=" . sprint_flags($obj->roomflags, \@ROOMFLAGS) . "\n" if $obj->roomflags;
    foreach my $exitk (keys %{$obj->exits}) {
      my $exit = $obj->exits->{$exitk};
      my $dstr = $exit->{key} ? "[@{[sprint_flags($exit->{flags}, \@EXITFLAGS)]} $exit->{key} {$exit->{keywords}}] " : '';
      $fieldstr .= "exit $exitk $exit->{to} $dstr\{@{[$exit->{desc} || '']}}\n";
    }
    print ZOUT "$lname @{[$obj->name]}\n$fieldstr@{[$obj->idesc]}\n~\n";
  }
  close ZOUT;
  delete $::DirtyFiles{"$type:$path"};
}

sub save_objects {
  my ($ref) = @_;
  my ($type, $path, $filepath) = split_file_ref($ref, 'obj');

  my ($dir) = $filepath =~ m#^(.*)/[^/]*$#;
  mkpath($dir);

  local *ZOUT;
  open ZOUT, "> $filepath" or die $!;
  for my $oname (sort grep m#^$path/[^/]+$#, keys %MObject::Prototypes) {
    $oname =~ m#\/([^/]+)$#;
    print ZOUT "$1\n", $MObject::Prototypes{$oname}->freeze, "~\n";
  }
  close ZOUT;
  delete $::DirtyFiles{"$type:$path"};
}
1;
