################################################
##
## This module generates a 'backpack' object 
## filled with SingleDecisionEvents.
##
## Query engine functionality fetches appropriate
## events from the database. Fool proof checks
## ensure robust querying.
##
## Prereqs include a MySQL Database with
## events under study (see SQL dumps).
##
################################################
## 
## CHANGELOG
##
## 1.0 - 25 Apr 2013
## * First public version
##
################################################

package Finance::EventBackpack;

use strict;
use warnings;
use Carp;
use DBI;
use DBD::mysql;
use Data::Dumper;

################################################

our $AUTHOR  = 'Joachim De Schrijver';
our $VERSION = '1.0';
our $DATE    = '25 Apr 2013';
our $MODULE  = 'Finance::EventBackpack';

################################################

#################
## Constructor ##
#################

sub new {
	my $class = shift;	
	my @a = ();
	my $self = {
		agency             => undef,
		backpack_content   => \@a,	# Empty array
		query              => undef
	};

	bless($self, $class);
	return $self;
}

#############
## Setters ##
#############

sub set_agency {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{agency} = $a;
}

sub set_backpack_content {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{backpack_content} = $a;
}

sub set_query {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{query} = $a;
}

#############
## Getters ##
#############

sub get_agency {
	
	# Read params
	my $self = shift;
	
	# return
	return($self->{agency});
}

sub get_backpack_content {
	
	# Read params
	my $self = shift;
	
	# return
	return($self->{backpack_content});
}

sub get_query {
	
	# Read params
	my $self = shift;
	
	# return
	return($self->{query});
}

##############
## Changers ##
##############

###########
# This is an internal function and should not be used directly!
#
# Adds an event object to the backpack when processing
# the SQL query results.
#
# Input [array]:
#   1) SingleDecisionEvent object
#
# Output [no return]:
#   EventBackpack object [modified]
#
###########

sub add_backpack_item {

	# Read params
	my @params    = @_;
	my $self      = $params[0];
	my $chain_item= $params[1];

	# Get backpack content
	my @chain = @{$self->get_backpack_content()};
	
	# Sanity check on object to add
	if (ref($ref) ne "Finance::SingleDecisionEvent"){
		croak "Error in $MODULE object: cannot add object which are not Finance::SingleDecisionEvent to the backpack.\n Probably a database querying error occured!\n";
	}
	
	# Add to backpack content
	push(@chain,$chain_item);
	
	# Set backpack content
	$self->set_backpack_content(\@chain);
}

######################
## Object functions ##
######################

###########
# Queries SingleDecisionEvent objects from the databases
# and stores them in the backpack.
#
# Option hashes should contain references to key-value pairs.
# Values can contain multiple values separated by a pipe (|).
#
# Input [array]:
#   1) Agency [EMA/FDA...]
#   2) First option query hash (approval parameters)
#   3) Second option query hash (company parameters)
#   4) Database handler object
#
# Output [no return]:
#   EventBackpack object [modified]
#
###########

sub query_events {

	# Catch params
	my @params   = @_;
	my $self     = $params[0];
	my $agency   = $params[1];
	my $options1 = $params[2];
	my $options2 = $params[3];
	my $dbh      = $params[4];
	
	# Sanity check on agency (should be FDA, FDA2 or EMA)
	if (($agency ne "FDA") && ($agency ne "EMA") && ($agency ne "FDA2")){
		croak "Error in $MODULE object: specified agency should be EMA, FDA or FDA2!\n";
	}
	
	# Sanity check options 1 (check approval parameters)
	my @allowed1 = @{allowed_query_filter(1,$agency)};
	foreach my $option(keys %$options1){
		my $check = 0;
		foreach my $allowed(@allowed1){
			if ($option eq $allowed){
				$check = 1;
			}
		}
		if ($check == 0){
			croak "Error in $MODULE object: first options hash (approval query parameters) contains unallowed value!\n";
		}
	}
	
	# Sanity check options 2 (check company parameters)
	my @allowed2 = @{allowed_query_filter(2,$agency)};
	foreach my $option(keys %$options2){
		my $check = 0;
		foreach my $allowed(@allowed2){
			if ($option eq $allowed){
				$check = 1;
			}
		}
		if ($check == 0){
			croak "Error in $MODULE object: second options hash (company query parameters) contains unallowed value!\n";
		}
	}	
	
	# Set table name in function of agency
	my $table_name = "";
	if ($agency eq "EMA"){
		$table_name = "EMA_approvals_withdrawals_refusals_suspensions";
	}
	elsif($agency eq "FDA"){
		$table_name = "FDA_approvals";
	}
	elsif($agency eq "FDA2"){
		$table_name = "FDA_refusals";
	}
	else{
		croak "Error in $MODULE object: wrong agency type specified!\n";
	}
	
	# Build initial query part
	my $query = "SELECT ".$table_name.".date,companies_tickers.ticker,companies_tickers.Xchange FROM ".$table_name." JOIN companies_tickers ON ".$table_name.".company = companies_tickers.name";
	
	# Stuffer to initialise the WHERE clause. If you would just add WHERE, this would cause an error when there are no query options!...
	$query   .= " WHERE companies_tickers.id IS NOT NULL";		

	# Add the first option hash parameters
	foreach my $key (keys %$options1){
		my @values = split(/\|/,$options1->{$key});
		$query .= " AND (".$table_name.".".$key." = \"".$values[0]."\"";
		for (my $i = 2; $i <= scalar(@values); $i++){
			$query .= " OR ".$table_name.".".$key." = \"".$values[($i - 1)]."\"";
		}
		$query .= ")";
	}
	
	# Add the second option hash parameters
	foreach my $key (keys %$options2){
		my @values = split(/\|/,$options2->{$key});
		$query .= " AND (companies_tickers.".$key." = \"".$values[0]."\"";
		for (my $i = 2; $i <= scalar(@values); $i++){
			$query .= " OR companies_tickers.".$key." = \"".$values[($i - 1)]."\"";
		}
		$query .= ")";
	}
	
	# Sort events
	$query .= "ORDER BY date ASC";
	
	# Store query in backpack
	$self->set_query($query);
	
	# Store agency in backpack
	$self->set_agency($agency);
	
	# Run the query on the database handler
	my $sth = $dbh->prepare($query);
	$sth->execute();
	
	# Loop through results
	while (my @data = $sth->fetchrow_array()){
	
		# Generate single event obj
		my $event_obj = Finance::SingleDecisionEvent->new();
		my @extra = ();	# Temp placeholder to allow quick setter function on Finance::SingleDecisionEvent obj!
		$event_obj->quick_setter($data[0],$data[1],$data[2],\@extra);
		
		# Store single event obj in the backpack
		$self->add_backpack_item($event_obj);
	}
	
	# Finish sth
	$sth->finish();
}

######################
## Helper functions ##
######################

###########
# This is an internal function and should not be used directly!
#
# Verifies the specified query parameters. 
#
# Input [array]:
#   1) Agency [EMA/FDA...]
#
# Output [array]:
#   1) Array with allowed query parameters
#
###########

sub allowed_query_filter {
	
	# Catch params
	my @params = @_;
	my $type   = $params[0];	# Indicates whether option1/2 is checked
	my $agency = $params[1];
	
	# Approval paremeters - type 1
	if ($type == 1){
		# FDA
		if ($agency eq "FDA"){
			my @FDA = ("id", "date", "drug_name", "duplicate", "active_ingredient", "FDA_app_nr", "NDA_type", "review_classification", "company", "hist");
			return(\@FDA);
		}
		
		# FDA2
		if ($agency eq "FDA2"){
			my @FDA = ("id", "date", "drug_name", "duplicate", "active_ingredient", "company", "type", "hist");
			return(\@FDA);
		}		

		# EMA
		elsif ($agency eq "EMA"){
			my @EMA = ("id", "date", "drug_name", "drug_name_common", "duplicate", "hist", "active_ingredient", "EMA_app_nr", "ATC_code", "status", "company", "conditional_approval", "exceptional_circum", "orphan", "generic", "biosimilar");
			return(\@EMA);
		}

		# Other
		else{
			# Sanity checked before...
		}
	}
	
	# Company parameters - type 2
	elsif ($type == 2){
		my @allowed = ("id", "name", "Xchange", "ticker");
		return(\@allowed);
	}
	else{
		croak "Error in $MODULE object: unknown type specified to verify query parameters\n";
	}
}

1;