# Ensembl module for Bio::EnsEMBL::Analysis::RunnableDB::Funcgen
#
# Copyright (c) 2007 Ensembl
#
=head1 NAME

Bio::EnsEMBL::Analysis::RunnableDB::Fungen

=head1 SYNOPSIS

=head1 DESCRIPTION

This module is the base class for the Fungen Runnabledbs that act as an 
interface between the functional genomics database and the Funcgen Runnables 
both fetching input data and writing data back to the databases.

=head1 LICENCE

This code is distributed under an Apache style licence. Please see
http://www.ensembl.org/info/about/code_licence.html for details.

=head1 AUTHOR

Stefan Graf, Ensembl Functional Genomics - http://www.ensembl.org

=head1 CONTACT

Post questions to the Ensembl development list: ensembl-dev@ebi.ac.uk

=cut

package Bio::EnsEMBL::Analysis::RunnableDB::Funcgen;

use strict;
use warnings;
use Data::Dumper;

use Bio::EnsEMBL::Analysis::RunnableDB;
use Bio::EnsEMBL::Analysis::Config::General;
use Bio::EnsEMBL::Funcgen::DBSQL::DBAdaptor;

use Bio::EnsEMBL::Analysis::Tools::Utilities qw( parse_config );
use Bio::EnsEMBL::Utils::Argument qw( rearrange );
use Bio::EnsEMBL::Utils::Exception qw( throw warning stack_trace_dump );
use vars qw(@ISA);

@ISA = qw(Bio::EnsEMBL::Analysis::RunnableDB);

=head2 new

  Arg [1]   : Bio::EnsEMBL::Analysis::RunnableDB
  Arg [2]   : Bio::EnsEMBL::Pipeline::DBSQL::DBAdaptor
  Arg [3]   : Bio::EnsEMBL::Analysis
  Function  : create a Bio::EnsEMBL::Analysis::RunnableDB object
  Returntype: Bio::EnsEMBL::Analysis::RunnableDB
  Exceptions: throws if not passed either a dbadaptor, input id or
    an analysis object
  Example   : $rdb = $perl_path->new( -analysis => $self->analysis,
                                      -input_id => $self->input_id,
                                      -db => $self->adaptor->db );

=cut

sub new{

    print "Analysis::RunnableDB::Funcgen::new\n";
    my ($class,@args) = @_;
    #print stack_trace_dump();
    my $self = $class->SUPER::new(@args);
    return $self;

}


################################################################################
### Declare and set up config variables
################################################################################

sub read_and_check_config {

    print "Analysis::RunnableDB::Funcgen::read_and_check_config\n";

    my ($self, $config) = @_;

    # get config for logic_name
    warn("MODULE:  \t".$self->analysis->module);
    warn("LOGIC_NAME:\t".$self->analysis->logic_name);
    warn("INPUT_ID:\t".$self->input_id);
    warn("INPUT_ID_TYPE:\t".$self->analysis->input_id_type);

    warn("Reading config for '".$self->analysis->logic_name."'");
    parse_config($self, $config, $self->analysis->logic_name);
    #print Dumper $self;

    # first get default and eFG analysis config
    #warn("Reading config for '$logic_name'");
    #parse_config($self, $config, $logic_name);
    # then get config for pipeline analysis (individual experiment settings,
    # like RESULT_SET_REGEXP)
    

	#NJ Must define dnadb first and pass to efgdb during creation
	#This is to avoid errors when autoguessing the dnadb from ensembldb
	if($self->DNADB->{-dbname}){
	  $self->dnadb(Bio::EnsEMBL::DBSQL::DBAdaptor->new(%{ $self->DNADB })); 
	}

    # Make sure we have the correct DB adaptors!!!
    $self->efgdb(Bio::EnsEMBL::Funcgen::DBSQL::DBAdaptor->new
				 (
				  %{ $self->EFGDB },
				  -dnadb => $self->dnadb,
				 ));

   
    # Set analysis
    my $efg_analysis = new Bio::EnsEMBL::Analysis( -logic_name => $self->analysis->logic_name );
    $self->efg_analysis($efg_analysis);

    # extract experiment name from input_id

    # Set experiment and query
    $self->check_InputId();

    # Make sure we deal with the correct analysis object!!!
    # -- moved to child's news method --
    #$self->analysis->logic_name($logic_name);

    # Set normalization method (only for ChIP-chip on slices)
    unless ($self->analysis->input_id_type eq "File") {
        my $m = $self->efgdb->get_AnalysisAdaptor->fetch_by_logic_name($self->NORM_METHOD);
        $self->norm_method($m) 
            or throw("Can't fetch analysis object for norm method ".$self->NORM_METHOD);
        #print Dumper $self->norm_method;
    }

    # make sure we have valid feature and data set objects incl. supporting sets
    # -- moved to child's news method --
    #$self->check_Sets();
    

    # set score factor (hack to detect depletion)
    #if ($self->experiment->name =~ m/H3-Core|H3K9me2/) {
    #    $self->SCORE_FACTOR(-1);
    #} else {
        $self->SCORE_FACTOR(1);
    #}

}

sub check_InputId {

    print "Analysis::RunnableDB::Funcgen::check_InputId\n";

    my ($self) = @_;
    my @input = split(':', $self->input_id);

    # set experiment
    my $ename = shift @input;
    my $e = $self->efgdb->get_ExperimentAdaptor->fetch_by_name($ename);
    $self->experiment($e) 
        or throw("Can't fetch experiment with name ".$ename);
    #print Dumper $self->experiment;
    warn("EXPERIMENT:\t".$self->experiment->name);

    # set query
    my $input_id_type = $self->analysis->input_id_type();

    my $sa = $self->efgdb->get_SliceAdaptor;

    my @slices = ();
    if ($input_id_type eq 'Slice') {

        @slices = ( $sa->fetch_by_name(join(':', @input)) );
        throw("Can't fetch slice with name ".join(':', @input)) 
            unless (@slices);
        
    } elsif ($input_id_type eq 'Array') {

        @slices = @{$sa->fetch_all('toplevel')};
        throw("Can't fetch toplevel slices")
            unless (@slices);
        
    } else {

        throw("Input_id type '$input_id_type' not implemented.");

    }

    $self->query(\@slices);

}

sub check_Analysis {

    my ($self) = @_;

    # add/update efg_analysis config
    $self->efg_analysis->module($self->MODULE);
    $self->efg_analysis->program($self->PROGRAM);
    $self->efg_analysis->program_file($self->PROGRAM_FILE);
    $self->efg_analysis->program_version($self->VERSION);
    $self->efg_analysis->parameters($self->PARAMETERS);
    $self->efg_analysis->displayable(1);

    my $logic_name = $self->efg_analysis->logic_name;
    #print Dumper $self->efg_analysis, $logic_name;

    ### check if analysis with this logic_name already exists in the database
    ### and has got same settings
    
    my $aa = $self->efgdb->get_AnalysisAdaptor;
    my $analysis = $aa->fetch_by_logic_name($logic_name);
    
    if ( ! defined $analysis ) { # NEW

        warn("Storing new analysis with logic name $logic_name.");
        $aa->store($self->efg_analysis);

    } elsif ( $self->efg_analysis->compare($analysis) ) { # UPDATE

        ### analysis compare
        # returns  1 if this analysis is special case of given analysis
        # returns  0 if they are equal
        # returns -1 if they are completely different

        warn('Analysis with logic name \''.$logic_name.'\' already '.
             'exists, but has different options! Updating analysis ...');

        $self->efg_analysis->dbID($analysis->dbID);
        $self->efg_analysis->adaptor($self->efgdb->get_AnalysisAdaptor);
        $aa->update($self->efg_analysis);
 
    } else { # EXISTS

        warn('Analysis with logic name \''.$logic_name.'\' already '.
             'exists.');

    }

    $self->efg_analysis($aa->fetch_by_logic_name($logic_name));

}

sub check_Sets {

    my ($self) = @_;

    ### get result sets to process and check that result_set, data_set, 
    ### feature_set are storable

    my $rsets = $self->fetch_ResultSets();

    ### get feature_set/data_set name by determining the longest common 
    ### prefix of the result_set names
    #print Dumper @{$self->ResultSets()};
    my @names = map { $_->name; } @{$self->ResultSets()};
    #print Dumper @names;

    my %hash = ();
    foreach my $n (@names) {
        my @chars = split(//, $n);

        my $string = '';
        foreach my $c (@chars) {
            $string .= $c;
            #print Dumper $string;
            $hash{$string}++;
        }
    }
    my $lcp = '';

    foreach (sort keys %hash) {
        last if ($hash{$_} < scalar(@$rsets));
        #print Dumper( $_, $hash{$_}, scalar(@$rsets) ) ;
        $lcp = $_;
    } 
    $lcp =~ s/_$//;
    
    my $set_name = $self->efg_analysis->logic_name.'_'.$lcp; #.'_'.$self->DATASET_NAME;
    print 'Set name: ', $set_name, "\n";
    
    my $fsa = $self->efgdb->get_FeatureSetAdaptor();
    my $fset = $fsa->fetch_by_name($set_name);
    #print Dumper $fset;

    if ( ! defined $fset ) {

        $fset = Bio::EnsEMBL::Funcgen::FeatureSet->new
            (
             -analysis => $self->efg_analysis,
             -feature_type => $self->feature_type,
             -cell_type => $self->cell_type,
             -name => $set_name,
             -type => 'annotated'
             );
        #print Dumper $fset;
        
        warn("Storing new feature set \'$set_name\'");
        eval { 
            ($fset) = @{$fsa->store($fset)};
        };
        throw("Coudn't store feature set \'$set_name\': $!") if ($@);

    } else {

        warn("Feature set with name $set_name already exists.");

    }
    # save FeatureSet
    $self->feature_set($fset);

    my $dsa = $self->efgdb->get_DataSetAdaptor;
    my $dset = $dsa->fetch_by_name($set_name);
    #print Dumper $dset;

    if ( ! defined $dset ) {

        $dset = Bio::EnsEMBL::Funcgen::DataSet->new
            (
             -SUPPORTING_SETS     => $self->ResultSets,
             -FEATURE_SET         => $fset,
             -DISPLAYABLE         => 1,
             -NAME                => $set_name,
             -SUPPORTING_SET_TYPE => 'result',
             );
        #print Dumper $dset;

        warn("Storing new data set \'$set_name\'");
        eval { 
            ($dset) = @{$dsa->store($dset)} 
        };
        throw("Coudn't store data set \'$set_name\': $!") if ($@);

    } else {

        warn("Data set with name $set_name already exists.");

        # need to check whether ResultSets and supporting_sets are the same and 
        # possibly add ResultSet to supporting_sets

        my $ssets = $dset->get_supporting_sets();

        my %ssets_dbIDs = ();
        map { $ssets_dbIDs{$_->dbID}='' } (@{$ssets});
        map { 
            $dset->add_supporting_sets([ $_ ]) if (! exists $ssets_dbIDs{$_->dbID}); 
        } @{$self->ResultSets} 

    }
    # save DataSet
    $self->data_set($dset);

}

=head2 fetch_ResultSets

  Arg [1]   : Bio::EnsEMBL::Analysis::RunnableDB
  Function  : fetch and set ResultSets of interest
  Returntype: 1
  Exceptions: none
  Example   : 

=cut


sub fetch_ResultSets
{
    print "Analysis::RunnableDB::Funcgen::fetch_ResultSets\n";

    my $self = shift;
    #print Dumper $self;

    # fetch all available result_sets for given experiment and norm_method
    my $rsa = $self->efgdb->get_ResultSetAdaptor;
    my $rsets = $rsa->fetch_all_by_Experiment_Analysis
        ($self->experiment, $self->norm_method);
    print "No. of available ResultSets: ", scalar(@$rsets), "\n";

    # select only those result_sets that match regular expression
    my @rsets = ();
    my $regex = $self->RESULT_SET_REGEXP;
    foreach my $rset (@{$rsets}) {
        #print Dumper $rset->name();
        next if ($rset->name() !~ m/$regex/);
        push(@rsets, $rset);

        # check feature_type
        if (! defined $self->feature_type ) {
            $self->feature_type($rset->feature_type);
        } else {
            throw("replicates differ in feature types")
                if ($self->feature_type->dbID != $rset->feature_type->dbID);
        }

        # check cell_type
        if ( ! defined $self->cell_type() ) {
            $self->cell_type($rset->cell_type);
        } else {
            throw("replicates differ in cell types")
                if ($self->cell_type->dbID != $rset->cell_type->dbID);
        }

    }

    if (!@rsets) {
        
        my $rset_list = join(' ', map { $_->name } @{$rsets});
        throw ("RESULT_SET_REGEXP doesn't match any the following result set:\n$rset_list");
    }
    
    $self->ResultSets(\@rsets);

    print "Selected result sets: ", join(', ', map { $_->name } @rsets), 
    ' (in total ', scalar(@rsets), ")\n";

    return \@rsets;

}

################################################################################
### end of config
################################################################################


=head2 fetch_input

  Arg [1]     : Bio::EnsEMBL::Analysis::RunnableDB::Funcgen
  Description : fetch data out of database and create runnable
  Returns     : 1
  Exceptions  : none
  Example     : 

=cut

sub fetch_input {
    
    my ($self) = @_;
    print "Bio::EnsEMBL::Analysis::RunnableDB::Funcgen::fetch_input\n";
    
    #warn("write file: ", Dumper $self->db);
    
    my %result_features = ();
    my $norf;

    # make sure the cache directory exists
    my $cachedir = $self->ANALYSIS_WORK_DIR.'/cache';
    if ( ! -d $cachedir) {
        my $retval = system("mkdir -p $cachedir");
        throw("Couldn't create cache directory $cachedir") 
            unless ($retval == 0);
    }


    warn("INPUT_ID_TYPE: ". $self->analysis->input_id_type);
    
    my $input_id_type = $self->analysis->input_id_type;
    my $query_name;

    foreach my $rset (@{$self->ResultSets}) {

        print join(" ", $rset->dbID, $rset->name), "\n";

        if ($input_id_type eq 'Slice') {
            
            $query_name = $self->query->[0]->name();

        } elsif ($input_id_type eq 'Array') {
            
            my @echips = @{$rset->get_ExperimentalChips()};
            warn("WARNING: Result set '".$rset->name."' comprises more than one experimental chip, ".
                 "which will result in very large datafiles!")
                if (scalar @echips > 1);
            $query_name = 'ec_' . join (':', map { $_->unique_id } @echips);

        } else {

            throw("No idea how to deal with input_id type ".$input_id_type);

        }



        my $datfile = $cachedir.'/'.$query_name.'.'.$rset->name.'.dat';
        warn('datafile: '.$datfile);

        my @result_features = ();

        warn("SCORE_FACTOR: ".$self->SCORE_FACTOR);

        unless ( -e $datfile ) { # dat file already dumped; skipping sql

            foreach my $slice ( @{$self->query} ) {
            
                #my $result_features = $rset->get_ResultFeatures_by_Slice($self->query(),'DISPLAYABLE',1);
                #my $result_features = $rset->get_ResultFeatures_by_Slice($self->query(),'DISPLAYABLE');
                my $result_features = $rset->get_ResultFeatures_by_Slice($slice);

                print "No. of ResultFeatures_by_Slice:\t", scalar(@$result_features), "\n";
        
                if (scalar(@$result_features) == 0) {
                    warn("No result_features on slice ".$slice->name());
                    next;
                }

                my $ft_cnt = 1;
                
                foreach my $rft (sort {$a->start <=> $b->start} @{$result_features}) {
                    #print '<', join("><", 
                    #           $self->query()->seq_region_name, 
                    #           $rft->start, 
                    #           $rft->end, 
                    #           $rft->score*($self->SCORE_FACTOR), 
                    #           $rft->probe->get_probename), ">\n";
                    push (@result_features, 
                          [ 
                            $slice->seq_region_name,
                            $rft->start, 
                            $rft->end, 
                            $rft->score*$self->SCORE_FACTOR,
                            $ft_cnt++,
                            #$rft->probe->get_probename,
                            ]
                          );
                }
            }

            open(RF, "> $datfile")
                or throw("Can't open file $datfile");
            
            map {
                print RF join("\t", @$_),"\n";
            } @result_features;
            
            close RF
                or throw("Can't close file $datfile");
            
        } else {

            print "Using cached ResultFeatures:\t", $datfile, "\n";

            open(CACHE, $datfile)
                or throw("Can't open file cache $datfile");
            while (<CACHE>) {
                chomp;
                #print;

                my @values = split(/\t/);
                push (@result_features, 
                      [ 
                        @values
                        ]
                      );
            }           
            close CACHE;
            
        }

        
        warn("No. of ResultFeatures_by_Slice:\t".scalar(@result_features));

        throw ("No of result_features doesn't match") 
            if (@result_features && $norf && $norf != scalar(@result_features));

        $result_features{$rset->name} = \@result_features;
        
        $norf = scalar(@result_features);
        
    }

    if (scalar(keys %result_features) == 0) {

        warn('No ResultFeatures on slice');
        return 1;

    }
    
    #print Dumper %result_features;
    
    #$self->result_features(\%features);
    #print Dumper $self->result_features();
    #warn('EFG ANALYSIS PARAMETERS: '.$self->efg_analysis->parameters);

    my %parameters_hash = %{$self->parameters_hash($self->efg_analysis->parameters)};

    $parameters_hash{-result_features} = \%result_features;

    my $runnable = 'Bio::EnsEMBL::Analysis::Runnable::Funcgen::'
        .$self->efg_analysis->module;
    $runnable = $runnable->new
        (
         -query => $self->query,
         -program => $self->efg_analysis->program_file,
         -analysis => $self->efg_analysis,
         -workdir => $self->ANALYSIS_WORK_DIR,
         %parameters_hash
         );
    
    $self->runnable($runnable);
    
    return 1;
    
}

=head2 is_gzip

  Arg [1]     : file
  Description : check whether a file is gzipped or ASCII
  Returns     : boolean
  Exceptions  : none
  Example     : 

=cut

sub is_gzip {
    
    my ($self, $file) = @_;

    warn "Bio::EnsEMBL::Analysis::RunnableDB::Funcgen::is_gzip";

    open(FILE, "file -L $file |")
        or throw("Can't execute command 'file' on '$file'");
    my $retval = <FILE>;
    close FILE;

    return ($retval =~ m/gzip compressed data/) ? 1 : 0;
    
}


=head2 write_output

  Arg [1]   : Bio::EnsEMBL::Analysis::RunnableDB::Funcgen::Chipotle
  Function  : set analysis and slice on each feature
  Returntype: 1
  Exceptions: none
  Example   : 

=cut
    
sub write_output{
    
    print "Bio::EnsEMBL::Analysis::RunnableDB::Funcgen::write_output\n";
    my ($self) = @_;

    if (scalar(@{$self->output}) == 0) {
        warn("No features to annotate on slice ".$self->query->name.
             " for experiment ".$self->experiment->name()."!");
        return 1;
    }
    
    # store analysis, feature set and data set
    # analysis was alredy been stored while checking config in read_and_check_config
    #$self->adaptor('Analysis')->store($self->analysis());
    if (! defined $self->feature_set->dbID) {
        $self->efgdb->get_FeatureSetAdaptor->store($self->feature_set());
    }
    if (! defined $self->data_set->dbID) {
        $self->efgdb->get_DataSetAdaptor->store($self->data_set());
    }
    
    ### annotated features

    #my $af = $self->adaptor('AnnotatedFeature')->fetch_all_by_Slice_FeatureSet
    #    ($self->query, $fset);

    my $fset = $self->feature_set;	
    my $fs_id = $fset->dbID();

    my @stored_af = ();
    foreach my $s (@{$self->query}) {

        push @stored_af, 
        @{$self->efgdb->get_AnnotatedFeatureAdaptor->fetch_all_by_Slice_FeatureSets($s, [$fset])};

    }

    warn('No. of annotated features already stored: '.scalar(@stored_af));
    warn('No. of annotated features to be stored:  '.scalar(@{$self->output}));

    if (@stored_af) {
        
        warn("NOT IMPORTING ".scalar(@{$self->output})." annotated features! Slice(s) ".
             join (', ', map {$_->name} @{$self->query})." already contains ".scalar(@stored_af).
             " annotated features of feature set '".$fset->name."' (dbId ".$fset->dbID.').');
        return 1;
        
    } else {
        
        # we need to transfer annotated features onto the toplevel slice, if the input
        # input slice is not a toplevel slice (starting with 1). This applies only if 
        # the input id is of type 'Slice'.
        
        my ($transfer, $slice, $tl_slice);
        my $input_id_type = $self->analysis->input_id_type;
        
        if ($input_id_type eq 'Slice' && 
            ($self->query->[0]->start != 1 || $self->query->[0]->strand != 1)) {
            my $sa = $self->efgdb->get_SliceAdaptor;
            $tl_slice = $sa->fetch_by_region($self->query->[0]->coord_system->name(),
                                             $self->query->[0]->seq_region_name(),
                                             undef, #start
                                             undef, #end
                                             undef, #strand
                                             $self->query->[0]->coord_system->version());
            $transfer = 1;
            warn('TRANSFER: 1');
        }
        
        my %af_slice = ();
        
        if ($input_id_type eq 'Array') {
            map { $af_slice{$_->seq_region_name} = $_ } @{$self->query};
        }
        
        my @af = ();
        foreach my $ft (@{$self->output}){
            
            #print Dumper $ft;
            my ($sr_name, $start, $end, $score) = @{$ft};
            
            $slice = $input_id_type eq 'Slice' ? $self->query->[0] : $af_slice{"$sr_name"};
            
            #print Dumper ($sr_name, $start, $end, $score);
            my $af = Bio::EnsEMBL::Funcgen::AnnotatedFeature->new
                (
                 -slice         => $slice,
                 -start         => $start,
                 -end           => $end,
                 -strand        => 0,
                 -display_label => $self->efg_analysis->logic_name,
                 -score         => $score,
                 -feature_set   => $fset,
                 );
            
            # make sure feature coords are relative to start of entire seq_region
            if ($transfer) {
                #warn("original af:\t", join("\t", $af->start, $af->end), "\n");
                $af = $af->transfer($tl_slice);
                #warn("remapped af:\t", join("\t", $af->start, $af->end), "\n");
            }
            push(@af, $af);
        }
        
        $self->efgdb->get_AnnotatedFeatureAdaptor->store(@af);
    }
    return 1;
}


### container for config hash parameter

sub MODULE {
    my ( $self, $value ) = @_;

    if ( defined $value ) {
        $self->{'_CONFIG_MODULE'} = $value;
    }

    if ( exists( $self->{'_CONFIG_MODULE'} ) ) {
        return $self->{'_CONFIG_MODULE'};
    } else {
        return undef;
    }
}

sub PROGRAM {
    my ( $self, $value ) = @_;

    if ( defined $value ) {
        $self->{'_CONFIG_PROGRAM'} = $value;
    }

    if ( exists( $self->{'_CONFIG_PROGRAM'} ) ) {
        return $self->{'_CONFIG_PROGRAM'};
    } else {
        return undef;
    }
}

sub PROGRAM_FILE {
    my ( $self, $value ) = @_;

    if ( defined $value ) {
        $self->{'_CONFIG_PROGRAM_FILE'} = $value;
    }

    if ( exists( $self->{'_CONFIG_PROGRAM_FILE'} ) ) {
        return $self->{'_CONFIG_PROGRAM_FILE'};
    } else {
        return undef;
    }
}

sub VERSION {
    my ( $self, $value ) = @_;

    if ( defined $value ) {
        $self->{'_CONFIG_VERSION'} = $value;
    }

    if ( exists( $self->{'_CONFIG_VERSION'} ) ) {
        return $self->{'_CONFIG_VERSION'};
    } else {
        return undef;
    }
}

sub PARAMETERS {
    my ( $self, $value ) = @_;

    if ( defined $value ) {
        $self->{'_CONFIG_PARAMETERS'} = $value;
    }

    if ( exists( $self->{'_CONFIG_PARAMETERS'} ) ) {
        return $self->{'_CONFIG_PARAMETERS'};
    } else {
        return undef;
    }
}

sub SCORE_FACTOR {
    my ( $self, $value ) = @_;

    if ( defined $value ) {
        $self->{'_CONFIG_SCORE_FACTOR'} = $value;
    }

    if ( exists( $self->{'_CONFIG_SCORE_FACTOR'} ) ) {
        return $self->{'_CONFIG_SCORE_FACTOR'};
    } else {
        return undef;
    }
}


### 

=head2 dnadb

  Arg [1]   : Bio::EnsEMBL::Analysis::RunnableDB
  Arg [2]   : Bio::EnsEMBL::DBSQL::DBAdaptor
  Function  : container for dbadaptor
  Returntype: Bio::EnsEMBL::DBSQL::DBAdaptor
  Exceptions: throws if not passed a Bio::EnsEMBL::DBSQL::DBConnection
    object
  Example   : 

=cut


sub dnadb{
  my ($self, $db) = @_;
  if($db){
      throw("Must pass RunnableDB:db a Bio::EnsEMBL::DBSQL::DBAdaptor ".
            "not a ".$db) 
          unless($db->isa('Bio::EnsEMBL::DBSQL::DBAdaptor'));
      $self->{'dnadb'} = $db;
  }
  return $self->{'dnadb'};
}

=head2 efgdb

  Arg [1]   : Bio::EnsEMBL::Analysis::RunnableDB
  Arg [2]   : Bio::EnsEMBL::Funcgen::DBSQL::DBAdaptor
  Function  : container for dbadaptor
  Returntype: Bio::EnsEMBL::Funcgen::DBSQL::DBAdaptor
  Exceptions: throws if not passed a Bio::EnsEMBL::Funcgen::DBSQL::DBConnection
    object
  Example   : 

=cut


sub efgdb{
  my ($self, $db) = @_;
  if($db){
      throw("Must pass RunnableDB:db a Bio::EnsEMBL::Funcgen::DBSQL::DBAdaptor ".
            "not a ".$db) 
          unless($db->isa('Bio::EnsEMBL::Funcgen::DBSQL::DBAdaptor'));
      $self->{'efgdb'} = $db;
  }
  return $self->{'efgdb'};
}

sub efg_analysis {
    my ($self, $a) = @_;
    if ($a) {
      throw("Must pass a Bio::EnsEMBL::Analysis not a ".$a)
          unless($a->isa('Bio::EnsEMBL::Analysis'));         
      $self->{'efg_analysis'} = $a;      
    }
    return $self->{'efg_analysis'};
}

sub query {

    my ($self, $query) = @_;

    if ( $query ) {
        
        throw("Must pass RunnableDB:Funcgen:query a array ref not a ".
              ref($query)) unless (ref($query) eq 'ARRAY');

        map { 
            throw($_->name . " is not a Bio::EnsEMBL::Slice")
                unless ($_->isa("Bio::EnsEMBL::Slice"));
        } @$query;

        $self->{'query'} = $query;
    }

    return $self->{'query'};

}

sub experiment {
    my ($self, $e) = @_;
    if ($e) {
      throw("Must pass a Bio::EnsEMBL::Funcgen::Experiment not a ".$e)
          unless($e->isa('Bio::EnsEMBL::Funcgen::Experiment'));         
      $self->{'experiment'} = $e;      
    }
    return $self->{'experiment'};
}

sub norm_method {
    my ($self, $m) = @_;
    if ($m) {
      throw("Must pass a Bio::EnsEMBL::Ananysis not a ".$m)
          unless($m->isa('Bio::EnsEMBL::Analysis'));         
      $self->{'norm_method'} = $m;
    }
    return $self->{'norm_method'};
}

sub feature_type {
    my ($self, $ft) = @_;
    if ($ft) {
      throw("Must pass a Bio::EnsEMBL::Funcgen::FeatureType not a ".$ft)
          unless($ft->isa('Bio::EnsEMBL::Funcgen::FeatureType'));         
      $self->{'feature_type'} = $ft;
    }
    return $self->{'feature_type'};
}

sub cell_type {
    my ($self, $ct) = @_;
    if ($ct) {
      throw("Must pass a Bio::EnsEMBL::Funcgen::CellType not a ".$ct)
          unless($ct->isa('Bio::EnsEMBL::Funcgen::CellType'));         
      $self->{'cell_type'} = $ct;
    }
    return $self->{'cell_type'};
}

sub feature_set {
    my ($self, $fs) = @_;
    if ($fs) {
      throw("Must pass a Bio::EnsEMBL::Funcgen::FeatureSet not a ".$fs)
          unless($fs->isa('Bio::EnsEMBL::Funcgen::FeatureSet'));         
      $self->{'feature_set'} = $fs;
    }
    return $self->{'feature_set'};
}

sub data_set {
    my ($self, $ds) = @_;
    if ($ds) {
      throw("Must pass a Bio::EnsEMBL::Funcgen::DataSet not a ".$ds)
          unless($ds->isa('Bio::EnsEMBL::Funcgen::DataSet'));         
      $self->{'data_set'} = $ds;
    }
    return $self->{'data_set'};
}




sub ResultSets {
    my ($self, $sets) = @_;
    $self->{'ResultSets'} = $sets if ($sets);
    throw("No ResultSets in RunnableDB.") 
        if (!$self->{'ResultSets'});
    return $self->{'ResultSets'};
}



sub NORM_METHOD {
    my ( $self, $value ) = @_;

    if ( defined $value ) {
        $self->{'_CONFIG_NORM_METHOD'} = $value;
    }

    if ( exists( $self->{'_CONFIG_NORM_METHOD'} ) ) {
        return $self->{'_CONFIG_NORM_METHOD'};
    } else {
        return undef;
    }
}

sub RESULT_SET_REGEXP {
    my ( $self, $value ) = @_;

    if ( defined $value ) {
        $self->{'_CONFIG_RESULT_SET_REGEXP'} = $value;
    }

    if ( exists( $self->{'_CONFIG_RESULT_SET_REGEXP'} ) ) {
        return $self->{'_CONFIG_RESULT_SET_REGEXP'};
    } else {
        return undef;
    }
}

sub ANALYSIS_WORK_DIR {
    my ( $self, $value ) = @_;

    if ( defined $value ) {
        $self->{'_CONFIG_ANALYSIS_WORK_DIR'} = $value;
    }

    if ( exists( $self->{'_CONFIG_ANALYSIS_WORK_DIR'} ) ) {
        return $self->{'_CONFIG_ANALYSIS_WORK_DIR'};
    } else {
        return undef;
    }
}

sub EFGDB {
  my ( $self, $value ) = @_;

  if ( defined $value ) {
    $self->{'_CONFIG_EFGDB'} = $value;
  }

  if ( exists( $self->{'_CONFIG_EFGDB'} ) ) {
    return $self->{'_CONFIG_EFGDB'};
  } else {
    return undef;
  }
}

sub DNADB {
  my ( $self, $value ) = @_;

  if ( defined $value ) {
    $self->{'_CONFIG_DNADB'} = $value;
  }

  if ( exists( $self->{'_CONFIG_DNADB'} ) ) {
    return $self->{'_CONFIG_DNADB'};
  } else {
    return undef;
  }
}



1;
