#!/usr/bin/perl -w
use POSIX;
use Getopt::Long qw(:config bundling require_order auto_version);
use Pod::Usage;
use FAST;
use FAST::Bio::SeqIO;
use FAST::Bio::AlignIO;
use File::Basename;
use strict;

use vars qw($VERSION $DESC $NAME $COMMAND $DATE);
$VERSION = $FAST::VERSION; 
$DESC    = "Convert sequences and alignments either to or from from fasta format.\n";
$NAME    = $0;
$NAME    =~ s/^.*\///;
$COMMAND = join " ",$NAME,@ARGV;
$DATE = POSIX::strftime("%c",localtime());

use constant { true => 1, false => 0 };

## DEFAULT OPTION VALUES
my $def_format  = $FAST::DEF_FORMAT;  #7/1/13 "fasta";
my $def_logname = $FAST::DEF_LOGNAME; #7/1/13 "FAST.log.txt";

## OPTION VARIABLES
my $man                  = undef;  # --man
my $help                 = undef;  # -h
my $log                  = undef;        # -l
my $logname              = $def_logname; # -L
my $comment              = undef;        # -C
my $input                = undef;
my $output               = undef;
my $files                = undef;
my $sequential           = undef;
my $maxlength            = undef;
my $seq_ext = {
	       fasta     => 'fas',
	       embl    =>  'embl',
	       genbank =>  'gbk',
	       swiss   =>  'swiss',
	       pir     =>  'pir',
	       gcg     =>  'gcg',
	       raw     =>  'raw',
         fastq   =>  'fastq'
	      };

my $aln_ext = {
	       bl2seq    => 'bl2seq',
	       clustalw  => 'aln',
	       fasta     => 'fas',
	       maf       => 'maf',
	       mega      => 'mega',
	       meme      => 'meme',
	       msf       => 'msf',
	       nexus     => 'nex',
	       pfam      => 'pfam',
	       phylip    => 'phy',
	       #	       psiblast  => 'psi',
	       selex     => 'slx'  
	      };


my $in_only_aln_ext = {
		       prodom    => 'prodom',
		       mase      => 'mase',
		       stockholm => 'sto',
		       emboss    => 'emboss'
		      };

sub print_help_header(){ 
  print "\n$NAME $VERSION\n\n$DESC\n";
  print "\nAllowed formats: ",(join ", ",(keys %$seq_ext),"\n",(keys %$aln_ext)),"\n";
  print "\nAllowed formats (input only): ",(join ", ",(keys %$in_only_aln_ext)),"\n";
}


GetOptions('help|h'         		 => \$help, 
	   'man'            		 => \$man,

	   'log|l'                       => \$log,
	   'logname|L=s'                 => \$logname,
	   'comment|C=s'                 => \$comment,
	   'input|i=s'                   => sub {  my (undef,$val) = @_; 
						   pod2usage(1) if ($output); 
						   $input = $val; 
						 },
	   'output|o=s'                  => sub {  my (undef,$val) = @_; 
						   pod2usage(1) if ($input); 
						   $output = $val; 
						 },
	   'files|f'                     => \$files,
           'sequential'                => \$sequential,
           'maxlength|M=i'                 => \$maxlength
	  ) 
  or pod2usage(2);

if ($help) {
  print_help_header();
  pod2usage(1); 
}
if ($man) {
  print_help_header();
  pod2usage(-verbose => 2); 
}
pod2usage("$NAME: Requires exactly zero arguments when taking input from STDIN. Try $NAME -h for help.") if (!(-t STDIN) and (@ARGV != 0));
pod2usage("$NAME: Requires at least one argument when using with --files or -f option. Try $NAME -h for help.") if ((-t STDIN) and $files and @ARGV == 0);
pod2usage("$NAME: Either option --input <format>/-i <format> or option --output <format>/-o <format> must be used. Try $NAME -h for help.") unless ($input or $output);

&FAST::log($logname, $DATE, $COMMAND, $comment) if ($log); 


my $mode = ($input ? 'input' : 'output');
my %format = ();
if (defined $input) {$format{'input'} = lc ($input)} else {$format{'input'} = 'fasta'}
if (defined $output) {$format{'output'} = lc ($output)} else {$format{'output'} = 'fasta'}


my %attributes = ();
if ($format{'output'} eq 'phylip') {
  $attributes{'-interleaved'} = ($sequential ? 0 : 1);
  $attributes{'-idlength'}    = ($maxlength || 10);
  $attributes{'-wrap_sequential'} = 1; 
  $attributes{'-line_length'}  = 80;
}


my $extension = undef;
if ($files and $input) {
  $extension = 'fas';
};

my ($module, $read, $write);
if (exists $$seq_ext{$format{$mode}}){
  $module = "FAST::Bio::SeqIO";
  $read =  "next_seq";
  $write = "write_seq";
  if ($files and $mode eq 'output') {
    $extension = $$seq_ext{$format{$mode}};
  } 
}
elsif(exists $$aln_ext{$format{$mode}}) {
  $module = "FAST::Bio::AlignIO";
  $read =  "next_aln";
  $write = "write_aln";
  if ($files and $mode eq 'output') {
    $extension = $$aln_ext{$format{$mode}};
  } 
}
elsif ($mode eq 'input' and exists $$in_only_aln_ext{$format{$mode}}){
  $module = "FAST::Bio::AlignIO";
  $read =  "next_aln";
  $write = "write_aln";
}
else{
  die "Fatal error in $NAME: $format{$mode} not recognized as an $mode format.\n"
}


if ($files) { ## IN AND OUT FROM FILE, ONE FOR EACH
  foreach my $infile (@ARGV) {
    my($filename, $directories, $suffix) = fileparse($infile,qr/\.[^.]*/);
    my $outfile = join '',$directories,$filename,'.',$extension;
    my $IN   = $module->new('-format' => $format{'input'},  -file => $infile);
    my $OUT  = $module->new('-format' => $format{'output'}, %attributes, -file => ">$outfile");
    while (my $obj = $IN->$read()) {
      if ($obj and ref $obj eq 'FAST::Bio::SimpleAlign') {
	$obj->set_displayname_flat();
      }
      $OUT->$write($obj);
    }
  }
}
else { ## STDIN AND STDOUT
  my $IN   = $module->newFh(-fh => *STDIN{IO}, '-format' => $format{'input'});
  my $OUT  = $module->newFh(-fh => *STDOUT{IO}, '-format' => $format{'output'}, %attributes);
  for (<$IN>){
    if ($_ and ref $_ eq 'FAST::Bio::SimpleAlign') {
      $_->set_displayname_flat();
    }
    print $OUT $_;
  }
}


__END__

=head1 NAME

B<fasconvert> - Convert sequences or alignments to or from fasta format.

=head1 SYNOPSIS

B<fasconvert> -o <format> < fasta-file > other-format file

B<fasconvert> -i <format> < other-format-file > fasta-file

B<fasconvert> -fo <format> fasta-file1 fasta-file2 ...

B<fasconvert> -fi <format> other-format-file1 other-format-file2 ...

=head1 DESCRIPTION

B<fasconvert> converts multisequence or alignment data to or from fasta
format. The alternative format must be explicitly specified using an
option argument to either the B<--input>/B<-i> option or the B<--output>/B<-o>
option. The format specifier is a single all-lower-case word
corresponding to formats supported by the Bioperl Seq::IO and
Align::IO modules. By default, input is from STDIN and output is to
STDOUT. With the B<--files>/B<-f> option, input files are converted
one-by-one to corresponding output files using extensions.

Options specific to B<fasconvert>:
  B<-i>, B<--input> <format>       convert from <format> to fasta
  B<-o>, B<--output> <format>      convert from fasta to <format>
  B<-f>, B<--files>                input file (not from stdin) 
  B<-s>, B<--sequential>           for phylip output, don't interleave sequences
  B<-M>, B<--maxlength>=<integer>  for phylip output, truncate identifiers to specified length                                      

Options general to FAST:
  B<-h>, B<--help>                 print a brief help message
  B<--man>                      print full documentation
  B<--version>                  print version
  B<-l>, B<--log>                  create/append to logfile  
  B<-L>, B<--logname>=<string>     use logfile name <string>
  B<-C>, B<--comment>=<string>     save comment <string> to log

=head1 INPUT AND OUTPUT

B<fasconvert> is part of FAST, the FAST Analysis of Sequences Toolbox,
based on Bioperl. Most core FAST utilities expect input and return
output in multifasta format. Input can occur in one or more files or
on STDIN. Output occurs to STDOUT. The FAST utility B<fasconvert> can
reformat other formats to and from multifasta format. The FAST
definition of fasta encompasses both single and multifasta formats and
alignments.

=head1 OPTIONS

=over 8

=item B<-i [format]>
      B<--input [format]>

Convert file from input format to FASTA format

=item B<-o [format]>
      B<--output [format]>

Convert file from FASTA format to output format

=item B<-f>
      B<--files>

Convert file in place. File name appended with format

=item B<-s>
      B<--sequential>

Used when the ouput format is phylip, specifies to not interleave 
sequences

=item B<-M [integer]>
      B<--maxlength=[integer]>

Used when the output format is phylip, this option truncates the
identifiers to a specified maximum length. Default: [none]

=item B<-h>,
      B<--help>

Print a brief help message and exit.

=item B<--man>

Print the manual page and exit.

=item B<--version>

Print version information and exit.

=item B<-l>,
      B<--log>

Creates, or appends to, a generic FAST logfile in the current working
directory. The logfile records date/time of execution, full command
with options and arguments, and an optional comment.

=item B<-L [string]>,
      B<--logname=[string]>

Use [string] as the name of the logfile. Default is "FAST.log.txt".

=item B<-C [string]>,
      B<--comment=[string]>

Include comment [string] in logfile. No comment is saved by default.

=back

=head1 EXAMPLES

Convert ClustalW to FASTA

=over 8

cat data.aln | B<fasconvert> -i clustalw > data.fas

=back


=head1 SEE ALSO

=over 8

=item C<man perlre>

=item C<perldoc perlre>

Documentation on perl regular expressions.

=item C<man FAST>

=item C<perldoc FAST>

Introduction and cookbook for FAST

=item L<The FAST Home Page|http://compbio.ucmerced.edu/ardell/FAST>"

=back 

=head1 CITING

If you use FAST, please cite I<Lawrence et al. (2015). FAST: FAST Analysis of
Sequences Toolbox.> and Bioperl I<Stajich et al.>.

=cut
