package MZone;
use strict;

use MCoreTools;
use MLoaders;
use MScheduler;

use vars qw(%Zones);

### Class ##########################################################################################

sub initialize {
  my ($class) = @_;
  
  #mudlog "Reading zone info...";
  local *proc_dir = sub {
    my ($dir) = @_;
    Mac::Events::WaitNextEvent(1) if MScheduler::MACEVENTS;
    mudlog "Scanning world$dir";
    local *WORLD;
    opendir WORLD, relfpath("data/world$dir") or ($! =~ /No such file or directory/ ? return : die "$!");
    while (defined (my $f = readdir WORLD)) {
      next unless -d relfpath("data/world$dir/$f");
      next unless $f =~ /^\w+$/;
      next if $f =~ /^_/;
      my $ref = "$dir/$f";
      $class->new($ref, _thawing => 1); 
      proc_dir("$dir/$f");
    }
    closedir WORLD;
  };

  proc_dir('');
  1;
}

sub new {
  my ($class, $path, %param) = @_;
  
  my $self = bless {
    path => $path,
  }, $class;
  
  if ($param{_thawing}) {
    $self->load_zinf or return;
  } else {
    $self->{info} = {};
  }
  $Zones{$path} = $self;
  
  return $self;
}

sub by_path {
  my ($class, $path) = @_;
  return $Zones{$path};
}

sub all_zones {
  return values %Zones;
}

### Instance ##########################################################################################

sub load_zinf {
  my ($self) = @_;

  my ($type, $path, $filepath) = parse_file_ref("zinf:$self->{path}");
  my $fh = IO::File->new($filepath) or do {
    mudlog("$type:$path ($filepath) - couldn't open for reading: $!")
      unless $! =~ /No such file/;
    return;
  };
  $self->{info} = {split /~/, <$fh>}; 
  delete $self->{info}{last_reset}; # ick, FIXME
  $fh->close;
  delete $::DirtyFiles{"$type:$path"};
  $self->{floaded}->{zinf} = 1;
  1;
}

sub save_zinf {
  my ($self) = @_;
  
  my ($type, $path, $filepath) = parse_file_ref("zinf:$self->{path}");
  MCoreTools->make_enclosing($filepath);
  my $fh = IO::File->new($filepath, '>', DATA_PERMS) or do {
    mudlog("$type:$path ($filepath) - couldn't open for writing: $!");
    return;
  };
  $fh->print(join '~', %{$self->{info}});
  $fh->close;
  delete $::DirtyFiles{"$type:$path"};
}

sub info {
  my ($self, $key, $value) = @_;
  if ($value) {
    $value =~ tr/~/^/;
    $self->{info}{$key} = $value;
    $::DirtyFiles{"zinf:".$self->path} = 1 unless $key eq 'last_reset'; # FIXME: hardcoded exception
  }
  return $self->{info}{$key};
}

sub info_keys {
  my ($self) = @_;
  return keys %{$self->{'info'}};
}

sub info_delete {
  my ($self, $key) = @_;
  $::DirtyFiles{"zinf:".$self->path} = 1;
  return delete $self->{'info'}{$key};
}

sub file_loaded {
  my ($self, $type) = @_;
  
  return $self->{floaded}->{$type};
}

sub load_file {
  my ($self, $type) = @_;

  my $path = $self->{path};
  my $ref = "$type:$path";
  my $filepath = (parse_file_ref($ref))[2];


  # This must be set BEFORE the file is loaded, so that the tied hash
  # (if any) does not trigger another load. Also, it is done BEFORE the
  # nonexistence check, so that one does not get multiple "File not found"
  # messages. (A nonexistent file is equivalent to an empty one.)
  $self->{floaded}->{$type} = 1;

  -e $filepath or do {
    #mudlog "File not found: $ref ($filepath)";
    return 0;
  };   
  mudlog "Loading $ref";

       if ($type eq 'obj' ) { MObject->load_prototypes($ref);
  } elsif ($type eq 'zinf') { $self->load_zinf;
  } elsif ($type eq 'room') { MLoaders->load_rooms($ref);
  } else {
    mudlog "Bad fileref type in ".__PACKAGE__."::load_file: '$type'";
    return 0;
  }
  return 1;
}

sub key_store { # don't use unless you're MZonedHash
  my ($self, $type, $key, $value) = @_;
  
  return $self->{data}->{$type}->{$key} = $value;
}

sub key_delete { # don't use unless you're MZonedHash
  my ($self, $type, $key) = @_;
  
  return delete $self->{data}->{$type}->{$key};
}

sub all_things {
  my ($self, $type) = @_;
  $self->file_loaded($type) or $self->load_file($type);
  return wantarray ? values %{$self->{data}->{$type}} : MCollection->new(values %{$self->{data}->{$type}});
}

sub all_keys {
  my ($self, $type) = @_;
  $self->file_loaded($type) or $self->load_file($type);
  return keys %{$self->{data}->{$type}};
}

sub path {$_[0]{path}}

1;
