#!perl
# Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved.
# This component and the accompanying materials are made available
# under the terms of the License "Eclipse Public License v1.0"
# which accompanies this distribution, and is available
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
# 
# Initial Contributors:
# Nokia Corporation - initial contribution.
# 
# Contributors:
# 
# Description:
# 
#

use strict;
use FindBin;
use lib "$FindBin::Bin";
use Getopt::Long;
use IniData;
use EnvDb;
use CommandController;
use CleanEnv;
use GetEnv;


#
# Constants.
#

my $KMissingFileName = "__missing.txt";
my $KCompsFileName = "__comps.txt";


#
# Globals.
#

my $verbose = 0;
my $reallyClean = 0;
my $force = 0;
my $snapShotFileName;
my $iniData = IniData->New();
my $commandController = CommandController->New($iniData, 'InstallSnapShot');


#
# Main.
#

ProcessCommandLine();
InstallSnapShot();


#
# Subs.
#

sub ProcessCommandLine {
  Getopt::Long::Configure ('bundling');
  my $help;
  GetOptions('h' => \$help, 'r' => \$reallyClean, 'f' => \$force, 'v+' => \$verbose);

  if ($help) {
    Usage(0);
  }

  $snapShotFileName = shift @ARGV;
  defined $snapShotFileName or die Usage(1);
  unless ($snapShotFileName =~ /\.zip$/i) {
    $snapShotFileName .= '.zip';
  }

  unless ($snapShotFileName and scalar(@ARGV) == 0) {
    print "Error: Invalid number of arguments\n";
    Usage(1);
  }
}

sub Usage {
  my $exitCode = shift;

  Utils::PrintDeathMessage($exitCode, "\nUsage: installsnapshot [options] <snap_shot_file_name>

options:

  -h  help
  -r  really clean
  -f  force (don't prompt)
  -v  verbose output (-vv very verbose)\n");
}

sub InstallSnapShot {
  my $newEnv = ReadSnapShotEnv();
  CheckEnvAvailable($newEnv);
  unless (CleanEnv::CleanEnv($iniData, $reallyClean, $force, $verbose)) {
    die "\nAborting because environment was not cleaned...\n";
  }
  print "Installing snapshot environment...\n";
  GetEnv::GetEnv($iniData, $newEnv, 0, undef, 0, 0, $verbose, undef, 0);
  print "Unpacking \"$snapShotFileName\"...\n";
  Utils::Unzip($snapShotFileName, Utils::EpocRoot(), $verbose, 1);
  my $problems = 0;
  unlink (Utils::PrependEpocRoot($KMissingFileName)) or (++$problems and print "Warning: Couldn't delete \"$KMissingFileName\": $!\n");
  unlink (Utils::PrependEpocRoot($KCompsFileName)) or (++$problems and print "Warning: Couldn't delete \"$KMissingFileName\": $!\n");
  my $missingFiles = ReadSnapShotMissingFiles();
  foreach my $thisMissingFile (@$missingFiles) {
    print "Removing \"$thisMissingFile\"...\n";
    unlink $thisMissingFile or (++$problems and print "Warning: Couldn't delete \"$thisMissingFile\": $!\n");
  }
  if ($problems) {
    print "There were problems installing this snapshot\n";
  }
  else {
    print "Snapshot \"$snapShotFileName\" successfully installed\n";
  }
}

sub CheckEnvAvailable {
  my $env = shift;
  print "Checking that all the component releases referred to by snap shot \"$snapShotFileName\" are available...\n" if ($verbose);
  my $pathData = $iniData->PathData();
  my $errors = 0;
  foreach my $thisComp (sort keys %$env) {
    unless ($pathData->ReleaseExists($thisComp, $env->{$thisComp})) {
      print "Error: $thisComp $env->{$thisComp} is referred to by snap shot \"$snapShotFileName\" but does not exist\n";
      $errors = 1;
    }
  }
  if ($errors) {
    die "Aborting (environment not altered)...\n";
  }
}

sub ReadSnapShotEnv {
  print "Reading snap shot environment details from \"$KCompsFileName\" within \"$snapShotFileName\"...\n" if ($verbose);
  Utils::InitialiseTempDir($iniData);
  my %env;
  eval {
    Utils::UnzipSingleFile($snapShotFileName, $KCompsFileName, Utils::TempDir(), $verbose);
    my $file = Utils::ConcatenateDirNames(Utils::TempDir(), $KCompsFileName);
    open (COMPS, $file) or die "Couldn't open \"$file\": $!\n";
    while (my $line = <COMPS>) {
      (my $comp, my $ver) = $line =~ /^(\S+)\s+(\S+)$/;
      unless ($comp and $ver) {
	die "Invalid line in \"$file\"\n";
      }
      $env{$comp} = $ver;
    }
    close (COMPS);
  };
  Utils::RemoveTempDir();
  if ($@) {
    die "Error: Problem reading environment from snap shot \"$snapShotFileName\": $@";
  }
  return \%env;
}

sub ReadSnapShotMissingFiles {
  print "Reading list of files missing from snap shot environment from \"$KMissingFileName\" within \"$snapShotFileName\"...\n" if ($verbose);
  Utils::InitialiseTempDir($iniData);
  my @missingFiles;
  eval {
    Utils::UnzipSingleFile($snapShotFileName, $KMissingFileName, Utils::TempDir(), $verbose);
    my $file = Utils::ConcatenateDirNames(Utils::TempDir(), $KMissingFileName);
    open (MISSING, $file) or die "Couldn't open \"$file\": $!\n";
    while (my $line = <MISSING>) {
      chomp $line;
      $line = Utils::PrependEpocRoot($line);
      push (@missingFiles, $line);
    }
    close (MISSING);
  };
  Utils::RemoveTempDir();
  if ($@) {
    die "Error: Problem reading missing files from snap shot \"$snapShotFileName\": $@";
  }
  return \@missingFiles;
}

__END__

=head1 NAME

InstallSnapShot - Installs a snap shot created with MakeSnapShot.

=head1 SYNOPSIS

  installsnapshot [options] <snap_shot_file_name>

options:

  -h  help
  -r  really clean
  -f  force (don't prompt)
  -v  verbose output (-vv very verbose)

=head1 DESCRIPTION

The release tools exist to make it relatively straight forward to share binary files in a controlled way. In order to acheive a suitable level of control, a fair amount of rigor is imposed on users when they are making releases. There are times when this is inappropriate. For example, if a user wants to temporarily capture the current state of their environment. The commands C<MakeSnapShot> and C<InstallSnapShot> exist to make it easy to accurately capture the current state of an environment, and subsequently revert to it, without the overhead of doing a full environment release. Snap shots should only be used in preference to full environment releases when there is a B<temporary> need to capture an environment, because:

=over 4

=item 1

No mechansims are provided for exporting or importing snap shots.

=item 2

No release notes are provided with snap shots.

=item 3

The contents of snap shots are inherently dirty - they consist of all the files that could not be accounted for with proper releases. Reliance on snap shots as a means of distributing software would therefore eventually become a self defeating activity since the snap shot files would get larger and larger over time.

=back

C<InstallSnapShot> uses a snap shot zip file generated by C<MakeSnapShot> to set the current environment state to that which the specified snap shot was made from. The following steps are performed:

=over 4

=item 1

The environment is cleaned. If the C<-r> option is specified, files that are normally ignored (e.g. the contents of F<\epoc32\build>) are also removed. If the C<-f> option is specified, the cleaning process is carried out without warning the user before deleting files and reinstalling components.

=item 2

Component releases are installed, removed or upgraded in such a way as to set the current environment to that which was present when the snap shot was made.

=item 3

The contents of the snap shot zip file is installed, overwriting exisitng files, thereby restoring the snap shot, but at the same time making the environment dirty.

=item 4

Any files that were missing from the snap shot environment are removed from the current environment.

=back

C<MakeSnapShot> generates a zip file that contains all the dirty files currently present in the environment. It makes no attempt to understand which component own which files. It also creates some metadata that list the component versions currently installed. This can subsequently be used by C<InstallSnapShot> to revert to the snap shot state.

=head1 STATUS

Supported. If you find a problem, please report it to us.

=head1 KNOWN BUGS

None.

=head1 COPYRIGHT

 Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
 All rights reserved.
 This component and the accompanying materials are made available
 under the terms of the License "Eclipse Public License v1.0"
 which accompanies this distribution, and is available
 at the URL "http://www.eclipse.org/legal/epl-v10.html".
 
 Initial Contributors:
 Nokia Corporation - initial contribution.
 
 Contributors:
 
 Description:
 

=cut
