#!/usr/bin/perl -w
#
=head1 NAME

cbusterize - Creates the correct CasparBuster structure in your CMS environment

=head1 SYNOPSIS

cbusterize.pl [options] --source /path/to/file/to/CasparBusterize

 Options:
   --debug  |-d			debug mode
   --help   |-h			brief help message
   --man			full documentation
   --source |-s <file/dir>	directory or file to copy in the CasparBuster tree
   --machine|-m <machine>	machine to consider in the subtree

=head1 OPTIONS

=over 4

=item B<--debug>

Enter debug mode. This will print what would be done. No commands are executed,
so this is safe to use when testing.

=item B<--help>

Print a brief help message and exits.

=item B<--man>

Prints the manual page and exits.

=item B<--machine> I<machine name>

Specify the machine to consider when dealing with the CasparBuster structure. 
The file will be taken from this machine, and a subdirectory named after the machine 
will be used under the basedir to host the directory structure to manage

=item B<--source> I<path>

Specify the path to the source file or directory to manage with CasparBuster.

=back

=head1 DESCRIPTION

Creates a directory under the machine dir passed as parameter in working 
directory or directory passed as parameter named like the last path
element of the parameter, and creates the standard CasparBuster setup 
that refers to the parameter path in the new directory, and configuration 
files when possible. It also copies the original config file into the new dir.
Is reasonably picky about path names, tries to avoid common errors.

Schema looks like:

Base dir
   |
   |- machine1 (optional)
   |     |
   |     |-- conf dir1
   |     |       |
   |     |       |- conf file 1
   |   [...]    [...]
   | 
   |- machine2 (optional)
   |     |
   |     |-- conf dir2
   |     |       |
   |     |       |- conf file 2
   |   [...]    [...]

Use of machines require use of option -m (if using cbusemachines in cb.conf)
If not, the conf dirs are directly attached to the base dir

=head1 EXAMPLES

	# this will create the appropriate CasparBuster environment
	# under the base ~/prj/musique-ancienne.org directory (Cf cbbasedir in cb.conf)
	# containing the directory victoria2 for this machine
	# under which it will copy the required structure if needed (/etc/ssh)
	# to finaly put a copy of the file sshd_conf in it from the victoria2 machine

	cbusterize -m victoria2 -s /etc/ssh/sshd_config

=head1 AUTHOR

=over 4

Bruno Cornec, http://brunocornec.wordpress.com

=back

=head1 LICENSE

Copyright (C) 2012  Bruno Cornec <bruno@project-builder.org>
Released under the GPLv2 or the Artistic license at your will.

=cut
use strict;
use Cwd 'realpath';
use File::Find;
use File::Copy;
use File::Basename;
use File::Path;
use File::Glob ':glob';
use Getopt::Long;
use Pod::Usage;
use List::Util qw(first);
use ProjectBuilder::Base;
use ProjectBuilder::Conf;
use CasparBuster::Env;

# settings
my $debug = 0;
my $help = 0;
my $man = 0;
my $source = undef;
my $machine = undef;
my $quiet = undef;
my $log = undef;
my $LOG = undef;

my ($cbver,$cbrev) = pb_version_init();
my $appname = "cb";
$ENV{'PBPROJ'} = $appname;
pb_temp_init();

# Initialize the syntax string
pb_syntax_init("$appname (aka CasparBuster) Version $cbver-$cbrev\n");

# parse command-line options
GetOptions(
	'machine|m=s' => \$machine,
	'debug|d+'    => \$debug,
	'help|h'      => \$help,
	'quiet|q'     => \$quiet,
	'man'         => \$man,
	'log-files|l=s' => \$log,
	'source|s=s'  => \$source,
) || pb_syntax(-1,0);

if (defined $help) {
	pb_syntax(0,1);
}
if (defined $man) {
	pb_syntax(0,2);
}
if (defined $quiet) {
	$debug=-1;
}
if (defined $log) {
	open(LOG,"> $log") || die "Unable to log to $log: $!";
	$LOG = \*LOG;
	$debug = 0  if ($debug == -1);
}
pb_log_init($debug, $LOG);
pb_log(0,"Starting cbusterize\n");

# Get conf file in context
pb_conf_init($appname);
# The system one
pb_conf_add(cb_env_conffile());
# The personal one if there is such
pb_conf_add("$ENV{'HOME'}/.cbrc") if (-f "$ENV{'HOME'}/.cbrc");

# Get configuration parameters
my ($basedir,$opt,$usemach,$pluginsdir,$cms) = pb_conf_get("cbbasedir","cbdatabase","cbusemachines","cbpluginssubdir","cbcms");

# Check for mandatory params
pod2usage("Error: --source is a mandatory argument\n") (if not defined $source);
pod2usage("Error: --machine is a mandatory argument when configure with cbusemachines = true\n") if ($usemach->{$appname} =~ /true/) && (not defined $machine));

# Are the source and basedir full path names ? if not, make it such
$source = realpath($source);
$basedir = realpath($basedir->{$appname});

# debug mode overview
if ($debug) {
	print <<EOF;
DEBUG MODE, not doing anything, just printing
DEBUG: basedir = $basedir
DEBUG: source  = $source
EOF
	if (defined ($machine)) {
		print "DEBUG: machine = $machine\n";
	}
}

# Create basedir if it doesn't exist
if (not -d $basedir) {
	if ($debug) {
		print "DEBUG: Creating recursively directory $basedir\n";
	} else {
		mkpath($basedir,0,0755) or die "Unable to recursively create $basedir";
		# TODO: Add it to the CMS
	}
}

# Is the source a file or a dir ? Split the source parameter in 2
my $srcdir = undef;
my $srcfile = undef;
if (-d $source) {
	$srcdir = $source;
} else {
	$srcdir = dirname($source);
	$srcfile = basename($source);
	}

if ($debug) {
	print "DEBUG: Found srcdir = $srcdir\n";
	if (defined $srcfile) {
		print "DEBUG: Found srcfile = $srcfile\n";
	} else {
		print "DEBUG: Found no srcfile\n";
	}
}

# Find the include/install.mk file by looking up the tree 
# from basedir/machine or basedir if no machine given
my $sdir1 = $basedir;
$sdir1 .= "/$machine" if defined ($machine);
my $sdir = $sdir1;
while (! -f "$sdir/include/install.mk") {
	$sdir = dirname($sdir);
	if ($sdir eq "/") {
		die <<EOF;
ERROR: No include/install.mk file was found while 
       parsing $sdir1 and its parents
       Your tree is not CasparBuster compliant. 
       Please fix it before relaunching CasparBuster.pl
EOF
	}
}
if ($debug) {
	print "DEBUG: Found sdir = $sdir\n";
}
# Now checking conformity
print "CHECK: $sdir/include/install.mk\n" if ($debug);
my $cf = 0;
my $remote = undef;
open(MF,"$sdir/include/install.mk");
while (<MF>) {
	if (($_ =~ /csp_UHOST\s*=/) && ($_ =~ /(\w+\@$machine.*)/)) {
		$remote = $1;
		$cf++; 
		if ($debug) {
			print "DEBUG: csp_UHOST conform line found with ref to $machine\n";
			print "DEBUG: remote configuration is $remote\n";
		}
	}
	if (/include\s+CasparBuster\/mk\/CasparBuster.mk/) {
		$cf++; 
		if ($debug) {
			print "DEBUG: include conform line found with ref to CasparBuster.mk\n";
		}
	}
}
close(MF);
if ($cf < 2) {
	print "WARNING: Non conform $sdir/include/install.mk file found\n";
}
die "No remote configuration defined in $sdir/include/install.mk" if (not defined $remote);

my $target = "$sdir1/$srcdir";
# If both source and target are dirs, then copy into the parent of the target
$target = basename($target) if ((not defined $srcfile) && (-d $target));

# Create Makefiles if none exists also in parents, warn else
my $tdir = $target;
my $sd = $srcdir;
while (! -f "$tdir/Makefile") {

	# Create target if it doesn't exist
	if (not -d $tdir) {
		if ($debug) {
			print "DEBUG: Creating recursively directory $tdir\n";
		} else {
			mkpath($tdir,0,0755) or die "Unable to recursively create $tdir";
			print "INFO: Created $tdir you may want to add it to your CMS\n";
		}
	}

	if (-f "$tdir/Makefile") {
		print "CHECK: $tdir/Makefile\n" if ($debug);
		# Now checking conformity
		$cf = 0;
		open(MF,"$tdir/Makefile") || die "Unable to open $tdir/Makefile";
		while (<MF>) {
			my $sd1 = $sd;
			$sd1 =~ s|/|\/|g;
			if (/csp_DIR\s*=\s*$sd1/) {
				$cf++; 
				if ($debug) {
					print "DEBUG: csp_DIR conform line found with ref to $sd\n";
				}
			}
			$sd1 = $sdir;
			$sd1 =~ s|/|\/|g;
			if (/include\s+$sd1[\/]*install.mk/) {
				$cf++; 
				if ($debug) {
					print "DEBUG: include conform line found with ref to install.mk\n";
				}
			}
			close(MF);
	
			if ($cf < 2) {
				print "WARNING: Non conform $tdir/Makefile file found\n";
			}
		}
	} else {
		# In this case we create one
		if ($debug) {
			print <<EOF;
DEBUG: Creating $tdir/Makefile with the following content:
csp_DIR = $sd
include $sdir/include/install.mk
EOF
		} else {
			my $mf = "$tdir/Makefile";
			$mf =~ s|//|/|g;
			open(MF,"> $mf") || die "Unable to create $mf";
			print MF <<EOF;
# Created by CasparBuster.pl - \$Id\$
csp_DIR = $sd
# That include should be last of these declarations
include $sdir/include/install.mk
EOF
			close(MF);
			print "INFO: Created $mf you may want to add it to your CMS\n";
		}
	}
	$tdir = dirname($tdir);
	$sd = dirname($sd);
	if ($debug) {
		print "DEBUG: Next round, tdir = $tdir and sd = $sd\n";
	}
	# Stop at basedir
	last if ($tdir eq $basedir);
}

# We need to know where to get the content from
my $cmd;
my $cmdopt = "";

# Recursive if we copy dirs
$cmdopt = "-r" if (not defined $srcfile);

if (defined $machine) {
	$cmd = "scp -p -q $cmdopt $remote:$source $target";
} else {
	$cmd = "cp -p $cmdopt $source $target";
}
# Now add content if not already there
if ((defined $srcfile) && (! -f "$target/$srcfile")) {
	if ($debug) {
		print "DEBUG: launching $cmd\n";
	} else {
		system($cmd);	
		print "INFO: Created $target/$srcfile you may want to add it to your CMS\n";
	}
}
