package LIMS::Controller::Local::Labels;

# provides methods for printing slide labels - requires entry in config/.local/worklists.yml

use base 'LIMS::Base';

use IO::Socket;

use Moose;
with (
	'LIMS::Controller::Roles::Misc',
	'LIMS::Controller::Roles::DataMap',
);

use MooseX::AttributeHelpers;

has labels => (
	is  => 'ro',
	isa => 'ArrayRef[HashRef]',
	default => sub { [] },
	lazy => 1,
	metaclass => 'Collection::Array',
	provides  => {
        push  => 'add_to_labels',
    }, 
);

__PACKAGE__->meta->make_immutable(inline_constructor => 0);

use Data::Dumper;

# ------------------------------------------------------------------------------
sub default : StartRunmode {
	my $self = shift; $self->_debug_path($self->get_current_runmode);
	
	# get label_type from query param, or complain:
	my $label_type = $self->query->param('label_type')
	|| return $self->error( 'no label_type provided at '
	   . $self->get_current_runmode );
	
	# check method exists or return error:
	unless ( UNIVERSAL::can($self, $label_type) ) {
		return $self->error( qq!no method  "$label_type" found at !
		. $self->get_current_runmode );
	}
	
	# forward to required method to generate data for template:
	my $data = $self->forward($label_type);
    $self->tt_params( labels => $data );
	
	return $self->render_view('worklist/local/labels/'.$label_type.'.tt', {});
}

# ------------------------------------------------------------------------------
sub histology_staining : Runmode {
    my $self = shift;
    
	# get lab_test details:
	my $lab_test = $self->get_lab_test_for_unique_test_name('haematoxylin_eosin')
	|| 	# have to die as default() caller doesn't handle err rtn: 
	die 'test_name = "haematoxylin_eosin" either not found, or is not unique';
	
	{ # get requests where test status is not complete:
		my %args = (
			status_option_id => 1, # if we need to restrict to status = 'new'
			lab_test_id      => $lab_test->id,
			fetch_only       => [ 'request' ], # don't need all cols from model 
		);
		
		my $data = $self->model('WorkList')->get_outstanding_investigations(\%args);	
		return $data;
	}
}

# ------------------------------------------------------------------------------
sub immunohistology_panels : Runmode {
	my $self = shift;
	
	# get list of lab_tests for immunohistochemistry secion:
	my %args = (
		section_name => 'Immunohistochemistry',
	);
	
	my $lab_tests = $self->model('LabTest')->get_section_lab_tests(\%args);
	
	my @lab_test_ids = map { $_->id } @$lab_tests; # warn Dumper \@lab_test_ids;
	
	{ # get requests where test status is not complete:
		my %args = (
#			status_option_id => 1, # if we need to restrict to status = 'new'
			lab_test_id      => \@lab_test_ids,
			fetch_only       => [ 'request' ], # don't need all cols from model 
		);
	
		my $data = $self->model('WorkList')->get_outstanding_investigations(\%args);
		return $data;
	}
}

# ------------------------------------------------------------------------------
sub print_histology_panel_labels : Runmode {
	my $self = shift;

	my @request_ids = $self->query->param('request_id'); # warn Dumper \@request_ids;	

	# if ! @request_ids, don't return via rm or may get loop if arrived via timeout:
	unless (@request_ids) {
		return $self->redirect(
			$self->query->url . '/local_labels?label_type=immunohistology_panels'
		);
	}

	my $yaml = $self->get_yaml_file('print_labels'); # warn Dumper $yaml;

	# get panel defs from immunohistology_panels section of yaml config:
	my $panels = $yaml->{immunohistology_panels}->{panels}; # warn Dumper $panels;
	
	# some tests don't require labels:
	my $skip_tests = $yaml->{immunohistology_panels}->{skip_tests};

	# get map of request_id => test_name(s):
	my $request_lab_test_map = $self->_get_request_test_name_map(\@request_ids);

	# just need request, investigation_name(s) & patient data on request_ids:
	for my $id (sort @request_ids) {
		my $o = $self->model('Request')->get_patient_and_request_data($id);

		my %d = (
			request => $o->as_tree(max_depth => 0), # only want base object data
			patient => $o->patient_case->patient->as_tree, # don't need dt object			
		);

		# get list of tests/panels for this request:
		my $investigations = $request_lab_test_map->{$id};

		# for each test/panel, calculate how many labels of each type required:
		TEST:
		for my $test (@$investigations) {
			next TEST if grep $test eq $_, @$skip_tests; # skip unwanted tests
			
			# if test_name is in the yaml cfg (ie it's a panel), add 1 label for
			# each label_list entry:
			if ( my $label_list = $panels->{$test} ) {
				for my $lbl (@$label_list) { # warn Dumper $lbl;
					my $data = $self->_format_label_data(\%d, $lbl);
					$self->add_to_labels($data);
				}
			}
			else { # probably individual test:
				my $label = $self->_format_label_data(\%d, $test);
				$self->add_to_labels($label);
			}
		}
	} 

    # do print_labels function:
    my $rtn = $self->_do_print_labels();
	# return error if method rtn value:
    return $self->error($rtn) if $rtn;
	
	# set flash message:
	$self->flash( info => $self->messages('worklist')->{print_labels_ok} );
	
	{ # make hash of request_id's for template:
		my %done = map { $_ => 1 } @request_ids;
		$self->tt_params( done => \%done );
	}
	
    return $self->forward('default');
}

# ------------------------------------------------------------------------------
sub print_histology_staining_labels : Runmode {
	my $self = shift;
	
	my @request_ids = $self->query->param('request_id'); # warn Dumper \@request_ids;	
	
	# if ! @request_ids, don't return via rm or may get loop if arrived via timeout:
	unless (@request_ids) {
		return $self->redirect(
			$self->query->url . '/local_labels?label_type=histology_staining'
		);
	}
	
	# get specimen_map for request_ids:
	my $specimen_map = $self->specimen_map(\@request_ids);
	
	my $yaml = $self->get_yaml_file('print_labels'); # warn Dumper $yaml;
	
	# get specimen_type defs from histology_staining section of yaml config:
	my $specimen_regex = $yaml->{histology_staining}->{specimen_types};
		# warn Dumper $specimen_regex;

	# just need request, specimen & patient data on request_ids:
	for my $id (sort @request_ids) {
		my $o = $self->model('Request')->get_patient_and_request_data($id);

		my %d = (
			request => $o->as_tree(max_depth => 0), # only want base object data
			patient => $o->patient_case->patient->as_tree, # don't need dt object			
		);

		my $specimen = $specimen_map->{$id}->{sample_code};
		
		# for each request_specimen, calculate how many labels of each type required:
		while ( my ($sample_code_regex, $label_list) = each %$specimen_regex ) {
			SPECIMEN:
			for my $sample_code (@$specimen) { # warn $sample_code;
				next SPECIMEN unless $sample_code =~ /$sample_code_regex/;
				# send request/patient data & label name to _format_label_data():
				for my $lbl (@$label_list) { # warn Dumper $lbl;
					my $data = $self->_format_label_data(\%d, $lbl);
					$self->add_to_labels($data);
				}
			}	
		}
	}
	
    # do print_labels function:
    my $rtn = $self->_do_print_labels();
	# return error if method rtn value:
    return $self->error($rtn) if $rtn;
	
	# set flash message:
	$self->flash( info => $self->messages('worklist')->{print_labels_ok} );
	
	{ # make hash of request_id's for template:
		my %done = map { $_ => 1 } @request_ids;
		$self->tt_params( done => \%done );
	}
	
    return $self->forward('default');
}

# ------------------------------------------------------------------------------
sub _do_print_labels {
    my $self = shift;
	
	my $labels = $self->labels; # warn Dumper $labels;

	my $peer_addr = $ENV{LOCALHOST_SOCKET} # print to a localhost socket for testing
		|| $self->cfg('settings')->{label_printer_addr}
		|| return 'no setting for label printer address found in config file';
	
    my $socket = new IO::Socket::INET(
        PeerAddr => $peer_addr,
        PeerPort => 9100,
        Proto 	 => 'tcp',
		Timeout  => 10,
    ) || return 'could not create socket: ' . $!;
    
    # need to shift 1st item from @data to format 1st label: 
    my $first_label = shift @$labels;

    my %first_label_data = (
		labno => $first_label->{labno},
		name  => $first_label->{name},
		label => $first_label->{label},
	);
	
	# process label.tt template:
	my $label_body = do {
		$self->tt_process('worklist/local/labels/label.tt', \%first_label_data);
	};

	# send 1st label to printer:
	print $socket ${$label_body}; # deref
	
    # (R)eplace 'labno', 'name' & 'label' params for rest of labels:
    for my $next_label (@$labels) { # warn Dumper $next_label;
		for my $field( qw/labno name label/ ) { # eg R LABEL;Giemsa R LABNO;H1/10 etc
			print $socket sprintf "R %s;%s\n", uc $field, $next_label->{$field};
		}
		print $socket "A 1\n"; # print 1 label each
    }

    close $socket;
	
	return 0; # as caller expects only errors returned
}

# ------------------------------------------------------------------------------
sub _format_label_data {
	my $self  = shift;
	my $data  = shift;
	my $label = shift; # warn $label;
	
	# need lab_no, patient name & label name:
	my $name  = ucfirst $data->{patient}->{last_name}; # truncate if too long
	my $labno = sprintf 'H%s/%s',
		$data->{request}{request_number}, $data->{request}{year} - 2000;
	
	my %data = (
		name  => $name,
		labno => $labno,
		label => $label,
	);
	
	return \%data;
}

# ------------------------------------------------------------------------------
sub _get_request_test_name_map {
	my ($self, $request_ids) = @_;
	
	my %args = (
		section_name => 'Immunohistochemistry',
		request_id   => $request_ids,
	);
	
	my $o = $self->model('LabTest')->get_request_lab_tests_status_for_section(\%args);
	
	my %map;
	for my $data (@$o) {
		my $request_id = $data->request_id;
		my $test_name = $data->lab_test->test_name;
		
		push @{ $map{$request_id} }, $test_name;
	} # warn Dumper \%map;
	
	return \%map;
}

1;