#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;


## Variables Declaration and Initialization

our (@MATRIX,%SEQS,$infile,$outfile,$IFH,$OFH,$dump);
    #
    # REMEMBER: "our" defines global variables,
    #           "my" only local (although they will work as global
    #           when declared at the beginning of the program)

$dump = 0; # 0 -> do not dump vars | 1 -> dump vars (@MATRIX, %SEQS)

## Reading Command-line Arguments

($infile,$outfile) = @ARGV;
         #
         # Obtaining filenames from the program
         # command-line arguments stored in @ARGV

(defined($infile)  &&  $infile ne '') || ($infile = undef);
         #
         # if $infile not provided just try stdin...

(defined($outfile) && $outfile ne '') || ($outfile = undef);
         #
         # if $outfile not provided just force to undefine it,
         # then ensure it is set to STDOUT later if undef...


## Opening Filehandle

if (defined($infile)) {

	open(IFH, "< $infile") ||
		die("### ERROR ### CANNOT OPEN FILE $infile ...\n");
        #
        # REMEMBER: return a message for the user and exit with error,
        #           it is a shortcut for the following code:
        #
        #  open(IFH, "< $infile") || do {
	    #      print STDERR "### ERROR ### CANNOT OPEN FILE $infile ...\n";
        #      exit(1);
        #  };

	$IFH = \*IFH;

} else {

	$IFH = \*STDIN; # Recovering STDIN as input source for the while behind

};


## Loading Sequences from Filehandle

%SEQS = (); # Initialize the sequence holder

my $seqs = 0; # Initialize the sequence counter

while (<$IFH>) { # Reading lines from $infile,
                 # we are assuming sequences to be in fasta format
                 #
                 # REMEMBER:   <$IFH>  =>  defined($_ = <$IFH>)

	chomp; # REMEMBER: remove newline chars, same as "chomp($_);"

	my $line = $_;

	$line =~ m/^\>/o && do {

		last if $seqs++ > 1;
              #
              # Exit from the while loop
              # if we have read enough sequences

		$SEQS{$seqs} = { ID => "$_", SEQ => '' };
                #
                # Initializing sequence data structure

		next; # Next iteration: Code after this block must
              #                 be executed only on sequence strings

	};

	$SEQS{$seqs}{SEQ} .= $line;
              # Concatenate current string
              # to the SEQ field in $SEQS{$seqs}

};

close($IFH);

$dump && print STDERR Data::Dumper->Dump([ \%SEQS ], [ qw/ *SEQS / ]);


## Build the Dot-Plot Matrix

# Initialization

my ($seqA, $seqB) = ($SEQS{1}{SEQ}, $SEQS{2}{SEQ});
                    # Choosing sequences to work with

$seqA = uc($seqA); # Ensuring all characters
$seqB = uc($seqB); # from the sequences are in upper case

my $len_seqA = length($seqA);
my $len_seqB = length($seqB);

@MATRIX = (); # Initialize the matrix holder

$MATRIX[0][0] = '*'; # The upper and leftmost cell
                     # does not contain data at all,
                     # remember the corresponding slide

# Now load sequences into the "zeroes" cells

for (my $j = 0; $j < $len_seqA; $j++) {
    #
    # Loops through columns and loads sequence A in row 0

	$MATRIX[($j + 1)][0] = substr($seqA, $j, 1);

}; # for $j

for (my $i = 0; $i < $len_seqB; $i++) {
    #
    # Loops through rows and loads sequence B in column 0

	$MATRIX[0][($i + 1)] = substr($seqB, $i, 1);

}; # for $j

# And finally, calculate identities/mismatches for each cell

$len_seqA++; # Increase length to include the sequence
$len_seqB++; # cells in the matrix, see below

for (my $i = 1; $i < $len_seqB; $i++) {
        # loop through rows (skip row 0 -> sequence holder)

	for (my $j = 1; $j < $len_seqA; $j++) {
        # loop through columns (skip col 0 -> sequence holder)

		if ($MATRIX[$j][0] eq $MATRIX[0][$i]) {

			$MATRIX[$j][$i] = 1; # match

		} else {

			$MATRIX[$j][$i] = 0; # mismatch

		};

	}; # for $j

}; # for $i

$dump && print STDERR Data::Dumper->Dump([ \@MATRIX ],[ qw/ *MATRIX / ]);


## Opening Filehandle

if (defined($outfile)) {

	open(OFH, "> $outfile") ||
		die("### ERROR ### CANNOT OPEN FILE $outfile ...\n");
        #
        # REMEMBER: return a message for the user and exit with error,
        #           it is a shortcut for the following code:
        #
        #  open(OFH, "< $outfile") || do {
	    #      print STDERR "### ERROR ### CANNOT OPEN FILE $outfile ...\n";
        #      exit(1);
        #  };

	$OFH = \*OFH;

} else {

	$OFH = \*STDOUT; # Recovering STDOUT as output source

};


## Print the Dot-Plot Matrix

for (my $i = 0; $i < $len_seqB; $i++) {
        # loop through rows (skip row 0 -> sequence holder)

	for (my $j = 0; $j < $len_seqA; $j++) {
        # loop through columns (skip col 0 -> sequence holder)

		unless ($i == 0 || $j == 0) {

			print $OFH ($MATRIX[$j][$i] ? '*' : ' '); # print dot-plot cells

		} else {

			print $OFH $MATRIX[$j][$i]; # print nucleotides

		};

		print $OFH " "; # print horizontal spacer

	}; # for $j

	print $OFH "\n"; # print rows

}; # for $i

close($OFH);

exit(0);
