package MIndex;

use strict;
use vars qw(%Indices);

use MCoreTools;
use File::Path;

### Class methods ###########################################################################

sub initialize {
  my ($class) = @_;
  
  local *NDIR;
  opendir NDIR, rdir($::Config{db_path}.'/indexes') or return;
  foreach (readdir NDIR) {
    next unless /^(.*)\.oindex$/;
    MIndex->new($1);
    #MConnection->all_idle;
  }
  close NDIR;
}

sub new {
  my ($class, $name) = @_;

  my $self = bless {
    name => $name,
    'index' => {},
    rev => {},
    dirty => 1,
  }, $class;
  
  if (-e rfile(my $file = $::Config{db_path} . "/indexes/$name.oindex")) {
    mudlog "DB: Reading '$name' index file";
    my $fh = IO::File->new(rfile($file), '<') or do {mudlog "Error opening $file for reading: $!"; return};
    while (defined(my $line = <$fh>)) {
      next unless $line =~ /^(.*)\s+(\d+)$/;
      $self->{'index'}->{$1} = $2;
      $self->{rev}->{$2}->{$1} = 1;
    }
    $self->{dirty} = 0;
  } 
  $Indices{$name} = $self;
  
  return $self;
}

### Dual methods ###########################################################################

sub sync { if (ref $_[0]) {
  my ($self) = @_;
  
  return unless $self->{dirty};
  mudlog "DB: Writing '$self->{name}' index file";
  
  rmkpath($::Config{db_path}.'/indexes');
  
  my $file = $::Config{db_path} . "/indexes/$self->{name}.oindex";
  my $fh = IO::File->new(rfile($file), '>') or do {mudlog "Error opening $file for writing: $!"; return};
  foreach (keys %{$self->{'index'}}) {
    $fh->print("$_ $self->{'index'}->{$_}\n");
  }
  $self->{dirty} = 0;
  
} else {
  my ($class) = @_;
  
  foreach (values %Indices) {
    $_->sync;
  }
}}

sub get { if (ref $_[0]) {
  my ($self, $key) = @_;
  
  my $id = $self->{'index'}->{$key} or return;
  if (my $obj = MObjectDB->get($id)) {
    return $obj;
  } else {
    mudlog "DB: Deleting stale index key '$key' for object #$id in index '$self->{name}'";
    delete $self->{rev}->{$id};
    delete $self->{'index'}->{$key};
    return;
  }
} else {
  my ($class, $key) = @_;
  
  $key =~ s/^([^.]*)\.?//;
  my $name = $1;
  my $idx = $Indices{$name} or return;
  return $key ? $idx->get($key) : $idx;
}}

sub find_reverse { if (ref $_[0]) {
  my ($self, $id) = @_;

  $id = $id->id if ref $id;
  if (my $keys = $self->{rev}->{$id}) {
    return wantarray ? keys %$keys : (keys %$keys)[0];
  } else {
    return ();
  }
} else {
  my ($class, $id) = @_;

  my @res = map {my $idx = $_; map {"$idx->{name}.$_"} $idx->find_reverse($id)} values %Indices;
  return wantarray ? @res : $res[0];
}}

sub set { if (ref $_[0]) {
  my ($self, $key, $id) = @_;
  
  $id = $id->id if ref $id;
  
  $self->{dirty} = 1;
  if (defined $id) {
    $self->{rev}->{$id}->{$key} = 1;
    return $self->{'index'}->{$key} = $id;
  } else {
    delete $self->{rev}->{$id}->{$key};
    return delete $self->{'index'}->{$key};
  }
  
} else {
  my ($class, $key, $id) = @_;
  
  $key =~ s/^([^.]*)\.?//;
  my $name = $1;
  my $idx = $Indices{$name} or do {mudlog "DB: Attempted to add index entry '$key' in nonexistent index '$name'"; return};
  return $idx->set($key, $id);
}}

sub find { if (ref $_[0]) {
  my ($self, $key) = @_;
  
  #mudlog "DEBUG: in instance find(), key = $key";
  return unless defined $key;
  return grep /^\E$key/, keys %{$self->{'index'}};
} else {
  my ($class, $key) = @_;
  
  return unless defined $key;
  $key =~ s/^([^.]*)\.?//;
  my $name = $1;
  #mudlog "DEBUG: in class find(), index name = $name, key = $key";
  if (my $idx = $Indices{$name}) {
    return map {"$idx->{name}." . $_} $idx->find($key);
  } elsif (!$name) {
    return values %Indices;
  } else {
    my @idx = grep /^\E$name/, keys %Indices;
    return map {$idx = $Indices{$_}; map {"$idx->{name}.$_"} $idx->find($key)} @idx;
  }
}}

### Instance methods ###########################################################################

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

sub all {keys %{$_[0]{'index'}}}

###

1;
