#!/usr/bin/perl -w

use strict;
use Getopt::Long;
use File::Temp;
use FindBin qw($Bin);
use lib "$Bin";
use TasukeVariantExistMap;
#use Data::Dumper;

my $BIN_DMATRIX = 'converter/makeBinaryDistanceMatrix';

my $INFILE = '';
my $OUTFILE = '';
#my $BCFTOOLS = '';
my $SKIP_NA_COL = 0;
my $AS_HAPLOID = 0;
my $NOT_DELETE_BINTABLE = 0;
my $METHOD = '';
my $VTYPES = '';
my $TMPDIR = '';
my $SMPLMAP;
my $PHYLIPDIR = '';

################################################################
# Checking parameters and database connection
################################################################
GetOptions(
	'i|infile=s',	=> \$INFILE,
	'o|outfile=s'	=> \$OUTFILE,
#	'b|bcftools=s'	=> \$BCFTOOLS,
	'm|method=s'	=> \$METHOD,
	'n|skipnacol'	=> \$SKIP_NA_COL,
	'd|haploid'	=> \$AS_HAPLOID,
	'l|nodel'	=> \$NOT_DELETE_BINTABLE,
	'v|vtypes=s'	=> \$VTYPES,
	't|tmpdir=s'	=> \$TMPDIR,
	's|smplmap=s'	=> \$SMPLMAP,
	'y|phylip=s'	=> \$PHYLIPDIR
);



if( !(-f "$Bin/$BIN_DMATRIX") ) {
	print STDERR "Dependency cannot be found: $Bin/$BIN_DMATRIX\n";
	exit(1);
}

if(!$INFILE || !(-f $INFILE)) {
	print STDERR "VCF file not fount: $INFILE\n";
	_error_method();
	exit(1);
}

if(!$OUTFILE) {
	print STDERR "[Warning!!!] Outfile(-o) is not specified. Output result to STDOUT.\n";
}

my %methods = ('jaccard' => 1, 'dice' => 1, 'simple' => 1, 'soergel' => 1);
if($METHOD && !exists($methods{$METHOD})) {
	print STDERR "Unknown method type: $METHOD\n";
	exit(1);
} elsif(!$METHOD) {
	$METHOD = 'simple';
}

my $obj = TasukeVariantExistMap->new('dbh' => "", 'db' => "");
$obj->verbose(1);

if($VTYPES) {
	if($obj->replaceTargetVariantTypesFromStr($VTYPES)) {
		print STDERR "Abnormal Varaint type: -v $VTYPES\n";
		exit(1);
	}
}

if(!$TMPDIR) {
	$TMPDIR = '/tmp';
} else {
	if( !(-d $TMPDIR) ) {
		print STDERR "Tempdir not found: $TMPDIR\n";
		exit(1);
	}
}

my $smplOrder = $obj->getSamples($INFILE);
if($SMPLMAP) {
	if(-f $SMPLMAP) {
		my %smpl2newname;
		local *FH;
		open(FH, "<$SMPLMAP") or die "$SMPLMAP: $!";
		while(<FH>) {
			chomp;
			my $datarow = $_;
			next if($datarow !~ m/\S/);

			my @vals = split /,/, $datarow;
			my $vcfName = trim($vals[0]);
			my $accName = trim($vals[1]);
				$accName = $vcfName if($accName ne '0' && !$accName);
			$smpl2newname{$vcfName} = $accName;
#			$smpl2newname{ trim($vals[0]) } = trim($vals[1]);
		}
		close(FH);

		my $smplOrderLen = @{$smplOrder};
		for(my $i=0; $i<$smplOrderLen; $i++) {
			my $smpleName = $smplOrder->[$i];
			if(!exists($smpl2newname{$smpleName})) {
				$smplOrder->[$i] = "";
			} else {
				$smplOrder->[$i] = $smpl2newname{$smpleName};
			}
			my $destName = $smplOrder->[$i];
			$destName = '(skip)' if($destName ne '0' && !$destName);
			printf STDERR "Convert VCF samplename: %s -> %s\n", $smpleName, $destName;
		}
	} else {
		print STDERR "Samplename map file not found: $SMPLMAP\n";
		exit(1);
	}
}

if($PHYLIPDIR) {
	if(-d $PHYLIPDIR && -d $PHYLIPDIR. "/exe" && -f $PHYLIPDIR. "/exe/neighbor") {
		# Phylip path OK
		if( !(-f $PHYLIPDIR. "/exe/retree") ) {
			printf STDERR "Phylip retree(%s/exe/retree) not found. No Mid-point rooting will be performed.\n", $PHYLIPDIR;
		}
	} else {
		print STDERR "Incorrect Phylip directory. Specify a directory that contains \"exec/neighbor\": $PHYLIPDIR\n";
		exit(1);
	}
}

my $binaryTableFile = $TMPDIR. '/tasuke_bittable'. $$;
my $binaryTableFileCreated = 0;
$SIG{'INT'} = '_SIGINThandler'; # Delete temporary files by terminating with Ctrl+C
$SIG{'KILL'} = '_SIGINThandler';
$SIG{'TERM'} = '_SIGINThandler';

eval {
	my $workDir = File::Temp->newdir(DIR => $TMPDIR, CLEANUP => 1);
	$obj->setTempDir($workDir);

	my $binaryTableFileFh;
	open($binaryTableFileFh, ">$binaryTableFile") or die "Can't open $binaryTableFile: $!";
	$obj->outputFromMvcf($INFILE, $binaryTableFileFh, $smplOrder);
	close($binaryTableFileFh);
	$binaryTableFileCreated = 1;

	###############################################################
	# Convert CSV -> PhylipDistanceMatrix
	###############################################################
	print STDERR "Createing distance matrix\n";
	my $cmd = "$Bin/$BIN_DMATRIX -i $binaryTableFile";
	$cmd .= " -n" if($SKIP_NA_COL);
	$cmd .= sprintf(" -m %s", $METHOD) if($METHOD);
	$cmd .= " -d" if($AS_HAPLOID);
	$cmd .= " > $OUTFILE" if($OUTFILE);
	print STDERR "Command: $cmd\n";
	system($cmd) == 0 or die "Can't execute $cmd: $!";

	if($PHYLIPDIR) {
		# In phylip, sample name can be up to 10 characters long.
		# If the name exceeds this, it must be rewritten.
		my $smplOrderLen = @{$smplOrder};
		my $maxLenSmplName = 0;
		for(my $i=0; $i<$smplOrderLen; $i++) {
			my $lenSmplName = length($smplOrder->[$i]);
			$maxLenSmplName = $lenSmplName if($maxLenSmplName < $lenSmplName);
		}

		my $newickPath = sprintf "%s.nwk", $OUTFILE;
		if($maxLenSmplName > 10) {
			my $stat = $obj->makeNewickByPhylipiViaRename($PHYLIPDIR, $OUTFILE, $newickPath);
		} else {
			my $stat = $obj->makeNewickByPhylip($PHYLIPDIR, $OUTFILE, $newickPath);
		}
	}
	
	endProcessing();
};
if ($@) {
	print STDERR "Exception occur: $@";
	endProcessing();
}

exit(0);

sub endProcessing {
        my($forceDel) = @_;
        if(-f $binaryTableFile) {
                if($NOT_DELETE_BINTABLE && $binaryTableFileCreated) {
                        eval {
                                my $csvFile;
                                if($OUTFILE) {
                                        $csvFile = $OUTFILE. '.btbl';
                                } else {
                                        $csvFile = './'. basename($binaryTableFile). '.btbl';
                                }
                                my $cmd = "mv $binaryTableFile $csvFile";
#                               printf STDERR "$cmd\n";
                                system($cmd) == 0 or die "$!\nCant't move $binaryTableFile. Proceed without file moving. $binaryTableFile remains\n";
                                $binaryTableFile = $csvFile;
                        };
                        if (my $error = $@) {
                                print STDERR "$error\n";
                        }
                } else {
                        printf STDERR "Delete binary table: %s\n", $binaryTableFile;
                        unlink($binaryTableFile);
                }
        }
}
sub trim {
	my($str) = @_;
	$str =~ s/^\s+//;
	$str =~ s/\s+$//;
	return $str;
}
sub _error_method {
	print STDERR "Create a binary distance matrix based on the presence or absence(1/0) of the variants.\n\n";
        print STDERR "Usage: $0 -i [VCFfile] -o [outfile]\n";
        print STDERR "Other options (Not required):\n";
	print STDERR "  -s <file>: Path to \"VCFsampleName > NewName\" correspondence table(*). VCFsampleName[,]NewName[\\n]...\n";
        print STDERR "  -y <dir>: Path to Phylip directory(having \"exe/neighbor\" under it). Create an additional Newick with name \"[outfile].nwk\".\n";
	print STDERR "  -v <str>: Specify target variant type. s=SNP, i=INS, d=DEL, order does not matter. ex:\"-v s\" \"-v di\" (Default: sid)\n";
        print STDERR "  -m <str>:  Distance matrix calculation method. simple(default)/jaccard/dice/soergel\n";
        print STDERR "  -n:  Cross all samples and skip Column where \"GT=./.\"(NA) exists\n";
#	print STDERR "  -d:  Simply calculate as haploid. 0:Ref(0/0)  1:Alt(1/1, 1/2, 0/1)  NA:DEPTH=NULL\n";
        print STDERR "  -l:  Leave binary table with name \"[outfile].btbl\". This file can be reused for distance calculation by $BIN_DMATRIX\n";
        print STDERR "  -t <dir>: Create temporary data under this directory. Large datasets may require tens to hundreds GB of free space.(Default: /tmp)\n";
        print STDERR "\n";
	print STDERR "  (*) Samples not included in this file will be skipped. Even if you do not want to change the name, set the same name before and\n";
	print STDERR "      after the comma. By default it uses all sample-names in the VCF.\n";
}

### SIGINT ###
sub _SIGINThandler{
	endProcessing(1);
	print STDERR "\n\nFinished.\n";
	exit(1);
}
