package MFreezer;
use strict;
use vars qw(
  $USE
  $MARKER
  %AVAIL
);

use MCoreTools;
use Carp;

sub _initmod {
  my ($mod, $tag) = @_;
  eval "use $mod ()";
  if (!$@) {
    $AVAIL{$mod} = 1;
    if (!$USE) {
      $USE = $mod;
      $MARKER = $tag;
    }
  }
}

sub _reqmod {
  $AVAIL{$_[0]} or croak "Couldn't thaw: $_[0] not available";
}

BEGIN {
  SELECT: {
    _initmod('Storable',     'Stora') if ($^O ne 'MSWin32');
    _initmod('FreezeThaw',   'FThaw');
    _initmod('Data::Dumper', 'Dumpr');
    $USE or die "No freezer available! Please install Storable, FreezeThaw, or Data::Dumper.";
    #mudlog "Will use '$USE' for saving data files.";
  }
  if ($AVAIL{'Data::Dumper'}) {
    $Data::Dumper::Indent = 0;
    $Data::Dumper::Purity = 1;
  }
}

sub freeze {
  my ($struct) = @_;
  if ($USE eq 'Storable') {
    return $MARKER . Storable::nfreeze($struct);
  } elsif ($USE eq 'FreezeThaw') {
    return $MARKER . FreezeThaw::freeze($struct);
  } elsif ($USE eq 'Data::Dumper') {
    return $MARKER . Data::Dumper::DumperX($struct);
  }
}

sub thaw {
  my ($data) = @_;

  defined $data or croak "Undef passed to MFreezer::thaw";
  length $data >= 5 or croak "Data '$data' too short in MFreezer::thaw";
  my $marker = substr($data, 0, 5);
               substr($data, 0, 5) = '';
  if ($marker eq 'Stora') {
    _reqmod('Storable');
    return Storable::thaw($data);
  } elsif ($marker eq 'FThaw') {
    _reqmod('FreezeThaw');
    return FreezeThaw::thaw($data);
  } elsif ($marker eq 'Dumpr') {
    my $VAR1;
    $data =~ s/\r/\n/;
    local $SIG{__WARN__} = sub {
      my ($wt) = @_;
      return if $wt =~ /^Ambiguous use of (.+) => resolved to "\1" =>/;
      (my $lt = $wt) =~ s#\s*\n\s*(\S)# / $1#g;
      $lt =~ s/\n//g;
      warn $wt;
      mudlog "ERROR/CORE: warning while thawing saved code structure: $lt";
    };
    try {
      eval $data;
    } catch {
      /Insecure dependency in eval/ and croak "Couldn't thaw: Must be untainted";
    };
    return $VAR1;
  } else {
    croak "Couldn't thaw: Bad data type marker '$marker' in MFreezer::thaw";
  }
}

sub clone {
  my ($struct) = @_;
  if ($AVAIL{'Storable'}) {
    return Storable::dclone($struct);
  } elsif ($AVAIL{'Data::Dumper'}) {
    my $VAR1;
    eval Data::Dumper::Dumper($struct);
    return $VAR1;
  } else {
    croak "No cloning tool available!";
  }
}
1;
