package SubExperiment;
require Exporter;

use strict;
use GenomicsUtils;
use DBI;

our (@ISA, @EXPORT);

@ISA = qw(Exporter);
@EXPORT = qw();

{
sub new;
sub columns();
sub subExpId($);
sub expId($);
sub biological($);		# NULL (undef) if not curated
sub technical($);		# NULL if not curated
sub file($);			# may be NULL
sub controlDye($);		# NULL if not curated
sub treatmentDye($);		# NULL if not curated
sub imageId($);			# may be NULL
sub attributes($);		# returns attribute -> list of values
sub getSpotData($$$$);		# pass in attributes to fetch, etc.
sub getSpotAttrs($$$);		# pass in attributes to fetch, and additional where clause
sub attrTypes(@);		# non-member function: get types of attributes (which table they are from)

sub new{
    my %params = @_;
    my $self = {};
    bless $self;
    if (!exists $params{'expId'}) {
	die if !exists $params{'subExpId'};
	my @columns = columns();
	GenomicsUtils::queryAttrs("SubExpInfo", "subExpId", $params{'subExpId'}, \@columns, $self, "_");
    } else {
	foreach my $attr (keys %params) {
	    $self->{$attr."_"} = $params{$attr};
	}
    }
    return($self);
}

sub columns() {
    return qw(subExpId expId biological technical file controlDye treatmentDye imageId);
}

sub subExpId($) {
    return shift->{'subExpId_'};
}

sub expId($) {
    return shift->{'expId_'};
}

sub biological($) {
    return shift->{'biological_'};
}

sub technical($) {
    return shift->{'technical_'};
}

sub file($) {
    return shift->{'file_'};
}

sub controlDye($) {
    return shift->{'controlDye_'};
}

sub treatmentDye($) {
    return shift->{'treatmentDye_'};
}

sub imageId($) {
    return shift->{'imageId_'};
}

sub attributes($) {
    my $self = shift;
    my $hash = {};
    return( queryHashList("SELECT attr,value FROM SubExpAttrs WHERE subExpId=" .$self->subExpId()) );
}

# returns a hash of spotId->list, with one list member for each attribute passed in
# $failedOK, $minDyeCorr, and $keepNulls and $whereExtra allow you to restrict the results
sub getSpotData($$$$) {
    my ($self, $attrsRef, $failedOK, $minDyeCorr, $keepNulls, $whereExtra) = @_;
    my @attrs = map { "ArrayData.$_" } @$attrsRef;
    my $select = "SELECT ArrayData.spotId," . join(",",@attrs) . " FROM ArrayData";
    my $where = "ArrayData.subExpId=" . $self->subExpId();
    if (!$failedOK) {
	$select .= ", SpotInfo s";
	$where .= " AND s.spotId=ArrayData.spotId"
	    . " AND (s.mismatch IS NULL OR s.mismatch=0)"
	    . " AND (s.verificationCode IS NULL OR s.verificationCode=0)"
	    . " AND (s.contaminated IS NULL OR s.contaminated=0)";
    }
    $where .= " AND (ArrayData.dyeCorr IS NULL OR ArrayData.dyeCorr >= $minDyeCorr)"
	if (defined $minDyeCorr && $minDyeCorr > -1);
    map { $where .= " AND $_ IS NOT NULL" } @attrs if (!$keepNulls);
    $where .= " AND ($whereExtra)" if $whereExtra;
    my $result = GenomicsUtils::query("$select WHERE $where");
    my $hash = {};
    foreach my $row (@$result) {
	my $spotId = shift @$row;
	$hash->{$spotId} = $row;
    }
    return($hash);
}

# returns a reference to a hash of attr->spotId->value
# empty attribute set means all
sub getSpotAttrs($$$) {
    my ($self,$attrRef,$where) = @_;
    my $query = "SELECT spotId,attr,value FROM ArrayDataAttrs WHERE subExpId=" . $self->subExpId();
    if (scalar @$attrRef > 0) {
	my @quoted = map {"'" . $_ . "'"} @$attrRef;
	$query .= " AND attr IN (" . join("," , @quoted) . ")";
    }
    $query .= " AND ($where)" if $where;
    my $result = GenomicsUtils::query($query);
    my $hash = {};
    foreach my $row (@$result) {
	my ($spotId,$attr,$value) = @$row;
	$hash->{$attr} = {} if !exists $hash->{$attr};
	$hash->{$attr}{$spotId} = $value;
    }
    return($hash);
}

# returns a reference to a hash of attr->type, or empty string if unknown
sub attrTypes(@) {
    my @attrs = @_;
    my @knownSpotAttrs = qw( spotId chipId sourceId seq mismatch verificationCode
			     contaminated sector sectorRow sectorCol );
    my @knownDataAttrs = qw(subExpId log2Ratio log2RatioNormalized control treatment
			    dyeCorr leftPix rightPix topPix bottomPix );
    my %attrTypes = ();
    foreach my $attr (@attrs) {
	my $attrType;
	if ($attr =~ m/^SpotAttr\./) { $attrType = "SpotAttr"; }
	elsif ($attr =~ m/^ArrayDataAttr\./) { $attrType = "ArrayDataAttr"; }
	elsif ($attr eq "locusId") { $attrType = "locusId"; }
	elsif (Vector::within($attr, @knownSpotAttrs)) { $attrType = "SpotInfo"; }
	elsif (Vector::within($attr, @knownDataAttrs)) { $attrType = "ArrayData"; }
	else { $attrType = ""; }
	$attrTypes{$attr} = $attrType;
    }
    return \%attrTypes;
}

}
return 1;
