package TRL::Microarray::Microarray_File::GenePix;

use 5.006;
use strict;
use warnings;
our $VERSION = '0.17';


{ package genepix_file;

	require TRL::Microarray::Microarray_File::Data_File;

	our @ISA = qw( data_file );

	# setter for { _spot_data }, { _data_fields } and { _header_info }
	sub sort_data {
		my $self = shift;
		my $aaData = shift;
		my $aFile_Format = shift @$aaData;
		my $aRow_Cols = shift @$aaData;
		my $header_rows = $$aRow_Cols[0];
		my $col_num = $$aRow_Cols[1];
		$self->set_header_info($aaData,$header_rows);
		$self->set_data_fields(shift @$aaData);
		$self->{ _spot_data } = $aaData;		# all the numbers
		$self->{ _spot_count } = scalar @$aaData;
	}
	# information about the scan
	sub set_header_info {
		my $self = shift;
		my $aaData = shift;
		my $header_rows = shift;
		my $hHeader_Info = { };
		for (my $i=1; $i<=$header_rows; $i++) {
			my $aLine = shift @$aaData;
			my ($key,$value) = split(/=/,$$aLine[0]);
			if ($value =~ /\t/){
				my @aValues = split(/\t/,$value);
				$hHeader_Info->{ "CH1 $key" } = $aValues[0];
				$hHeader_Info->{ "CH2 $key" } = $aValues[1];
			} else {
				$hHeader_Info->{ $key } = $value;
			}
		}
		$self->{ _header_info } = $hHeader_Info;
	}
	sub pixel_size {
		my $self = shift;
		$self->get_header_info('PixelSize');
	}
	sub num_channels {
		2
	}
	sub channel1_name {
		'Cy5'
	}
	sub channel2_name {
		'Cy3'
	}
	sub channel_id {
		my $self = shift;
		my $ch = shift;
		"CH$ch";
	}
	sub channel_name {
		my $self = shift;
		my $ch = shift;
		my $method = "channel".$ch."_name";
		$self->$method;
	}	
	sub slide_barcode {
		my $self = shift;
		$self->get_header_info('Barcode');
	}
	sub gal_file {
		my $self = shift;
		$self->get_header_info('GalFile');
	}
	sub analysis_software {
		my $self = shift;
		$self->get_header_info('Creator');
	}
	sub scanner {
		my $self = shift;
		$self->get_header_info('Scanner');
	}
	sub user_comment {
		my $self = shift;
		$self->get_header_info('Comment');
	}
	sub channel1_image_file {
		my $self = shift;
		$self->get_header_info('CH1 FileName');
	}
	sub channel2_image_file {
		my $self = shift;
		$self->get_header_info('CH2 FileName');
	}
	sub channel_image_file {
		my $self = shift;
		my $ch = shift;
		$self->get_header_info("CH$ch FileName");
	}
	sub channel1_pmt {
		my $self = shift;
		$self->get_header_info('CH1 PMTVolts');
	}
	sub channel2_pmt {
		my $self = shift;
		$self->get_header_info('CH2 PMTVolts');
	}
	sub channel_pmt {
		my $self = shift;
		my $ch = shift;
		$self->get_header_info("CH$ch PMTVolts");
	}
	sub channel1_laser {
		my $self = shift;
		$self->get_header_info('CH1 LaserPower');
	}
	sub channel2_laser {
		my $self = shift;
		$self->get_header_info('CH2 LaserPower');
	}
	sub channel_laser {
		my $self = shift;
		my $ch = shift;
		$self->get_header_info("CH$ch LaserPower");
	}
	sub block_number {
		my $self = shift;
		if (@_){
			$self->{ _block_number } = shift;
		} else {
			unless (defined $self->{ _block_number }){
				$self->set_array_layout;
			}
			$self->{ _block_number };
		}
	}
	sub spot_columns {
		my $self = shift;
		if (@_){
			$self->{ _spot_columns } = shift;
		} else {
			unless (defined $self->{ _spot_columns }){
				$self->set_array_layout;
			}
			$self->{ _spot_columns };
		}
	}
	sub spot_rows {
		my $self = shift;
		if (@_){
			$self->{ _spot_rows } = shift;
		} else {
			unless (defined $self->{ _spot_rows }){
				$self->set_array_layout;
			}
			$self->{ _spot_rows };
		}
	}
	# this only works if the array is sorted by block/spot
	sub set_array_layout {
		my $self = shift;
		my $aaData = $self->spot_data;
		my $block_num = $self->get_column_id('Block');
		my $spot_row = $self->get_column_id('Row'); 
		my $spot_col = $self->get_column_id('Column'); 
		my $aLast_Row = $aaData->[-1];
		$self->block_number($aLast_Row->[$block_num]);
		$self->spot_columns($aLast_Row->[$spot_col]);
		$self->spot_rows($aLast_Row->[$spot_row]);
	}
	# data
	sub feature_id {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('Name'));
	}
	sub synonym_id {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('ID'));
	}
	sub x_pos {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('X'));
	}
	sub y_pos {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('Y'));
	}
	sub ch1_mean_f {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('F635 Mean'));
	}
	sub ch2_mean_f {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('F532 Mean'));
	}
	sub ch1_mean_b {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('B635 Mean'));
	}
	sub ch2_mean_b {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('B532 Mean'));
	}
	sub ch1_median_b {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('B635 Median'));
	}
	sub ch2_median_b {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('B532 Median'));
	}
	sub ch1_b1_sd {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('% > B635+1SD'));
	}
	sub ch2_b1_sd {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('% > B532+1SD'));
	}
	sub block_row {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('Block'));
	}
	sub block_col {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('Block'));
	}
	sub block {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('Block'));
	}
	sub spot_row {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('Row'));
	}
	sub spot_col {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('Column'));
	}
	sub channel1_signal {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('F635 Mean - B635'));
	}
	sub channel2_signal {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('F532 Mean - B532'));
	}
	sub ch1_sd_b {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('B635 SD'));
	}
	sub ch2_sd_b {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('B532 SD'));
	}
	sub ch1_sd_f {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('F635 SD'));
	}
	sub ch2_sd_f {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('F532 SD'));
	}
	sub ch1_median_f {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('F635 Median'));
	}
	sub ch2_median_f {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('F532 Median'));
	}
	sub channel1_quality {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('% > B635+2SD'));
	}
	sub channel2_quality {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('% > B532+2SD'));
	}
	sub channel1_sat {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('F635 % Sat.'));
	}
	sub channel2_sat {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('F532 % Sat.'));
	}
	sub spot_diameter {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('Dia.'));
	}
	sub spot_pixels {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('F Pixels'));
	}
	sub flag_id {
		my $self = shift;
		$self->return_data(shift,$self->get_column_id('Flags'));
	}
	sub bad_flags {
		{ '-50'=>'1', '-75'=>'1' }
	}
}

{ package genepix_image;

	require TRL::Microarray::Microarray_File;

	our @ISA = qw( microarray_image_file );
	
	sub set_header_data {
		my $self = shift;
		my $hInfo = $self->get_header_info;
		my $info = $hInfo->{HostComputer}."; ".$hInfo->{Artist};
		$info =~ s/AxImageIO: //g;
		my @aInfo = split(/; /,$info);
		for my $data (@aInfo){
			if ($data =~ /PMT|Power|Barcode/){
				my ($field,$value) = split(/=/,$data);
				$hInfo->{ "_$field" } = $value;
			}
		}
	}
	sub image_width {
		my $self = shift;
		my $hInfo = $self->get_header_info;
		return $hInfo->{'ImageWidth'};
	}
	sub image_height {
		my $self = shift;
		my $hInfo = $self->get_header_info;
		return $hInfo->{'ImageHeight'};
	}
	sub image_scanner {
		my $self = shift;
		my $hInfo = $self->get_header_info;
		return $hInfo->{'Model'};
	}
	sub laser_power {
		my $self = shift;
		my $hInfo = $self->get_header_info;
		return $hInfo->{'_LaserPower'};
	}
	sub pmt_gain {
		my $self = shift;
		my $hInfo = $self->get_header_info;
		return $hInfo->{'_PMT'};
	}
	sub scan_power {
		my $self = shift;
		my $hInfo = $self->get_header_info;
		return $hInfo->{'_ScanPower'};
	}
	sub slide_barcode {
		my $self = shift;
		my $hInfo = $self->get_header_info;
		return $hInfo->{'_Barcode'};
	}
	sub image_name {
		my $self = shift;
		my $hInfo = $self->get_header_info;
		return $hInfo->{'FileName'};
	}
	sub image_description {
		my $self = shift;
		my $hInfo = $self->get_header_info;
		return $hInfo->{'ImageDescription'};
	}
	sub fluor_excitation {
		my $self = shift;
		my $wavelength = $self->image_description;
		if ($wavelength =~ s/ \[R1\]//){
			$wavelength =~ s/\(|\)//g;
		} else {
			$wavelength =~ s/ \[W\d\]//;
		}
		return $wavelength;
	}
	sub collection_software {
		my $self = shift;
		my $hInfo = $self->get_header_info;
		return $hInfo->{'Software'};
	}
	sub image_datetime {
		my $self = shift;
		my $hInfo = $self->get_header_info;
		return $hInfo->{'FileModifyDate'};
	}
}

1;

__END__

=head1 NAME

TRL::Microarray::Microarray_File::GenePix - A Perl module for managing microarray Axon GenePix data files

=head1 SYNOPSIS

	use TRL::Microarray::Microarray_File::GenePix;

	my $data_file = genepix_file->new("/file.csv");
	my $image_file = genepix_image->new("/image.tif");

=head1 DESCRIPTION

TRL::Microarray::Microarray_File::GenePix is an object-oriented Perl module for managing microarray data files created by Axon's GenePix software. It inherits from L<TRL::Microarray::Microarray_File|TRL::Microarray::Microarray_File>, and maps data fields in the GenePix file to those used by L<TRL::Microarray::Microarray_File::Data_File|TRL::Microarray::Microarray_File::Data_File>. This module also parses GenePix image header info, although the amount of information contained there is limited.

=head1 METHODS

=head2 genepix_file methods

Where a method exists for "channel1" in the following methods, there is an equivalent method for "channel2". 

=head3 General methods - typically retrieving information from the header

=over

=item B<pixel_size>

Measured in micrometers. 

=item B<channel1_name>

i.e. Cy3, or Cy5. 

=item B<channel1_image_file>

The full path of the image file when saved by GenePix.

=item B<gal_file>

The full path of the GAL file, when/if imported by GenePix during data extraction. 

=item B<user_comment>

No idea where this comes from. But its there in the data file.

=item B<block_number>

GenePix numbers blocks consecutively, and does not record their layout in terms of rows/columns. While it isn't really very useful in the real World, this method returns this number. 

=item B<spot_columns>, B<spot_rows>

Number of columns and rows of spots in each block.

=back

The following methods don't really need any explanation;

=over

=item B<analysis_software>, B<num_channels>, B<slide_barcode>, B<channel1_pmt>, B<channel1_laser>

=back

=head3 Spot methods 

Pass the data row index to these methods to return information for a particular spot. The first row of data is index 0.

=over

=item B<block>, B<spot_row>, B<spot_col>

While there are no C<'block_row'> or C<'block_col'> equivalents in a GenePix file, these methods can be used and will return the C<block_number>. Also note there is no C<'spot_index'> field in GenePix. And to think that Axon invented the array file format.....

=item B<feature_id>, B<synonym_id>

The 'Name' and 'ID' columns respectively. 

=item B<x_pos>, B<y_pos>

Spot centre location in pixels, from the top-left of the image. 

=item B<flag_id>

The flag associated with the spot. 

=item B<ch1_median_f, ch1_mean_f, ch1_sd_f>

Median, mean and SD values for the fluorescence measurements of spot pixels.

=item B<ch1_median_b, ch1_mean_b, ch1_sd_b>

Median, mean and SD values for the fluorescence measurements of background pixels.

=item B<ch1_b1sd, channel1_quality, channel1_sat>

The percent of spot pixels 1 SD above background, percent of spot pixels 2 SD above background, and percent of spot pixels that are saturated.

=item B<channel1_snr>

GenePix does not return signal-to-noise ratios, so this is calculated as the median signal/background SD.

=item B<spot_diameter>, B<spot_pixels>

Diameter is measured in microns. 

=back

=head2 genepix_image methods

GenePix can save images as individual TIFF files, or both images in a single file. Channel specific information, such as the laser power and PMT, is not available in the combined file. To be honest, Axon have completely missed the boat when it comes to understanding the point of having information in the TIFF header - there's very little here that helps you keep track of your experiments, other than the slide barcode. Compare the information here with what is returned from a L<ScanArray image header|TRL::Microarray::Microarray_File::Quantarray/"quantarray_image methods">, and you'll see what I mean.

=over

=item B<image_barcode>

Returns the slide barcode associated with the scan. 

=item B<fluor_excitation>

The wavelength of the laser used to generate the image. Returns both wavelengths from combined image files.

=item B<pmt_gain>, B<laser_power>, B<scan_power>

PMT and laser powers used in the scan - return null from combined image files. No idea what C<'scan_power'> really means - always returns 100 for our scans! 

=item B<image_name>, B<image_width>, B<image_height>, B<image_datetime>

Basic image information.

=item B<image_scanner>, B<collection_software>

Scanner model and serial number, and some strange code that doesn't appear to have anything to do with the software version. Go figure....

=back

=head1 SEE ALSO

L<TRL::Microarray|TRL::Microarray>, L<TRL::Microarray::Microarray_File|TRL::Microarray::Microarray_File>, L<TRL::Microarray::Microarray_File::Data_File|TRL::Microarray::Microarray_File::Data_File>, L<Image::ExifTool|Image::ExifTool>

=head1 AUTHOR

Christopher Jones, Translational Research Laboratories, Institute for Women's Health, University College London.

L<http://www.instituteforwomenshealth.ucl.ac.uk/trl>

c.jones@ucl.ac.uk

=head1 COPYRIGHT AND LICENSE

Copyright 2007 by Christopher Jones, University College London

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself. 

=cut
