package Bio::Tools::Run::Chinook::ChinookManager;

use vars qw(@ISA @CHINOOKMANAGER_PARAMS %OK_FIELD $AUTOLOAD);

use strict;
use Bio::Root::Root;
use Bio::Tools::Run::Chinook::Database;
use Bio::Tools::Run::Chinook::DEO;
use Bio::Tools::Run::Chinook::QueueBatch;
use Bio::Tools::Run::Chinook::Service;
use Bio::Tools::Run::Chinook::SupportData::SupportData;
use IO::Socket;
use Data::Dumper;

@ISA = qw( Bio::Root::Root );

BEGIN {
  @CHINOOKMANAGER_PARAMS = qw(machine_name port);
  foreach my $attr (@CHINOOKMANAGER_PARAMS)
                { $OK_FIELD{$attr}++; }
}

sub new {
        my($class, @args) = @_;
        my $self = $class->SUPER::new(@args);
        while (@args) {
        	my $attr = shift @args;
		my $value = shift @args;	
		$self->$attr($value);
	}
        
	$self->{'SENDER_DONE_STRING'} = '*****';

        return $self;
}

sub AUTOLOAD {
    my $self = shift;
    my $attr = $AUTOLOAD;
    $attr =~ s/.*:://;

    $self->throw("Unallowed parameter: $attr !") unless $OK_FIELD{$attr};
    $self->{$attr} = shift if @_;
    return $self->{$attr};
}

sub _getSocket {
	my $self = shift;
	my $host = $self->{'machine_name'};
	my $port = $self->{'port'};
	return IO::Socket::INET->new("$host:$port") or $self->throw($@);
}

### SOCKET COM PUBLIC FUNCTIONALITY

##BatchProtocol.SEND**** Methods

sub getServices {
	my $self = shift;
	my $socket = $self->_getSocket;
	$self->_notifyReceive($socket);
	print $socket "0\n";
	my @services;
	while (my $string = <$socket>) {
		chomp $string;
		if ($string eq $self->{'SENDER_DONE_STRING'}) {
			last;
		}
		else {
			if ($string =~ /SERVICES\|([\s\S]+)\|([\s\S]+)\|([\s\S]+)\|([\s\S]+)\|([\s\S]+)\|([\s\S]*)\|([\s\S]*)/g) {
				my $service = Bio::Tools::Run::Chinook::Service->new(	name => $2, 
											type => $1,
											location => $3,
											mode => $4,
											version => $5,
											batch_filename => $6,
											filewire_uri => $7);
				push @services, $service;
			} else {
				$self->_stopSocket($socket);
				$self->throw("Undefined service information: " . $string);
			}
		}
	}
	$self->_stopSocket($socket);
	return \@services;
}

sub getOptimizedService {
	my $self = shift;
	my $service_name = shift;
	my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "16\n";
	
	my $service_name_string = $service_name . "\n";
        print $socket $service_name_string;

        my $service;
	while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
		else {
                        if ($string =~ /SERVICES\|([\s\S]+)\|([\s\S]+)\|([\s\S]+)\|([\s\S]+)\|([\s\S]+)\|([\s\S]*)\|([\s\S]*)/g) {
                                $service = Bio::Tools::Run::Chinook::Service->new(   name => $2,
                                                                                        type => $1,
                                                                                        location => $3,
                                                                                        mode => $4,
                                                                                        version => $5,
                                                                                        batch_filename => $6,
                                                                                        filewire_uri => $7);
                        } else {
                                $self->_stopSocket($socket);
                                $self->throw("Cannot find optimized service information: " . $string);
                        }
                }
	}
	$self->_stopSocket($socket);
        return $service;		
}

sub getRandomMatchingService {
	my $self = shift;
	my $service_name_to_match = shift;
	my $services = $self->getServices();
	my @matching_services;
	foreach my $service (@$services) {
		if ($service->getName eq $service_name_to_match) {
			push @matching_services, $service;
		}
	}

	if (scalar(@matching_services) == 0) {
		$self->throw("Could not find matching service for: " . $service_name_to_match);
	}

	$self->_fisher_yates_shuffle(\@matching_services);
	return pop(@matching_services);
}

sub _fisher_yates_shuffle {
	my $self = shift;
	my $deck = shift;
	my $i = @$deck;
	while ($i--) {
		my $j = int rand ($i+1);
		@$deck[$i, $j] = @$deck[$j, $i];
	}
}

sub getBatchDirectoryPath {
	my $self = shift;
	my $socket = $self->_getSocket;
	$self->_notifyReceive($socket);
        print $socket "1\n";
	my $batch_directory_path;
	while (my $string = <$socket>) {
		chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
		else {
			if ($string =~ /BATCHDIR\|([\s\S]*)/g) {
				$batch_directory_path = $1;
			} else {
				$self->_stopSocket($socket);
                                $self->throw("Undefined batch directory path: " . $string);
			}
		}
	}
	$self->_stopSocket($socket);
        return $batch_directory_path;
}

sub getBatchQueueDirectoryPath {
	my $self = shift;
	my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "2\n";
	my $batch_queue_directory_path;
        while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /BATCHQUEUEDIR\|([\s\S]*)/g) {
                                $batch_queue_directory_path = $1;
                        } else {
                                $self->_stopSocket($socket);
                                $self->throw("Undefined batch queue directory path: " . $string);
                        }
                }
        }
        $self->_stopSocket($socket);
        return $batch_queue_directory_path;
}

sub getBatchReportingDirectoryPath {
	my $self = shift;
	my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "3\n";
	my $batch_reporting_directory_path;
        while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /BATCHREPORTINGDIR\|([\s\S]*)/g) {
                                $batch_reporting_directory_path = $1;
                        } else {
                                $self->_stopSocket($socket);
                                $self->throw("Undefined batch reporting directory path: " . $string);
                        }
                }
        }
        $self->_stopSocket($socket);
        return $batch_reporting_directory_path;
}

sub getDEO {
        my $self = shift;
        my $data_entry_type_name = shift;

        if (!defined $data_entry_type_name) {
                $self->throw("No data_entry_type_name defined");
        }

        my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "4\n";

        my $data_entry_type_name_string = $data_entry_type_name . "\n";
        print $socket $data_entry_type_name_string;

        my $deo = Bio::Tools::Run::Chinook::DEO->new();
        my %attributes = ();
        while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /DEOINFO: key:([\s\S]+) value:([\s\S]+) class_name:([\s\S]+)/g) {
                                my $key = $1;
                                my $value = $2;
                                my $class_name = $3;
                                chomp($key);
                                chomp($value);
                                chomp($class_name);
                                %attributes->{$key}->{0} = $value;
                                %attributes->{$key}->{1} = $class_name;
                        } elsif ($string =~ /DEOINFO: ([\s\S]+):([\s\S]*)/g) {
                                my $method = $1;
                                my $value = $2;
                                chomp($method);
                                chomp($value);
                                if ($method eq "data_entry_type_name") {
                                        $deo->setDataEntryTypeName($value);
                                } elsif ($method eq "name") {
                                        $deo->setName($value);
                                } elsif ($method eq "order") {
                                        $deo->setOrder($value);
                                } else {
                                        $self->_stopSocket($socket);
                                        $self->throw("Undefined DEO method (perl code out-of-date): " . $string);
                                }
                        } else {
                               $self->_stopSocket($socket);
                               $self->throw("Undefined DEO information from Chinook: " . $string);
                        }
                }
        }
        $deo->setAttributes(\%attributes);
        $self->_stopSocket($socket);
        return $deo;
}

sub getAvailableDatabaseTypes {
	my $self = shift;
	my $service = shift;
	my $data_entry_type_name = shift;

	if (!defined($service)) {
		$self->throw("Service cannot be null");
	}
	if ($service->isa('Bio::Tools::Run::Chinook::Service')) {
		if (!defined($service->getLocation())) {
			$self->throw("Service location not defined for service: " . $service->getName());
		}
	} else {
		$self->throw("Service must be an instance of Bio::Tools::Run::Chinook::Service");
	}
	if (!defined($data_entry_type_name)) {
		$self->throw("Data Entry Type Name cannot be null");
	}

	my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "5\n";

	my $service_location_string = $service->getLocation() . "\n";
	print $socket $service_location_string;

	my $data_entry_type_name_string = $data_entry_type_name . "\n";
	print $socket $data_entry_type_name_string;


	my @database_types;

	while (my $string = <$socket>) {
                chomp $string;
		if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /DATABASETYPE:([\s\S]*)/g) {
				push @database_types, $1;
			}
			elsif ($string =~ /NODATA:([\s\S]+)/g) {
				$self->_stopSocket($socket);
				$self->warn("No database_types found for $data_entry_type_name, returning 0");
				return 0;
			}
			else {
			      	$self->_stopSocket($socket);
                                $self->throw("Undefined database type: " . $string);
			}
		}
	}
        $self->_stopSocket($socket);
        return \@database_types;
}

sub getAvailableRootDataObjectNames() {
        my $self = shift;
        my $service = shift;
        my $data_entry_type_name = shift;
	my $database_type = shift;

        if (!defined($service)) {
                $self->throw("Service cannot be null");
        }
        if ($service->isa('Bio::Tools::Run::Chinook::Service')) {
                if (!defined($service->getLocation())) {
                        $self->throw("Service location not defined for service: " . $service->getName());
                }
        } else {
                $self->throw("Service must be an instance of Bio::Tools::Run::Chinook::Service");
        }
        if (!defined($data_entry_type_name)) {
                $self->throw("Data Entry Type Name cannot be null");
        }
	if (!defined($database_type)) {
		$self->throw("Database Type cannot be null");
	}

        my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "6\n";

        my $service_location_string = $service->getLocation() . "\n";
        print $socket $service_location_string;

        my $data_entry_type_name_string = $data_entry_type_name . "\n";
        print $socket $data_entry_type_name_string;

	my $database_type_string = $database_type . "\n";
	print $socket $database_type_string;

        my @root_data_object_names;

        while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /ROOTDON:([\s\S]*)/g) {
                                push @root_data_object_names, $1;
                        }
                        elsif ($string =~ /NODATA:([\s\S]+)/g) {
                                $self->_stopSocket($socket);
                                $self->warn("No database_types found, returning 0");
                                return 0;
                        }
                        else {
                                $self->_stopSocket($socket);
                                $self->throw("Undefined root data object name: " . $string);
                        }
                }
        }
        $self->_stopSocket($socket);
        return \@root_data_object_names;
}

sub getAvailableChildDataObjectNames() {
        my $self = shift;
        my $service = shift;
        my $data_entry_type_name = shift;
        my $database_type = shift;
	my $parent_data_object_name = shift;

        if (!defined($service)) {
                $self->throw("Service cannot be null");
        }
        if ($service->isa('Bio::Tools::Run::Chinook::Service')) {
                if (!defined($service->getLocation())) {
                        $self->throw("Service location not defined for service: " . $service->getName());
                }
        } else {
                $self->throw("Service must be an instance of Bio::Tools::Run::Chinook::Service");
        }
        if (!defined($data_entry_type_name)) {
                $self->throw("Data Entry Type Name cannot be null");
        }
        if (!defined($database_type)) {
                $self->throw("Database Type cannot be null");
        }
	if (!defined($parent_data_object_name)) {
		$self->throw("Parent data object name cannot be null");
	}

        my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "7\n";

        my $service_location_string = $service->getLocation() . "\n";
        print $socket $service_location_string;

        my $data_entry_type_name_string = $data_entry_type_name . "\n";
        print $socket $data_entry_type_name_string;

        my $database_type_string = $database_type . "\n";
        print $socket $database_type_string;

	my $parent_data_object_name_string = $parent_data_object_name . "\n";
	print $socket $parent_data_object_name_string;

        my @child_data_object_names;

        while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /CHILDDON:([\s\S]*)/g) {
                                push @child_data_object_names, $1;
                        }
                        elsif ($string =~ /NODATA:([\s\S]+)/g) {
                                $self->_stopSocket($socket);
                                return 0;
                        }
                        else {
                                $self->_stopSocket($socket);
                                $self->throw("Undefined child data object name: " . $string);
                        }
                }
        }
        $self->_stopSocket($socket);
        return \@child_data_object_names;
}

sub getArgumentPathForSupportData() {
        my $self = shift;
        my $service = shift;
        my $data_entry_type_name = shift;
        my $database_type = shift;
        my $parent_data_object_name = shift;

        if (!defined($service)) {
                $self->throw("Service cannot be null");
        }
        if ($service->isa('Bio::Tools::Run::Chinook::Service')) {
                if (!defined($service->getLocation())) {
                        $self->throw("Service location not defined for service: " . $service->getName());
                }
        } else {
                $self->throw("Service must be an instance of Bio::Tools::Run::Chinook::Service");
        }
        if (!defined($data_entry_type_name)) {
                $self->throw("Data Entry Type Name cannot be null");
        }
        if (!defined($database_type)) {
                $self->throw("Database Type cannot be null");
        }
        if (!defined($parent_data_object_name)) {
                $self->throw("Parent data object name cannot be null");
        }

        my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "8\n";

        my $service_location_string = $service->getLocation() . "\n";
        print $socket $service_location_string;

        my $data_entry_type_name_string = $data_entry_type_name . "\n";
        print $socket $data_entry_type_name_string;

        my $database_type_string = $database_type . "\n";
        print $socket $database_type_string;

        my $parent_data_object_name_string = $parent_data_object_name . "\n";
        print $socket $parent_data_object_name_string;

        my @argument_path;

        while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /ARGPATH:([\s\S]*)/g) {
                                push @argument_path, $1;
                        }
                        elsif ($string =~ /NODATA:([\s\S]+)/g) {
                                $self->_stopSocket($socket);
                                return 0;
                        }
                        else {
                                $self->_stopSocket($socket);
                                $self->throw("Undefined argument path: " . $string);
                        }
                }
        }
        $self->_stopSocket($socket);
        return \@argument_path;
}


sub getAvailableSupportData() {
        my $self = shift;
        my $service = shift;
        my $data_entry_type_name = shift;
        my $database_type = shift;
        my $data_object_name = shift;
	my $arguments = shift;

        if (!defined($service)) {
                $self->throw("Service cannot be null");
        }
        if ($service->isa('Bio::Tools::Run::Chinook::Service')) {
                if (!defined($service->getLocation())) {
                        $self->throw("Service location not defined for service: " . $service->getName());
                }
        } else {
                $self->throw("Service must be an instance of Bio::Tools::Run::Chinook::Service");
        }
        if (!defined($data_entry_type_name)) {
                $self->throw("Data Entry Type Name cannot be null");
        }
        if (!defined($database_type)) {
                $self->throw("Database Type cannot be null");
        }
        if (!defined($data_object_name)) {
                $self->throw("Parent data object name cannot be null");
        }

        my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "9\n";

        my $service_location_string = $service->getLocation() . "\n";
        print $socket $service_location_string;

        my $data_entry_type_name_string = $data_entry_type_name . "\n";
        print $socket $data_entry_type_name_string;

        my $database_type_string = $database_type . "\n";
        print $socket $database_type_string;

        my $data_object_name_string = $data_object_name . "\n";
        print $socket $data_object_name_string;

	my $argument_string;
	if (defined($arguments)) {
		for(my $i = 0; $i < scalar(@$arguments); $i++) {
			if ($i == (scalar(@$arguments) - 1)) {
				$argument_string .= $arguments->[$i];
			} else {
				$argument_string .= $arguments->[$i] . "|"
			}
		}
	}
	$argument_string .= "\n";
	print $socket $argument_string;

	print STDERR $service_location_string . $data_entry_type_name_string . $database_type_string . $data_object_name_string . $argument_string . "*****************\n";

	my @support_data;

        while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /SUPDATA:([\s\S]*)/g) {
                                push @support_data, $1;
                        }
                        elsif ($string =~ /NODATA:([\s\S]+)/g) {
                                $self->_stopSocket($socket);
                                return 0;
                        }
                        else {
                                $self->_stopSocket($socket);
                                $self->throw("Undefined support data: " . $string);
                        }
                }
        }
        $self->_stopSocket($socket);
        return \@support_data;
}

sub getSupportData() {
	my $self = shift;
	my $service = shift;
	my $data_entry_type_name = shift;

	my $support_data = new Bio::Tools::Run::Chinook::SupportData::SupportData( 	'chinook_manager' => $self,
											'service' => $service,
											'data_entry_type_name' => $data_entry_type_name );
	return $support_data;
}

##I'm not a big fan of how sequences files are so special here, this may one day be generalized for how
##data gets transformed from one representation to a different representation.  I.e.
##the service says I need DNA_SEQUENCE and so then what DNA_FILE format file types can be converted into a DNA_SEQUENCE, etc. etc.
sub getAvailableSequenceFileTypes {
        my $self = shift;
        my $service = shift;

        if (!defined($service)) {
                $self->throw("Service cannot be null");
        }
        if ($service->isa('Bio::Tools::Run::Chinook::Service')) {
                if (!defined($service->getLocation())) {
                        $self->throw("Service location not defined for service: " . $service->getName());
                }
        } else {
                $self->throw("Service must be an instance of Bio::Tools::Run::Chinook::Service");
        }

        my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "10\n";

        my $service_location_string = $service->getLocation() . "\n";
        print $socket $service_location_string;

        my @supported_sequence_file_types;

        while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /FILETYPE:([\s\S]*)/g) {
                                push @supported_sequence_file_types, $1;
                        }
                        elsif ($string =~ /NODATA:([\s\S]+)/g) {
                                $self->_stopSocket($socket);
                                $self->warn("No supported_sequence_file_types found, returning 0");
                                return 0;
                        }
                        else {
                                $self->_stopSocket($socket);
                                $self->throw("Undefined supported_sequence_file_types: " . $string);
                        }
                }
        }
        $self->_stopSocket($socket);
        return \@supported_sequence_file_types;
}

##true, false - can the service be connected to
sub isServiceConnectable {
	my $self = shift;
        my $service = shift;

        if (!defined($service)) {
                $self->throw("Service cannot be null");
        }
        if ($service->isa('Bio::Tools::Run::Chinook::Service')) {
                if (!defined($service->getLocation())) {
                        $self->throw("Service location not defined for service: " . $service->getName());
                }
        } else {
                $self->throw("Service must be an instance of Bio::Tools::Run::Chinook::Service");
        }

        my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "11\n";

        my $service_location_string = $service->getLocation() . "\n";
        print $socket $service_location_string;

        my $service_status = 0;

        while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /SERVICESTATUS:CONNECTED/g) {
                                $service_status = 1;
                        }
                }
        }
        $self->_stopSocket($socket);
        return $service_status;
}

sub getServiceDetails {
        my $self = shift;
        my $service = shift;

        if (!defined($service)) {
                $self->throw("Service cannot be null");
        }
        if ($service->isa('Bio::Tools::Run::Chinook::Service')) {
                if (!defined($service->getLocation())) {
                        $self->throw("Service location not defined for service: " . $service->getName());
                }
        } else {
                $self->throw("Service must be an instance of Bio::Tools::Run::Chinook::Service");
        }

        my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "12\n";

        my $service_location_string = $service->getLocation() . "\n";
        print $socket $service_location_string;

        my $service_details;

        while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /SERVERINFO:([\s\S]+):([\s\S]*)/g) {
                                $service_details->{$1} = $2;
                        }
                        elsif ($string =~ /ERROR:([\s\S]*)/g) {
                        	$service_details->{'ERROR'} = $1;
			}
			else {
				$self->_stopSocket($socket);
                                $self->throw("Undefined service details response: " . $string);
			}
                }
        }
        $self->_stopSocket($socket);
        return $service_details;
}

sub getJobsProcessingOnServerCount {
        my $self = shift;
        my $service = shift;

        if (!defined($service)) {
                $self->throw("Service cannot be null");
        }
        if ($service->isa('Bio::Tools::Run::Chinook::Service')) {
                if (!defined($service->getLocation())) {
                        $self->throw("Service location not defined for service: " . $service->getName());
                }
        } else {
                $self->throw("Service must be an instance of Bio::Tools::Run::Chinook::Service");
        }

        my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "13\n";

        my $service_location_string = $service->getLocation() . "\n";
        print $socket $service_location_string;

        my $job_count = -1;

        while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /JOBSCOUNT:([\s\S]*)/g) {
                                $job_count = $1;
                        }
                        elsif ($string =~ /ERROR:([\s\S]*)/g) {
                                $self->warn("ERROR: " . $1);
                        }
                        else {
                                $self->_stopSocket($socket);
                                $self->throw("Undefined service details response: " . $string);
                        }
                }
        }
        $self->_stopSocket($socket);
        return $job_count;
}


sub processQueueBatch {
        my $self = shift;
        my $queue_batch_filename = shift;

        if (!defined($queue_batch_filename)) {
                $self->throw("Queue Batch Filename cannot be null");
        }

	my $transferred_successfully = 0;

	my $transfer_filename = $self->_transferFileToRemotePerlClient($queue_batch_filename);
        if (defined ($transfer_filename)) {
		my $socket = $self->_getSocket;
        	$self->_notifyReceive($socket);
        	print $socket "14\n";

		my $queue_batch_filename_string = $transfer_filename . "\n";
        	print $socket $queue_batch_filename_string;

		while (my $string = <$socket>) {
                	chomp $string;
                	if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        	last;
                	}
                	else {
                        	if ($string =~ /BATCHRUN:SUCCESS:([\s\S]*)/g) {
                                	$transferred_successfully = 1;
                        	}
                        	elsif ($string =~ /BATCHRUN:FAILED:([\s\S]*)/g) {
                                	$self->warn("ERROR: " . $1);
                        	}
                        	else {
                                	$self->_stopSocket($socket);
                                	$self->throw("Undefined service details response: " . $string);
				}
                	}	
        	}
        	$self->_stopSocket($socket);
	}
	return $transferred_successfully;
}

sub isReportReady {
	my $self = shift;
        my $queue_batch_id = shift;
	my $download_report_to = shift;

        if (!defined($queue_batch_id)) {
                $self->throw("Queue Batch ID cannot be null");
        }

	sleep (2); ##Wait a bit before checking

	my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "15\n";

        my $queue_batch_id_string = $queue_batch_id . "\n";
        print $socket $queue_batch_id_string;

        my $report_file;

        while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /REPORTSTAT:RUNNING/g) {
                                ##Still running, do nothing
                        }
                        elsif ($string =~ /REPORTSTAT:COMPLETED:([\s\S]*)/g) {
				print STDERR "Report completed\n";
				$report_file = $1;
                        }
			elsif ($string =~ /REPORTSTAT:NOTPROCESSING/g) {
				$self->warn("Job has not yet been submitted");
                        }
                        else {
                                $self->_stopSocket($socket);
                                $self->throw("Undefined service details response: " . $string);
                        }
                }
        }
        $self->_stopSocket($socket);

	if (defined($report_file)) {
		print STDERR "Getting report file from perl client\n";
		if ($self->_transferFileFromRemotePerlClient($report_file, $download_report_to)) {
			return $download_report_to;
		}
	}

        return $report_file;
}



############################### TRANSFER ROUTINES, PRIVATE (USED TO TRANFER FILES TO REMOTE CHINOOK MANAGERS)

sub _transferFileToRemotePerlClient {
	my $self = shift;
	my $absolute_filename = shift;

	if (!defined($absolute_filename)) {
		$self->throw("ChinookManager cannot find file: " . $absolute_filename );
	}

	my $failed = 0;
	if (!open( FILE_TO_TRANSFER, $absolute_filename)) {
		$failed = 1;
	}
	if ($failed) {
		print STDERR "Could not open file $absolute_filename\n";
		return 0;
	}

	my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "40\n";
	
	foreach my $line (<FILE_TO_TRANSFER>) {
	#	my $transfer_line = $line . "\n";
		print $socket $line;
	}

	my $done_string = $self->{'SENDER_DONE_STRING'} . "\n";
       	print $socket $done_string;

	my $transfer_filename;
	while (my $string = <$socket>) {
		chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /FILETRANS:([\s\S]*)/g) {
                                $transfer_filename = $1;
                        }
                        elsif ($string =~ /ERROR:([\s\S]*)/g) {
                                $self->warn("ERROR: " . $1);
                        }
                        else {
                                $self->_stopSocket($socket);
                                $self->throw("Undefined file transfer response: " . $string);
                        }
                }
	}
	close(FILE_TO_TRANSFER);
        $self->_stopSocket($socket);
	return $transfer_filename;
}

sub _removeTransferFileFromRemotePerlClient {
	my $self = shift;
        my $transfer_filename = shift;

        if (!defined($transfer_filename)) {
                $self->throw("File to remove from perl client cannot be null");
        }

        my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "41\n";

        my $transfer_filename_string = $transfer_filename . "\n";
        print $socket $transfer_filename_string;

        my $transfer_remove_success = 0;
        while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /FILEREM:SUCCESS/g) {
                                $transfer_remove_success = 1;
                        }
                        elsif ($string =~ /ERROR:([\s\S]*)/g) {
                                $self->warn("ERROR: " . $1);
                        }
                        else {
                                $self->_stopSocket($socket);
                                $self->throw("Undefined file transfer response: " . $string);
                        }
                }
        }
        $self->_stopSocket($socket);
        return $transfer_remove_success;
}

sub _transferFileFromRemotePerlClient {
	my $self = shift;
        my $transfer_filename = shift;
	my $file_to_transfer_to = shift;

	if (!defined($transfer_filename)) {
                $self->throw("File to obtain from perl client cannot be null");
        }
	if (!defined($file_to_transfer_to)) {
                $self->throw("File to load transferred file into cannot be null");
        }

	my $failed = 0;
	if (!open( FILE_TO_TRANSFER, ">$file_to_transfer_to")) {
 		$failed = 1;
	}
	if ($failed) {
		$self->throw("Could not open file $file_to_transfer_to");
	}
	else {
		my $socket = $self->_getSocket;
        	$self->_notifyReceive($socket);
        	print $socket "42\n";

        	my $transfer_filename_string = $transfer_filename . "\n";
        	print $socket $transfer_filename_string;
	
		while (my $string = <$socket>) {
			chomp $string;
                	if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        	last;
                	}
                	else {
                        	if ($string =~ /FILEDAT:([\s\S]*)/g) {
                                	print FILE_TO_TRANSFER  $1 . "\n";
                        	}
                        	elsif ($string =~ /ERROR:([\s\S]*)/g) {
                                	$self->throw("ERROR: " . $1);
                        	}
                        	else {
                                	$self->_stopSocket($socket);
                                	$self->throw("Undefined file transfer response: " . $string);
                        	}
                	}
		}

		close (FILE_TO_TRANSFER);
		$self->_stopSocket($socket);
		return 0;

	}
}

sub _getNewTempTransferFileOnRemotePerlClient {
        my $self = shift;

        my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "43\n";


        my $transfer_file = 0;
        while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
                        if ($string =~ /FILETRANSTEMP:([\s\S]*)/g) {
                                $transfer_file = $1;
                        }
                        elsif ($string =~ /FILETEMPFAILED:([\s\S]*)/g) {
                                $self->warn("ERROR: " . $1);
                        }
                        else {
                                $self->_stopSocket($socket);
                                $self->throw("Undefined create new temp transfer file response: " . $string);
                        }
                }
        }
        $self->_stopSocket($socket);
        return $transfer_file;
}


######################### FILE UPLOAD/DOWNLOAD METHODS

sub uploadFile {
        my $self = shift;
        my $service = shift;
	my $file_to_upload = shift;

        if (!defined($service)) {
                $self->throw("Service cannot be null");
        }
        if ($service->isa('Bio::Tools::Run::Chinook::Service')) {
                if (!defined($service->getLocation())) {
                        $self->throw("Service location not defined for service: " . $service->getName());
                }
        } else {
                $self->throw("Service must be an instance of Bio::Tools::Run::Chinook::Service");
        }
	if (!defined($file_to_upload)) {
		$self->throw("File to upload cannot be null");
	}

	my $transfer_filename = $self->_transferFileToRemotePerlClient($file_to_upload);
	print Dumper $transfer_filename;
	if (defined($transfer_filename)) {

        	my $socket = $self->_getSocket;
        	$self->_notifyReceive($socket);
        	print $socket "20\n";

        	my $service_location_string = $service->getLocation() . "\n";
        	print $socket $service_location_string;

		my $file_to_upload_string = $transfer_filename . "\n";
        	print $socket $file_to_upload_string;
	
        	my $file_id;

       		 while (my $string = <$socket>) {
               	 	chomp $string;
                	if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        	last;
                	}
                	else {
                        	if ($string =~ /UPLOADID:([\s\S]*)/g) {
                                	$file_id = $1;
                        	}
                        	else {
                                	$self->_stopSocket($socket);
                                	$self->throw("Could not upload file: " . $string);
                        	}
                	}
        	}
        	$self->_stopSocket($socket);
        	return $file_id;
	} else {
		$self->warn("File cannot be uploaded to server, returning -1 for file_id");
		return -1;
	}
}

sub downloadFile {
        my $self = shift;
        my $service = shift;
	my $file_id = shift;
        my $file_to_download_to = shift;

        if (!defined($service)) {
                $self->throw("Service cannot be null");
        }
        if ($service->isa('Bio::Tools::Run::Chinook::Service')) {
                if (!defined($service->getLocation())) {
                        $self->throw("Service location not defined for service: " . $service->getName());
                }
        } else {
                $self->throw("Service must be an instance of Bio::Tools::Run::Chinook::Service");
        }
        if (!defined($file_to_download_to)) {
                $self->throw("File to download to cannot be null");
        }
	if (!defined($file_id)) {
		$self->throw("File id cannot be null");
	}

	my $server_file = $self->_getNewTempTransferFileOnRemotePerlClient();
	if (!$server_file) {
		$self->throw("Could not download file.  Could not get new intermediate transfer file");
	}

        my $socket = $self->_getSocket;
        $self->_notifyReceive($socket);
        print $socket "21\n";

        my $service_location_string = $service->getLocation() . "\n";
        print $socket $service_location_string;

        my $file_id_string = $file_id . "\n";
        print $socket $file_id_string;

	my $file_to_download_to_string = $server_file . "\n";
        print $socket $file_to_download_to_string;

        while (my $string = <$socket>) {
                chomp $string;
                if ($string eq $self->{'SENDER_DONE_STRING'}) {
                        last;
                }
                else {
			if ($string =~ /DOWNLOAD:([\s\S]*)/g) {
                                print STDERR $1 . "\n";
                        }
			else {
                      		$self->_stopSocket($socket);
                      		$self->throw("Could not download file: " . $string);
			}
                }
        }
        $self->_stopSocket($socket);

	$self->_transferFileFromRemotePerlClient($server_file, $file_to_download_to);
}

#sub isReportReady {
#	my $self = shift;
#	my $client_id = shift;
#	my $reporting_directory = shift;
#
#	sleep (0.03);
#	
#	my $glob_name = $reporting_directory . "*.batchreport";
#	my @files = glob($glob_name);
#	foreach my $file (@files) {
#		#print STDERR $file . "\n";
#		if ($file =~ /([\S ]+)[.]{1}([\S ]+)[.]{1}batchreport/g) {
#			#print STDERR $2 . "\n";
#			if ($2 eq $client_id) {
#				return $file;
#			}
#		}
#	}
#	return 0;
#}

##PRIVATE

sub _notifySend {
	my $self = shift;
	my $socket = shift;
	if (!defined $socket) {
                $self->throw("Socket could not be bound on machine:" . $self->{'machine_name'} .
                                " port: " . $self->{'port'});
        }
	print $socket 1;
}

sub _notifyReceive {
	my $self = shift;
	my $socket = shift;
	if (!defined $socket) {
		$self->throw("Socket could not be bound on machine:" . $self->{'machine_name'} . 
				" port: " . $self->{'port'});
	}
	print $socket 2;
}

sub _stopSocket {
	my $self = shift;
	my $socket = shift;
	$socket->close or warn $@;
}

1;
