# Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved.
# This component and the accompanying materials are made available
# under the terms of "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:
#

#!perl

use strict;
use File::Copy;
use File::Path;
use File::stat;
use Getopt::Long;
use Cwd;
use FindBin;


if ($^O =~ /MSWin32/)
{
  require Win32::Mutex;
  require Win32::File;
}


$::emulatorCfg = 'emulator.cfg';
$::targetCfg = 'target.cfg';

GetOptions (
           'command=s' => \$::command,
           'platform=s' => \$::platform,
           'variant=s' => \$::variant,
           'targetcfg=s' => \$::targetCfg,
           'emulatorcfg=s' => \$::emulatorCfg,
           'sourceDir=s' => \$::sourceDir,
           'platsec!' => \$::platsec);

#
# Constants.
#

my $epocRoot = GetEpocRoot();
my $EpocWinsStore = 'c:\defaultcommdb';
my $emptyDatabase = 'empty.txt';

my $emulatorstore = $epocRoot . 'epoc32/winscw/c/defaultcommdb';

my $release = $epocRoot . 'epoc32/release/';
my $winscedUREL = $release . 'wins/urel/ced.exe';
my $winscwcedUREL = $release . 'winscw/urel/ced.exe';
my $winscedUDEB = $release . 'wins/udeb/ced.exe';
my $winscwcedUDEB = $release . 'winscw/udeb/ced.exe';

# Linux static copies
my $bindir = $FindBin::Bin.'/../bin/';
my @bincopies=(
	# plat       var     source                  destination
	['emulator', 'both', $bindir.'commsdb.dat',  $emulatorstore.'/commsdb.dat'],
	['emulator', 'udeb', $bindir.'cccccc00.cre', $epocRoot.'epoc32/release/winscw/udeb/z/private/10202be9/cccccc00.cre'],
	['emulator', 'urel', $bindir.'cccccc00.cre', $epocRoot.'epoc32/release/winscw/urel/z/private/10202be9/cccccc00.cre'],
	['target',   'both', $bindir.'cccccc00.cre', $epocRoot.'epoc32/data/z/private/10202be9/cccccc00.cre']
	);

#
# Main.
#
unless ($::command and $::platform and $::variant and (scalar(@ARGV) == 0)) 
{
	die "CreateCommdbs Error: Invalid arguments\n";
}

my $platType;
if ($::platform =~ /^(wins|wincw)/i) 
{
	$platType = 'emulator';
}
else 
{
	$platType = 'target';
}

#
# Check OS, and perform copies of key binaries from source if dynamic generation in unsupported
#

if (!($^O =~ /MSWin32/))
	{
	foreach my $bincopy (@bincopies)
		{
		my ($copyplat, $copyvar, $source, $destination) = @$bincopy;

		if (($copyplat eq $platType) and (($copyvar eq lc($::variant)) or ($copyvar eq "both")))
			{
			if (($::command eq "build") and not (-e $destination))
				{
				# Existing files are considered to be current
				print ("Warning: createcommdbs.pl: \'$destination\' generated by a copy from source as building is not supported on this platform.\n");
				print ("Copy \'$source\' to \'$destination\'\n");
				CopyFile($source, $destination);
				}
			elsif (($::command eq "clean") and (-e $destination))
				{
				unlink ($destination)
				}
			elsif ($::command eq "releasables")
				{
				print ("$destination\n");
				}
			}
		}
	exit;
	}

my $cccccc00Root;
my $cccccc00NameCre;
my $cccccc00Nametxt;

$cccccc00Root = "/private/10202be9/";
$cccccc00NameCre = "cccccc00.cre";
$cccccc00Nametxt = "cccccc00.txt";

my $data = 
{
	target => 
	{
		sourceCfg => "$::sourceDir/$::targetCfg",
		cedCfg => "$EpocWinsStore\\$::targetCfg",
		emptyDb => "$::sourceDir/$emptyDatabase",
		destinationCdb => $emulatorstore . '/CommsDb.dat',
	},
	emulator => 
	{
		sourceCfg => "$::sourceDir\\$::emulatorCfg",
		cedCfg => "$EpocWinsStore\\$::emulatorCfg",
		emptyDb => "$::sourceDir\\$emptyDatabase",
		destinationCdb => $emulatorstore . '/CommsDb.dat',
	}
};

my $ced;
my $cedPlatfrom;
my $cedVariant;
my $emulatorstore;
my $CopiedCfg;
my $CedLog;
my $winsCdb;
my $CdbEmpty;
my $romCdb;
my $attrib;
my $cdbIn;
my $cdbOut;
my $cdbDatPath;
my $cdbDatFile;
my $cdbDatExt;
my $zDrive;
my $zDriveCre;

#
# Establish location of ced.exe
#
if ($platType =~ 'emulator')
{
	$cedPlatfrom = $::platform;
	$cedVariant = $::variant;
}
elsif (($platType =~ 'target') && (-e $winscwcedUREL))
{
	$cedPlatfrom = 'WINSCW';
	$cedVariant = 'UREL';
}
elsif (($platType =~ 'target') && (-e $winscwcedUDEB))
{
	$cedPlatfrom = 'WINSCW';
	$cedVariant = 'UDEB';
}
elsif (($platType =~ 'target') && (-e $winscedUREL))
{
	$cedPlatfrom = 'WINS';
	$cedVariant = 'UREL';
}
elsif (($platType =~ 'target') && (-e $winscedUDEB))
{
	$cedPlatfrom = 'WINS';
	$cedVariant = 'UDEB';
}

$cdbIn = '/c/defaultcommdb/';
$cdbOut = '/c/defaultcommdb/';

$CopiedCfg = $epocRoot . 'epoc32/' . $cedPlatfrom . $cdbIn . $platType . '.cfg';
$CedLog = $CopiedCfg . '.log';

if  ($ENV{DEFAULTCOMMSDATDATAFILE})
{
	($cdbDatPath, $cdbDatFile, $cdbDatExt) = SplitFileName($ENV{DEFAULTCOMMSDATDATAFILE});
}
else
{
	$cdbDatPath = $epocRoot. 'epoc32/' . $cedPlatfrom . $cdbOut;
	$cdbDatFile = 'CommsDb';
	$cdbDatExt = '.dat';
}

$zDrive = $release . $cedPlatfrom . '/' . $cedVariant . '/z';
$zDriveCre = $zDrive . $cccccc00Root . $cccccc00NameCre;
$CdbEmpty = $zDrive . $cccccc00Root . $cccccc00Nametxt;

if ($::command eq 'build') 
{
	$ced = $release . $cedPlatfrom . '/' . $cedVariant . '/ced.exe';
	$emulatorstore = $epocRoot . 'epoc32/' . $cedPlatfrom . $cdbIn;
	$winsCdb = $epocRoot . 'epoc32/' . $cedPlatfrom . '/c' . $cccccc00Root . "persists/" . $cccccc00NameCre;
	$romCdb = $epocRoot . 'epoc32/' . 'data/z' . $cccccc00Root . $cccccc00NameCre; 
	
	if ($platType =~ 'emulator')
	{
		$CopiedCfg = $emulatorstore . 'emulator.cfg';
	}
	else
	{
		$CopiedCfg = $emulatorstore . 'target.cfg';
	}
	
	PreBuild();
}
elsif ($::command eq 'clean')
{
	Clean();
}
elsif ($::command eq 'releasables') 
{
	Releasables();
}

#
# Subs.
#

sub PreBuild
{
	if ($^O =~ /MSWin32/)
	{
		# We cannot have more than one instance of this script's activites running at the same time on Windows, so
		# we safeguarded everything with a Windows mutex.  Mutex name needs to be unique for every build,
		# so we add the current working directory and EPOCROOT to the mutex name

		my $drive = Win32::GetCwd();
		$drive =~ s/^(\D:).*/$1/;
		my $mutexname = $drive.lc($ENV{'EPOCROOT'});
		$mutexname =~ s/[:\\\/]+/_/g;

		my $mutex = Win32::Mutex->new(1,"CEDMUTEX_".$mutexname);
		die "Unable to create mutex, createcommdbs.pl not run" if not $mutex;
		if ($mutex->wait())
		{
			Build();
			$mutex->release();
		}
		else
		{
			die "Unable to access mutex, createcommdbs.pl not run";
		}
	}
	else
	{
		Build();
	}
}

sub Build
{
	print("\n**Constructing $::platform $::variant database**\n");
	print("**$winsCdb\n\n");
	# delete old log file if it exists
	DeleteFile($CedLog);

	#Check for empty cenrep initialisation  .txt file and store if missing
	if (!(-e $CdbEmpty))
	{
		print("\nCopying $data->{$platType}->{emptyDb} to $CdbEmpty\n");
		CopyFile($data->{$platType}->{emptyDb}, $CdbEmpty);
		print("Remove ReadOnly attribute! from $CdbEmpty \n\n");
		if ($^O =~ /MSWin32/)
		{
			no strict;  # Not nice this, but avoids compilation errors on Linux
			Win32::File::SetAttributes($CdbEmpty, NORMAL) || die $!;
		}
	}

	# Ensure CFG file exists
	if (-e $data->{$platType}->{sourceCfg})
	{
		print("Copying $data->{$platType}->{sourceCfg} ... \n");
		CopyIfYounger($data->{$platType}->{sourceCfg}, $CopiedCfg);
		print("copied successfully to $CopiedCfg\n\n");

		# Ensure replacement file is more up-to-date than existing file
		if (FileIsYounger($CopiedCfg, $data->{$platType}->{destinationCdb}))
		{
			print("$CopiedCfg newer than $data->{$platType}->{destinationCdb}\n");
			RunCed();

			if ($@)
			{
				die $@;
			}
			print("\n");
		}
		else 
		{
			print("$data->{$platType}->{sourceCfg} older than $data->{$platType}->{destinationCdb}\n");
			print("Construction aborted.\n");
		}
	}
	else 
	{
		print("CreateCommdbs Error: $data->{$platType}->{sourceCfg} does not exist, construction aborted.\n");
	}

	print("Deleting $CopiedCfg\n");
	DeleteFile($CopiedCfg);
	
}


sub Clean 
{
	print("$data->{$platType}->{destinationCdb}\n");
	DeleteFile($data->{$platType}->{destinationCdb});
	print("Deleting $CopiedCfg\n");
	DeleteFile($CopiedCfg);
	print("Deleting $CedLog\n");
	DeleteFile($CedLog);
	print("Deleting $cdbDatPath$cdbDatFile$cdbDatExt\n");
	DeleteFile("$cdbDatPath$cdbDatFile$cdbDatExt");
}

sub Releasables 
{
	print("$data->{$platType}->{destinationCdb}\n");
	print("$CdbEmpty\n");
}

sub RunCed 
{
	print("sub RunCed\n");

	print("Running $ced -Mconsole -dJustInTime=0 -dtextshell -- -i $data->{$platType}->{cedCfg} -d -o $data->{$platType}->{cedCfg}.log\n");

	system ("$ced -Mconsole -dJustInTime=0 -dtextshell -- -i $data->{$platType}->{cedCfg} -d -o $data->{$platType}->{cedCfg}.log");

	CheckCedLog("$CopiedCfg.log");
	CheckEpocLog();

	#copy to output location
	print("Copy $winsCdb to $data->{$platType}->{destinationCdb}\n");
	CopyFile($winsCdb, $data->{$platType}->{destinationCdb});

	#copy to z drive
	print("Copy $winsCdb to $zDriveCre\n");
	CopyFile($winsCdb, $zDriveCre);

	#print("Copy $winsCdb to $romCdb\n");
	#CopyFile($winsCdb, $romCdb);
}

sub CheckCedLog 
{
	print("sub CheckCedLog\n");
	my $log = shift;
	open (LOG, $log) or die "CreateCommdbs Error: Couldn't open \"$log\" for reading: $!\n";
	
	my @log = reverse <LOG>;
	close (LOG);
	unless ($log[0] eq "SUCCESS\n") 
	{
		die "CreateCommdbs Error: CED failed to generate commdb, see \"$log\" for details\n";
	}
}

sub CheckEpocLog
{
	print("sub CheckEpocLog\n");
	my $log = $ENV{'TEMP'}."\\epocwind.out";
	unless (open (LOG, $log))
	{
		print "CreateCommdbs Error: Couldn't open \"$log\" for reading: $!\n";
		return;
	}
	
	foreach my $line (<LOG>) 
	{
		if ($line =~ m/ced\.exe.+panic/i)
		{
			print "CreateCommdbs Error: CED panic detected, see \"$log\" for details\n";
		}
	}

	close(LOG);
}

sub CopyFile 
{
	my $file1 = shift;
	my $file2 = shift;
	(my $path) = SplitFileName($file2);
	
	unless (-e $path) 
	{
		mkpath ($path) or die "CreateCommdbs Error: Couldn't make path \"$path\": $!\n";
	}
	
	if (-e $file2 and not -w $file2) 
	{
		system "attrib -r $file2";
	}
	copy ($file1, $file2) or die "CreateCommdbs Error: Couldn't copy \"$file1\" to  \"$file2\": $!\n";
}

sub MoveFile 
{
	my $file1 = shift;
	my $file2 = shift;
	(my $path) = SplitFileName($file2);
	
	unless (-e $path) 
	{
		mkpath ($path) or die "CreateCommdbs Error: Couldn't make path \"$path\": $!\n";
	}
	move ($file1, $file2) or die "CreateCommdbs Error: Couldn't move \"$file1\" to  \"$file2\": $!\n";
}

sub DeleteFile 
{
	my $file = shift;
	if (-e $file) 
	{
		unlink ($file) or die "CreateCommdbs Error: Couldn't delete \"$file\": $!\n";
	}
}

sub SplitFileName 
{
	my $fileName = shift;
	my $path = '';
	my $base = '';
	my $ext = '';
	
	if ($fileName =~ /^(.*\/)/) 
	{
		$path = $1;
	}
	if ($fileName =~ /\/?([^\/]*?)(\.[^\/\.]*)?$/) 
	{
		$base = $1;
	}
	if ($fileName =~ /(\.[^\/\.]*)$/o) 
	{
		$ext =  $1;
	}
	
	die unless ($fileName eq "$path$base$ext");
	return ($path, $base, $ext);
}

sub CopyIfYounger 
{
	my $from = shift;
	my $to = shift;
	unless (-e $from) 
	{
		die "Error: \"$from\" not found\n";
	}
	
	if (FileIsYounger($from, $to)) 
	{
		CopyFile($from, $to);
	}
}

sub FileIsYounger 
{
	my $file1 = shift;
	my $file2 = shift;
	return (FileModifiedTime($file1) > FileModifiedTime($file2));
}

sub FileModifiedTime 
{
	my $file = shift;
	if (-e $file) 
	{
		my $st = stat($file) or return 0;
		return $st->mtime;
	}
	return 0;
}

sub RenameFile
{
	my $file1 = shift;
	my $file2 = shift;
	
	rename ($file1, $file2) or die "installCommdb Error: Couldn't rename \"$file1\" to \"$file2\": $!\n";
}

sub GetEpocRoot
{
  my $path = $ENV{EPOCROOT};

  # replace to forward slashes
  $path =~ s/\\/\//g;

  # append trailing slash if one isn't already present
  if($path =~ m/.*\/$/)
  {
    return $path;
  }
  else
  {
    return $path."\/";
  }
}
