package LIMS::Model::HMRN::PrognosticIndicator;

use Moose::Role;
requires 'patient_id';

use Data::Dumper;

# all previous & current diagnostic categories on this patient:
has diagnostic_categories_map => (is => 'ro', isa => 'HashRef', lazy_build => 1);
# number of nodal & extranodal sites - set in prognostic_indicators():
has involved_sites => (is => 'rw', isa => 'HashRef', default => sub { {} });
# supplied by calling model class & set in prognostic_indicators()
has params_data => (is => 'rw', isa => 'HashRef', default => sub { {} });

has patient_age => (is => 'rw', isa => 'Int'); # calculated in prognostic_indicators()
has ann_arbor   => (is => 'rw', isa => 'Str'); # calculated in ann_arbor()

sub prognostic_indicators {
    my $self = shift;
    my $args = shift;
    
    my $dbix = $self->lims_dbix;

	# set some internal params for calculations:
	my $data = $args->{params_data}; # warn Dumper $data;
	$self->params_data($data);
	
    my $patient_id = $self->patient_id;
    
    # set some internal params for calculations:
	{ # number of extranodal sites + nodal sites from above & below diaphragm:
        my %involved_sites = ( # initialise all 3 regions:
			upper_nodal => 0,
			lower_nodal => 0,
			extranodal  => 0,                              
        );
        
        my $sql = $self->sql_lib->retr('hmrn_staging_sites');
        my $staging_locations = $dbix->query($sql)->map;

        SITE: while ( my ($description, $location) = each %$staging_locations ) {
                # warn Dumper [$description, $location];
            $involved_sites{$location}++ if $data->{staging}->{$description};
        } 
        $self->involved_sites(\%involved_sites); # warn Dumper \%involved_sites;
	}
	{ # need to calculate age again, as patient object not handed to model:
		my $sql = $self->sql_lib->retr('hmrn_calculate_patient_age');
		my $dates = $dbix->query($sql, $patient_id)->hash; # warn Dumper $dates;
		$self->inflate_mysql_dates_to_datetime($dates, [ qw(dob diagnosed) ]);
        
		my $age = LIMS::Local::Utils::calculate_age(@{$dates}{qw(dob diagnosed)});
        $self->patient_age($age); # warn "AGE: $age";
	} 
    
    my %indices = ();
    
    # do this 1st as some other calculated indices depend on it:
    if ( my $ann_arbor = $self->_calculate_ann_arbor ) {
        $indices{ann_arbor} = $ann_arbor;
    }
    if ( my $binet = $self->_calculate_binet ) {
        $indices{binet} = $binet;
    }
    if ( my $flipi = $self->_calculate_flipi ) {
        $indices{flipi} = $flipi;
    }
    if ( my $hipi = $self->_calculate_hipi ) {
        $indices{hipi} = $hipi;
    }
    if ( my $ipi = $self->_calculate_ipi ) {
        $indices{ipi} = $ipi;
    }
    
    return \%indices;
}

# calculate Ann-Arbor if HL, FL, DLBCL, EMZL or T-cell (ie not CLL, SMZL, MCL or HCL):
sub _calculate_ann_arbor {
    my $self = shift;
    
    my $data = $self->params_data;    
    
    # all previous & current diagnostic categories:
    my $categories_map = $self->diagnostic_categories_map; # warn Dumper $categories_map;
    
    my @categories = qw( Hodgkin FL DLBCL MZL T-lymphoproliferative );
	
	my $ct = $data->{lymphoid}->{ct} || 'N'; # avoid uninitialized error

    # Ann Arbor calc needs to be one of above, plus had a CT scan:
    return unless ( grep $categories_map->{$_}, @categories ) && $ct eq 'Y';

    my $sites = $data->{staging}; # list of nodal & extranodal sites involved
    my $i = $self->involved_sites; # numerical representation of site distribution

    my $AA;
    
	# marrow involvement, extensive disease or > 1 extranodal site:
	if ( $sites->{marrow} || $sites->{extensive} || $i->{extranodal} > 1 ) {
	 	$AA = 'IV'; # number of nodal sites not relevant
	}
	# both upper & lower diaphragm involved:
	elsif ( $i->{upper_nodal} && $i->{lower_nodal} ) { $AA = 'III' }
	# either upper or lower diaphragm involved with > 1 group:
	elsif ( $i->{upper_nodal} > 1 || $i->{lower_nodal} > 1 ) { 
		$AA = sprintf 'II[%s]', $i->{upper_nodal} || $i->{lower_nodal};
	}
	# one group only, or no nodal involvement:
	elsif ( ($i->{upper_nodal} + $i->{lower_nodal}) <= 1 ) { $AA = 'I' }
	else { $AA = '[Unclassifiable - check data]' }  # presumably cocked-up the algorithm!!  
	
	$AA .= 'S' if ($sites->{spleen});
	$AA .= 'X' if ($sites->{bulky});
	# ? needs modifying to contiguous/proximal to nodal site:
	$AA .= 'E' if ($i->{extranodal} && $AA !~ /^IV/); # any extranodal & not already IV
	$AA .= 'B' if grep $data->{lymphoid}->{$_} eq 'Y', qw(sweats wt_loss fever);
    
	# some other calculated indices depend on this value:
    $self->ann_arbor($AA); # warn Dumper $AA;
	return $AA;
}

# calculate IPI if DLBCL or MCL + Ann-Arbor + age: 
sub _calculate_ipi {
    my $self = shift;
    
    # all previous & current diagnostic categories:
    my $categories_map = $self->diagnostic_categories_map;
    
    return unless grep $categories_map->{$_}, qw(DLBCL MCL); 
    
    my $data = $self->params_data;
    my $age  = $self->patient_age;
    my $AA   = $self->ann_arbor;
    
    my $i = $self->involved_sites; # numerical representation of site distribution
    
	my $ecog = $data->{lymphoid}->{ecog};
	my $ldh  = $data->{lymphoid}->{ldh};

	my $ipi;

    if ( $ldh && (defined $ecog) && $age &&	$AA ) { # ECOG can be '0'
		my $score = 0 # add 1 for each (range 0 to 4):
			+ ( $AA =~ /^(III|IV)/ )
			+ ( $ecog > 1 )
			+ ( $ldh eq 'raised' )
			+ ( $age > 60 && $i->{extranodal} > 1 ); # only relevant > 60yr

		# works for both IPI & age-adjusted IPI, if age > 60 score cannot be 0,
		# so don't need to consider age 
		my @risk = qw(low low-intermediate high-intermediate high high); 
		
		$ipi = sprintf "%s [%s]", $risk[$score], $score; # warn Dumper $ipi;
	}
	return $ipi;
}

# calculate HIPI if HL: 
sub _calculate_hipi {
    my $self = shift;
    
    # all previous & current diagnostic categories:
    my $categories_map = $self->diagnostic_categories_map;
    
    return unless $categories_map->{Hodgkin};
    
    my $dbix = $self->lims_dbix;

    my $sql = 'select gender FROM patients WHERE id = ?';
	my $gender = $dbix->query($sql, $self->patient_id)->list || 'U';

    my $data = $self->params_data;
    my $age  = $self->patient_age;
    my $AA   = $self->ann_arbor;
    
	my $lymph_data = $data->{lymphoid};

	my $albumin = $lymph_data->{albumin};
	my $lymphs  = $lymph_data->{lymphs};
	my $wbc = $lymph_data->{wbc};
	my $hb  = $lymph_data->{hb};

	my $hipi;

	if ( $age && $AA && $gender # plus all 4 params defined:
			&& !grep { !defined $lymph_data->{$_} } qw/hb albumin wbc lymphs/ ) {
		my $score = 0 # add 1 for each (range 0 to 7):
			+ ( $gender eq 'M')
			+ ( $age >= 45 )
			+ ( $AA =~ /^IV/ )
			+ ( $wbc > 15 )
			+ ( $hb < 10.5 )
			+ ( $lymphs < 0.6 )
			+ ( $albumin < 40 ); # = 4g/dL 

		my $risk = $score <= 2 ? 'low-risk' : 'mod/high risk';
		$hipi = sprintf "%s [%s]", $risk, $score; 
	}
    return $hipi;
}

# calculate FLIPI if FL or diffuse FL: 
sub _calculate_flipi {
    my $self = shift;    
    
    # all previous & current diagnostic categories:
    my $categories_map = $self->diagnostic_categories_map;
    
    return unless $categories_map->{FL};
    
    my $data = $self->params_data;
    my $age  = $self->patient_age;
    my $AA   = $self->ann_arbor;

    my $i = $self->involved_sites; # numerical representation of site distribution

	my $ldh = $data->{lymphoid}->{ldh}; # warn Dumper $ldh;
	my $hb  = $data->{lymphoid}->{hb};  # warn Dumper $hb;
	my $ct  = $data->{lymphoid}->{ct} || 'N'; # avoid uninitialized error

	my $flipi;

	if ( $age && $AA && $hb && $ldh && $ct eq 'Y' ) {
		my @risk = qw(good good intermediate poor poor poor); # for scores 0 - 5 

		my $score = 0 # add 1 for each (range 0 to 5):
			+ ( $age > 60 )
			+ ( $hb < 12 )
			+ ( $ldh eq 'raised' )
			+ ( $AA =~ /^(III|IV)/ )
			+ ( $i->{upper_nodal} + $i->{lower_nodal} > 4 );

		$flipi = sprintf "%s [%s]", $risk[$score], $score; # warn Dumper $flipi;
	}
    return $flipi;
}

# BINET for CLL:
sub _calculate_binet {
    my $self = shift;
    
    # all previous & current diagnostic categories:
    my $categories_map = $self->diagnostic_categories_map;
    
    return unless $categories_map->{CLL};
    
    my $data = $self->params_data;

    my $sites = $data->{staging}; # list of nodal & extranodal sites involved
    my $i = $self->involved_sites; # numerical representation of site distribution

	my $binet;

	my $plts = $data->{lymphoid}->{plts};
	my $hb   = $data->{lymphoid}->{hb};

	if ( $hb && $plts ) {
		if ($hb < 10 || $plts < 100) { $binet = 'C' }
		else { # 1 for each of neck, axillae, groin, spleen and liver (assumes CLL does not get CT scan)
			no warnings 'uninitialized'; # adding undef vals causes error
			my $sites = 0 # add numeric data:
				+ $i->{upper_nodal}
				+ $i->{lower_nodal}
				+ $sites->{liver};

			$binet = $sites >= 3 ? 'B' : 'A';
		}
	} 
    return $binet;
}

# get all previous & current diagnostic categories on this patient:
sub _build_diagnostic_categories_map {
    my $self = shift; # warn $self->patient_id;
    
    my $dbix = $self->lims_dbix;
    
	my $sql = $self->sql_lib->retr('hmrn_patient_diagnostic_categories');

	my $categories = $dbix->query($sql, $self->patient_id)->flat; # warn Dumper $categories;
    my %map = map { $_ => 1 } @$categories; # warn Dumper \%map;
    return \%map;    
}

1;
