###########################################################################
#
# AudioConverter - helper plugin that does audio conversion using sox
#
# A component of the Greenstone digital library software
# from the New Zealand Digital Library Project at the 
# University of Waikato, New Zealand.
#
# Copyright (C) 2008 New Zealand Digital Library Project
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
###########################################################################
package AudioConverter;

use MediaConverter;


use strict;
no strict 'refs'; # allow filehandles to be variables and viceversa

use gsprintf 'gsprintf';

BEGIN {
    @AudioConverter::ISA = ('MediaConverter');
}

my $arguments = [
      { 'name' => "cache_generated_audio",
	'desc' => "{AudioConverter.cache_generated_audio}",
	'type' => "flag",
	'reqd' => "no",
	'hiddengli' => "yes" # option to delete cache from GLI not yet implemented
	}
		 ];

my $options = { 'name' => "AudioConverter",
		'desc' => "{AudioConverter.desc}",
		'abstract' => "yes",
		'inherits' => "yes",
		'args' => $arguments };

sub new {
    my ($class) = shift (@_);
    my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
    push(@$pluginlist, $class);

    push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
    push(@{$hashArgOptLists->{"OptList"}},$options);

    my $self = new MediaConverter($pluginlist, $inputargs, $hashArgOptLists, 1);


    return bless $self, $class;

}

# needs to be called after BasePlugin init, so that outhandle is set up.
sub init {
    my $self = shift(@_);

    $self->{'tmp_file_paths'} = ();

    # Check that sox is installed and available on the path 
    my $audio_conversion_available = 1;
    my $no_audio_conversion_reason = "";

    # Conversion does not work very well on Windows 95/98...
    # (because of distinction needed between stdout and stderr???)

    if ($ENV{'GSDLOS'} eq "windows" && !Win32::IsWinNT()) {
	$audio_conversion_available = 0;
	$no_audio_conversion_reason = "win95notsupported";
    } else {
	my $result = `sox -h 2>&1`;
	if ($? == -1 || $? == 256) {  # Linux and Windows return different values for "program not found"
	    $audio_conversion_available = 0;
	    $no_audio_conversion_reason = "soxnotinstalled";
	}
    }
    $self->{'audio_conversion_available'} = $audio_conversion_available;
    $self->{'no_audio_conversion_reason'} = $no_audio_conversion_reason;

    if ($self->{'audio_conversion_available'} == 0) {
	my $outhandle = $self->{'outhandle'};
	&gsprintf($outhandle, "AudioConverter: {AudioConverter.noconversionavailable} ({AudioConverter.".$self->{'no_audio_conversion_reason'}."})\n");
    }
}
	

# convert audio to new type if converttotype is set
# discover audio metadata
sub generate_audio {
    my $self = shift(@_);

    my ($filename_full_path, $filename_no_path, $doc_obj, $section) = @_;

    # check sox status
    return 0 if $self->{'audio_conversion_available'} == 0;
    # check the filenames
    return 0 if ($filename_no_path eq "" || !-f $filename_full_path);

    if ($self->{'cache_generated_audio'}) {
	$self->init_cache_for_file($filename_full_path);
    }

    my $verbosity = $self->{'verbosity'};
    my $outhandle = $self->{'outhandle'};

    
    my $filehead = $filename_no_path;
    $filehead =~ s/\.([^\.]*)$//; # filename with no extension
    my $assocfilemeta = "[assocfilepath]";
    if ($section ne $doc_obj->get_top_section()) {
	$assocfilemeta = "[parent(Top):assocfilepath]";
    }

    # Convert the audio to a new type (if required).
    my $converttotype = $self->{'converttotype'};
    my $type = "unknown";

    if ($converttotype ne "" && $filename_full_path !~ m/$converttotype$/) {
	
	my ($result,$filename_full_path) 
	    = $self->convert($filename_full_path, $converttotype, "", "CONVERTTYPE");

	$type = $converttotype;
	$filename_no_path = "$filehead.$type";
    }

    # add Audio metadata 
    $doc_obj->add_metadata($section, "Audio", $filename_no_path);

    # Source and SourceUTF8 - should this be converted filename or original?
    # here we overwrite the originals with converted ones
    $self->set_Source_metadata($doc_obj, $filename_no_path);

    # use identify to get info about the (possibly converted) audio

    my ($audio_type, $audio_srate, $audio_quant, $audio_chans, $audio_dur, $audio_samples) 
	= &identify($filename_full_path, $outhandle, $verbosity);

    if ($audio_type ne " ") {
	$type = $audio_type;
    }

    my $file_size = -s $filename_full_path;
    #overwrite the ones added in BasePlugin
    $doc_obj->set_metadata_element ($section, "FileFormat", $type);
    $doc_obj->set_metadata_element ($section, "FileSize",   $file_size);

    $doc_obj->add_metadata ($section, "AudioType",         $audio_type);
    $doc_obj->add_metadata ($section, "AudioSampleRate",   $audio_srate);
    $doc_obj->add_metadata ($section, "AudioQuantization", $audio_quant);
    $doc_obj->add_metadata ($section, "AudioNumChannels",  $audio_chans);
    $doc_obj->add_metadata ($section, "AudioDuration",     $audio_dur);
    $doc_obj->add_metadata ($section, "AudioNumSamples",   $audio_samples);

    $doc_obj->add_metadata ($section, "srclink", "<a href=\"_httpprefix_/collect/[collection]/index/assoc/$assocfilemeta/[Audio]\">");
    $doc_obj->add_metadata ($section, "/srclink", "</a>");
    $doc_obj->add_metadata ($section, "srcicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/$assocfilemeta/_icon${type}_\" width=\"100\">");

    # Add the audio as an associated file
    $doc_obj->associate_file($filename_full_path, $filename_no_path, "audio/$type", $section);

}



sub convert {
    my $self = shift(@_);
    my $source_file_path = shift(@_);
    my $target_file_type = shift(@_);
    my $convert_options  = shift(@_) || "";
    my $convert_id       = shift(@_) || "";

    my $outhandle = $self->{'outhandle'};
    my $verbosity = $self->{'verbosity'};

    my $source_file_no_path = &File::Basename::basename($source_file_path);

    # Determine the full name and path of the output file
    my $target_file_path;
    if ($self->{'cache_generated_audio'}) {
	my $cached_audio_dir = $self->{'cached_dir'};
	my $audio_root = $self->{'cached_file_root'};
	$audio_root .= "_$convert_id" if ($convert_id ne "");
	my $audio_file = "$audio_root.$target_file_type";
	$target_file_path = &util::filename_cat($cached_audio_dir,$audio_file);
    }
    else {
	$target_file_path = &util::get_tmp_filename($target_file_type);
	push(@{$self->{'tmp_file_paths'}}, $target_file_path);
    }

    # Generate and run the convert command
    my $vl = int($verbosity/2);

    my $convert_command = "sox -V$vl --show-progress $convert_options \"$source_file_path\" \"$target_file_path\"";

    my $print_info = { 'message_prefix' => $convert_id,
		       'message' => "Converting audio $source_file_no_path to: $convert_id $target_file_type" };
 
    my ($regenerated,$result,$had_error) 
	= $self->autorun_general_cmd($convert_command,$target_file_path,$print_info);

    return ($result,$target_file_path);
}


# Discover the characteristics of an audio file with sox
# Name 'indentify' relates to original operation written for images using
# ImageMagick's 'indentify' 
sub identify { 
    my ($audio, $outhandle, $verbosity) = @_;

    # Use the 'sox -V3' command to get the file specs
    my $command = "sox -V3 \"$audio\" 2>&1";
    print $outhandle "$command\n" if ($verbosity > 2);
    my $result = '';
    $result = `$command`;
    print $outhandle "$result\n" if ($verbosity > 3);

    # Extract useful metadata about audio files
    ## my $type  = 'unknown';  # ... set this to something related to mime type?
    ## allow plugin to specify mime-type info?

    my $encod = 'unknown';
    my $srate = 'unknown';
    my $qsize = 'unknown';
    my $chans = 'unknown';
    my $durat = 'unknown';
    my $sampl = 'unknown';

    ($encod) = ($result =~ m/Sample Encoding\s*:\s+(.*)$/m);
    ($srate) = ($result =~ m/Sample Rate\s*:\s+(.*)$/m);
    ($qsize) = ($result =~ m/Sample Size\s*:\s+(\d+)$/m);
    ($chans) = ($result =~ m/Channels\s*:\s+(.*)$/m);

    my ($durat_full) = ($result =~ m/Duration\s*:\s+(.*)$/m);
    ($durat,$sampl) = ($durat_full =~ m/^(.*?)\s+=\s+(\d+)/m);

    print $outhandle "file: $audio:\t $encod, $srate, $qsize, $chans, $durat\n" 
	if ($verbosity > 2);

    # Return the specs
    return ($encod, $srate, $qsize, $chans, $durat, $sampl);
}

### Can this be moved into MediaConverter ???

sub clean_up_temporary_files {
    my $self = shift(@_);

    foreach my $tmp_file_path (@{$self->{'tmp_file_paths'}}) {
	if (-e $tmp_file_path) {
	    &util::rm($tmp_file_path);
	}
    }
   
}

1;	
