#!/usr/bin/env perl

use Getopt::Long;

$son_cvsroot=":ext:joseki.sandia.gov:/Net/Proj/Xyce/CVS";
$srn_cvsroot=":ext:charleston.sandia.gov:/Net/Proj/Xyce/CVS";

$son_modules="Xyce Xyce_Regression Xyce_FastrackRegression";
$srn_modules="Xyce_SandiaRegression SandiaModels SandiaModelsDocs";

my @copyARGV = @ARGV;

&GetOptions( "branch_stub=s" => \$branch_stub,
             "tag_stub=s" => \$tag_stub,
             "help!" => \$helpflag,
             "create_branch!" => \$create_branch,
             "merge_from_head!" => \$merge_from_head,
             "merge_to_head!" => \$merge_to_head,
             "checkout_branch!" => \$checkout_branch,
             "checkout_headtag!" => \$checkout_headtag,
             "checkout_head!" => \$checkout_head,
             "son_cvsroot=s" => \$son_cvsroot,
             "srn_cvsroot=s" => \$srn_cvsroot,
             "son_modules=s" => \$son_modules,
             "srn_modules=s" => \$srn_modules,
             "branchdir=s" => \$branchdir,
             "headtagdir=s" => \$headtagdir,
             "headdir=s" => \$headdir,
             "debug!" => \$debug
           ) or $helpflag=1;

if ( (not defined $create_branch) and
     (not defined $merge_from_head) and
     (not defined $merge_to_head) and 
     (not defined $checkout_branch) and 
     (not defined $checkout_headtag) and
     (not defined $checkout_head)        ) { 
  $helpflag = 1; 
}

if (defined $helpflag) {
  print "Usage:\n";
  print "$0 [options]\n";
  print "--branch_stub=string       # Short stub to identify branch (e.g. COIL_GUN)\n";
  print "--tag_stub=string          # Even shorter stub to identify tags (e.g. CG)\n";
  print "--create_branch            # Creates branch and sets up tags\n";
  print "--checkout_branch          # cvs checkout the branch\n";
  print "--checkout_headtag         # cvs checkout the TAG on the HEAD\n";
  print "--merge_from_head          # Merges changes on head to branch\n";
  print "--merge_to_head            # Merges changes on branch to head\n";
  print "--son_cvsroot=string       # Specify the SON CVS ROOT\n";
  print "--srn_cvsroot=string       # Specify the SRN CVS ROOT\n";
  print "--son_modules=\"string\"     # Specify the SON modules to use\n";
  print "--srn_modules=\"string\"     # Specify the SRN modules to use\n";
  print "--branchdir=directory      # Define the working directory for the branch\n";
  print "--headtagdir=directory     # Define the working directory for the TAG on HEAD\n";
  print "--headdir=directory        # Define the working directory for the HEAD\n";
  print "--debug                    # Turn on debugging output\n";
  print "\n";
  print "Example usage:\n";
  print "First create the branch and associated tags:\n";
  print "$0 --branch_stub=COIL_GUN --tag_stub=CG --create_branch\n";
  print "Then checkout the branch and start working on it:\n";
  print "$0 --branch_stub=COIL_GUN --tag_stub=CG --headtagdir=$HOME/Project --branchdir=$HOME/Project/COIL_GUN_BRANCH --checkout_branch\n";
  print "Commit changes to branch just like normal\n";
  print "When the HEAD changes, bring those changes to the BRANCH (do this before merging BRANCH changes to HEAD also.\n";
  print "$0 --branch_stub=COIL_GUN --tag_stub=CG --headtagdir=$HOME/Project --branchdir=$HOME/Project/COIL_GUN_BRANCH --merge_from_head\n";
  print "When you're done with the branch and you want to merge everything back to HEAD:\n";
  print "$0 --branch_stub=COIL_GUN --tag_stub=CG --headtagdir=$HOME/Project --branchdir=$HOME/Project/COIL_GUN_BRANCH --merge_to_head\n";
  print "That's it.\n";
  print "\n";
  exit 1;
}

if ($srn_modules =~ m/none/) {
  $srn_modules = undef;
}
if ($son_modules =~ m/none/) {
  $son_modules = undef;
}

sub debugPrint {
  print @_ if ($debug);
}

debugPrint("\$son_modules = $son_modules\n");
debugPrint("\$srn_modules = $srn_modules\n");

if (not defined($branchdir)) {
  $branchdir = `pwd`; chomp($branchdir);
}
debugPrint "\$branchdir = $branchdir\n";

if (not defined($headtagdir)) {
  $headtagdir = `pwd`; chomp($headtagdir);
}
debugPrint "\$headtagdir = $headtagdir\n";

if (not defined($headdir)) {
  $headdir = `pwd`; chomp($headdir);
}
debugPrint "\$headdir = $headdir\n";

if (not $son_cvsroot =~ m/^-d/) {
  $son_cvsroot = "-d $son_cvsroot";
}
if (not $srn_cvsroot =~ m/^-d/) {
  $srn_cvsroot = "-d $srn_cvsroot";
}

# Define where these modules are normally checked out from CVS
# e.g.
# cd $branchdir/$son_modules_hash{'Xyce_FastrackRegression'}/
# cvs co Xyce_FastrackRegression
%son_modules_hash = ();
if ($son_modules =~ m/Xyce/) {
  $son_modules_hash{"Xyce"} = '/';
}
if ($son_modules =~ m/Xyce_Regression/) {
  $son_modules_hash{"Xyce_Regression"} = '/';
}
if ($son_modules =~ m/Xyce_FastrackRegression/) {
  $son_modules_hash{"Xyce_FastrackRegression"} = '/Xyce_Regression';
}
@son_modules_list = split(/\s+/,$son_modules);
debugPrint("\@son_modules_list = @son_modules_list\n");

# Define where these modules are normally checked out from CVS
# e.g.
# cd $branchdir/$srn_modules_hash{'SandiaModels'}/
# cvs co SandiaModels
%srn_modules_hash = ();
if ($srn_modules =~ m/SandiaModels/) {
  $srn_modules_hash{"SandiaModels"} = '/Xyce/src/DeviceModelPKG';
}
if ($srn_modules =~ m/SandiaModelsDocs/) {
  $srn_modules_hash{"SandiaModelsDocs"} = '/Xyce/doc';
}
if ($srn_modules =~ m/Xyce_SandiaRegression/) {
  $srn_modules_hash{"Xyce_SandiaRegression"} = '/Xyce_Regression';
}
@srn_modules_list = split(/\s+/,$srn_modules);
debugPrint("\@srn_modules_list = @srn_modules_list\n");

if (not defined($branch_stub)) {
  $branch_stub = "COIL_GUN";
}
if (not defined($tag_stub)) {
  $tag_stub = "CG";
}
$branch_name = "BRANCH_$branch_stub";
$create_tag_name = "TAG_CREATE_$branch_stub";
$head_merge_tag = "TAG_HEAD_MERGE_TO_$tag_stub";
$branch_merge_tag = "TAG_${tag_stub}_MERGE_FROM_HEAD";
debugPrint("\$branch_name = $branch_name\n");
debugPrint("\$create_tag_name = $create_tag_name\n");
debugPrint("\$head_merge_tag = $head_merge_tag\n");
debugPrint("\$branch_merge_tag = $branch_merge_tag\n");

# Print out the input arguments
print "#!/bin/sh\n";
print "# Input Arguments: $0 @copyARGV\n";

if (defined $create_branch) {
  if (defined $son_modules) {
    create_branch($son_cvsroot,\@son_modules_list);
  }
  if (defined $srn_modules) {
    create_branch($srn_cvsroot,\@srn_modules_list);
  }
}

if (defined $checkout_branch) {
  my $cocmd = "co -r $branch_name";
  debugPrint "\$cocmd = $cocmd\n";
  if (defined $son_modules) {
    coXyce($cocmd,$son_cvsroot,\@son_modules_list,\%son_modules_hash,$branchdir);
  }
  if (defined $srn_modules) {
    coXyce($cocmd,$srn_cvsroot,\@srn_modules_list,\%srn_modules_hash,$branchdir);
  }
}

if (defined $checkout_headtag) {
  my $cocmd = "co -r $head_merge_tag";
  debugPrint "\$cocmd = $cocmd\n";
  if (defined $son_modules) {
    coXyce($cocmd,$son_cvsroot,\@son_modules_list,\%son_modules_hash,$headtagdir);
  }
  if (defined $srn_modules) {
    coXyce($cocmd,$srn_cvsroot,\@srn_modules_list,\%srn_modules_hash,$headtagdir);
  }
}

if (defined $checkout_head) {
  my $cocmd = "co";
  debugPrint "\$cocmd = $cocmd\n";
  if (defined $son_modules) {
    coXyce($cocmd,$son_cvsroot,\@son_modules_list,\%son_modules_hash,$headdir);
  }
  if (defined $srn_modules) {
    coXyce($cocmd,$srn_cvsroot,\@srn_modules_list,\%srn_modules_hash,$headdir);
  }
}

if (defined $merge_from_head) {
#####
# Warning, the branch must not have commits made to it during this operation.
#
# To fix this in the future, make tags on working directories instead.
#####
  if (defined $son_modules) {
    merge_from_head($son_cvsroot,\@son_modules_list,\%son_modules_hash);
  }
  if (defined $srn_modules) {
    merge_from_head($srn_cvsroot,\@srn_modules_list,\%srn_modules_hash);
  }
}

if (defined $merge_to_head) {
  if (defined $son_modules) {
    merge_to_head($son_cvsroot,\@son_modules_list,\%son_modules_hash);
  }
  if (defined $srn_modules) {
    merge_to_head($srn_cvsroot,\@srn_modules_list,\%srn_modules_hash);
  }
}

sub create_branch {
  my ($cvsroot,$modules_list_ref) = @_;
  my @modules_list = @$modules_list_ref;
  print "# Create branch for modules @modules_list:\n";
  print "cvs $cvsroot rtag $create_tag_name @modules_list\n";
  print "cvs $cvsroot rtag -b -r $create_tag_name $branch_name @modules_list\n";
  print "# Create merge tags on head and branch:\n";
  print "cvs $cvsroot rtag $head_merge_tag @modules_list\n";
  print "cvs $cvsroot rtag -r $branch_name $branch_merge_tag @modules_list\n";

}


sub coXyce {
  my ($cocmd,$cvsroot,$modules_list_ref,$modules_hash_ref,$workdir) = @_;
  my @modules_list = @$modules_list_ref;
  my %modules_hash = %$modules_hash_ref;
  debugPrint "coXyce: \$workdir = $workdir\n";
  debugPrint "coXyce: \$cvsroot = $cvsroot\n";
  my @module_keys = keys(%modules_hash);
  debugPrint "coXyce: keys \%modules_hash = @module_keys\n";
  debugPrint "coXyce: \$cocmd = $cocmd\n";
  foreach $module (@modules_list) {
    my $moduledir = $modules_hash{"$module"};
    debugPrint "coXyce:  \%modules_hash:  $module => $moduledir\n";
    print("cd $workdir$moduledir\n"); 
    print("cvs $cvsroot $cocmd $module\n");
  }
}

sub merge_from_head {
  my ($cvsroot,$modules_list_ref,$modules_hash_ref) = @_;
  my @modules_list = @$modules_list_ref;
  my %modules_hash = %$modules_hash_ref;
  foreach $module (@modules_list) {
    my $moduledir = $modules_hash{"$module"};
    print "#\n# Merge changes from HEAD for module $module\n#\n";
    print "cd $branchdir$moduledir/$module\n";
    print "cvs update -kk -j$head_merge_tag -jHEAD\n";
    print "# Move tag on HEAD:\n";
    print "cvs $cvsroot rtag -F $head_merge_tag $module\n";
    print "# Fix conflicts, compile, run tests\n";
    print "# Commit changes on branch\n";
    print "cvs commit\n";
    print "# Move tag on BRANCH:\n";
    print "cvs $cvsroot rtag -F -r $branch_name $branch_merge_tag $module\n";
  }

}

sub merge_to_head {
  my ($cvsroot,$modules_list_ref,$modules_hash_ref) = @_;
  my @modules_list = @$modules_list_ref;
  my %modules_hash = %$modules_hash_ref;
  foreach $module (@modules_list) {
    my $moduledir = $modules_hash{"$module"};
    print "#\n# Merge changes to HEAD for module $module#\n\n";
    print "cd $headtagdir$moduledir/$module\n";
    print "cvs update -kk -j$branch_name\n";
    print "# Fix conflicts, compile, run tests\n";
    print "# Commit changes on head\n";
    print "cvs commit\n";
  }
}
