package StructuresManager;

use strict;
use warnings;

use DBI;
use Digest::MD5 qw(md5 md5_hex md5_base64);
use DateTime;

use lib qw(Core);

use PDBUtils;
use Database;
use TargetsManager;
use DomainsManager;

use Configuration;
use LocalConfiguration;

my $structures_manager = undef;

sub new {
    my ($class) = @_;
    
    return $structures_manager if(defined($structures_manager));
    
    my $self = {
        _id => undef,
        _database => Database->new($CONFIG->{HOSTNAME}, $CONFIG->{PORT}, $CONFIG->{DATABASE}, $CONFIG->{USERNAME}, $CONFIG->{PASSWORD})
    };
    
    $structures_manager = bless $self, $class;
    return $structures_manager;
}

sub exist {
    my ($self, $id) = @_;
    
    my $result = 0;
    
    my $query = sprintf("SELECT COUNT(id) FROM casp9.structures WHERE (id = %d)", $id);
    my $sth = $self->{_database}->query($query);
    
    if(defined($sth) && ($sth->rows() > 0)) {
        my ($count) = $sth->fetchrow_array();
        
        $result = ($count == 1) ? 1 : 0;
    }
    
    return $result;
}

sub get_id_by_target {
    my ($self, $target_id) = @_;
    
    my $result = 0;
    
    my $query = sprintf("SELECT id FROM casp9.structures WHERE (targets_id = %d)", $target_id);
    my $sth = $self->{_database}->query($query);
    
    if(defined($sth) && ($sth->rows() > 0)) {
        ($result) = $sth->fetchrow_array();
    }
    
    return $result;
}

sub get_id_by_target_published {
    my ($self, $target_id) = @_;
    
    my $result = 0;
    
    my $query = sprintf("SELECT id FROM casp9.structures WHERE (targets_id = %d) and is_published = 1", $target_id);
    my $sth = $self->{_database}->query($query);
    
    if(defined($sth) && ($sth->rows() > 0)) {
        ($result) = $sth->fetchrow_array();
    }
    
    return $result;
}

sub publish {
    my ($self, $id) = @_;
    
    my $result = 0;
    
    return $result if(! $self->exist($id));
    
    my $query = sprintf("UPDATE casp9.structures SET is_published = 1, date = now() WHERE (id = %d)", $id);
    my $sth = $self->{_database}->query($query);
    
    if(defined($sth)) {
        # add logger
        $result = defined($sth) ? 1 : 0;
    }
    
    return $result;
}

sub unpublish {
    my ($self, $id) = @_;
    
    my $result = 0;
    
    return $result if(! $self->exist($id));
    
    my $query = sprintf("UPDATE casp9.structures SET is_published = 0 WHERE (id = %d)", $id);
    my $sth = $self->{_database}->query($query);
    
    if(defined($sth)) {
        # add logger
        $result = defined($sth) ? 1 : 0;
    }
    
    return $result;
}

sub info {
    my ($self, $target_id, $id) = @_;
    
    my %result = (
        TARGET => '',
        TARGET_ID => '',
        IS_PUBLISHED => '',
        RESOLUTION => '',
        R_FACTOR => '',
        SPACE_GROUP => '',
        NUMBER_OF_CHAINS => '',
        LIGANDS => '',
        NUMBER_OF_RESIDUES => '',
        MISSING => '',
        CHANGES => '',
        COMMENTS => '',
    );
    
    my $targets_manager = new TargetsManager();
    if(!defined($id) || (int($id) <= 0)) {
        $result{TARGET} = $targets_manager->name($target_id);
        return %result;
    }
    
    return %result if(! $self->exist($id));
    
    my $query = sprintf("SELECT id, is_published, targets_id, resolution, r_factor, space_group, number_of_chains, ligands, number_of_residues, missing, changes, comments FROM casp9.structures WHERE id = %d", $id);
    my $sth = $self->{_database}->query($query);
    
    if(defined($sth) && ($sth->rows() > 0)) {
        ($result{ID}, $result{IS_PUBLISHED}, $result{TARGET_ID}, $result{RESOLUTION}, $result{R_FACTOR}, $result{SPACE_GROUP}, $result{NUMBER_OF_CHAINS}, $result{LIGANDS}, $result{NUMBER_OF_RESIDUES}, $result{MISSING}, $result{CHANGES}, $result{COMMENTS}) = $sth->fetchrow_array();
        $result{TARGET} = $targets_manager->name($result{TARGET_ID});
    }
    
    return %result;
}

sub update {
    my ($self, $id, $target_id, $resolution, $r_factor, $space_group, $number_of_chains, $ligands, $number_of_residues, $missing, $changes, $comments, $update_date) = @_;
    
    my $result = 0;
    
    return $result if(! $self->exist($id));
    
    my $sub_query = '';
    if($update_date == 1) {
        $sub_query = ', date=now() ';
    }
    
    my $query = sprintf("UPDATE casp9.structures SET targets_id = %d, resolution = '%s', r_factor = '%s', space_group = '%s', number_of_chains = '%s', ligands = '%s', number_of_residues = '%s', missing = '%s', changes = '%s', comments = '%s' %s WHERE (id = %d)", $target_id, quotemeta($resolution), quotemeta($r_factor), quotemeta($space_group), quotemeta($number_of_chains), quotemeta($ligands), quotemeta($number_of_residues), quotemeta($missing), quotemeta($changes), quotemeta($comments), $sub_query, $id);
    my $sth = $self->{_database}->query($query);
    
    if(defined($sth)) {
        $result = 1;
    }
    
    return $result;
}

sub add {
    my ($self, $target_id, $resolution, $r_factor, $space_group, $number_of_chains, $ligands, $number_of_residues, $missing, $changes, $comments) = @_;
    
    my $result = 0;
    
    my $query = sprintf("INSERT INTO casp9.structures (targets_id, resolution, r_factor, space_group, number_of_chains, ligands, number_of_residues, missing, changes, comments) VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')", $target_id, $resolution, $r_factor, $space_group, $number_of_chains, $ligands, $number_of_residues, $missing, $changes, $comments);
    my $sth = $self->{_database}->query($query);
    
    if(defined($sth)) {
        my $query = sprintf("SELECT MAX(id) FROM casp9.structures");
        my $sth = $self->{_database}->query($query);
        
        ($result) = $sth->fetchrow_array();
    }
    
    return $result;
}

sub releases {
    my ($self, $published_only) = @_;
    
    $published_only = (!defined($published_only) || ($published_only eq 1)) ? 1 : $published_only;
    
    my $published_only_query = '';
    if($published_only) {
        $published_only_query = sprintf(" AND (s.is_published = 1)");
    }

    my @releases = ();
    
    my $query = sprintf("SELECT s.id, s.date, t.id, t.name FROM casp9.structures s JOIN casp9.targets t ON (t.id = s.targets_id) WHERE 1=1 %s ORDER BY s.date DESC", $published_only_query);
    my $sth = $self->{_database}->query($query);
    
    if(defined($sth) && ($sth->rows() > 0)) {
        my @structures = ();
        my $previous_date = '2000-01-01';
        my $index = 1;
        while(my($id, $date, $target_id, $target) = $sth->fetchrow_array()) {
            push(@structures, {
                INDEX => $index,
                ID => $id,
                DATE => $date,
                TARGET_ID => $target_id,
                TARGET_NAME => $target,
            });
            
            if($previous_date ne $date) {
                $previous_date = $date;
                
                push(@releases, { DATE => $date });
            }
            $index++;
        }
        
        for(my $i = 0; $i < scalar(@releases); $i++) {
            my @current_structures = ();
            for(my $j = 0; $j < scalar(@structures); $j++) {
                if($releases[$i]->{DATE} eq $structures[$j]->{DATE}) {
                    push(@current_structures, $structures[$j]);
                }
            }
            $releases[$i]->{TARGETS} = [@current_structures];
            $releases[$i]->{TARGETS_COUNT} = scalar(@current_structures);
        }
    }
    
    return @releases;
}

sub structures {
    my ($self, $field, $order, $published_only) = @_;
    
    my @structures = ();
    
    $field = (!defined($field) || ($field eq '')) ? 't.name' : $field;
    $order = (!defined($order) || ($order eq '')) ? 'ASC' : $order;
    $published_only = (!defined($published_only) || ($published_only eq 1)) ? 1 : $published_only;
    
    my $published_only_query = '';
    if($published_only) {
        $published_only_query = sprintf(" AND (s.is_published = 1)");
    }
    
    my $query = sprintf("SELECT s.id, s.is_published, t.id, t.name, s.resolution, s.r_factor, s.space_group, s.number_of_chains, s.ligands, s.number_of_residues, s.missing, s.changes, s.comments, ss.sequence FROM casp9.targets t LEFT JOIN casp9.structures s ON (s.targets_id = t.id) JOIN casp9.submitted_sequences ss ON (ss.id = t.submitted_sequences_id) WHERE (t.release_date < now()) %s ORDER BY %s %s", $published_only_query, $field, $order);
    #print $query;
    my $sth = $self->{_database}->query($query);
    
    if(defined($sth) && ($sth->rows() > 0)) {
        my $domains_manager = new DomainsManager();
        
        my $index = 1;
        while(my($id, $is_published, $target_id, $target, $resolution, $r_factor, $space_group, $number_of_chains, $ligands, $number_of_residues, $missing, $changes, $comments, $sequence) = $sth->fetchrow_array()) {
            my @domains = $domains_manager->get_domains($target_id);
            my $domains_count = scalar(@domains);
            
            push(@structures, {
                    INDEX => $index,
                    ID => (!defined($id) ? '-' : $id),
                    IS_PUBLISHED => $is_published,
                    TARGET_ID => (!defined($target_id) ? '-' : $target_id),
                    TARGET => (!defined($target) ? '-' : $target),
                    HAS_TARGET_FILE => (defined($target) && (-e sprintf("%s/%s.pdb", $LOCAL_CONFIG->{TARGETS_DIR}, $target))),
                    HAS_CHAIN_SUPERPOSIONS_FILE => (defined($target) && (int($number_of_chains) > 1) && (-e sprintf("%s/%s.%dchains.pdb", $LOCAL_CONFIG->{CHAIN_STRUCTURES_DIR}, $target, $number_of_chains))),
                    DOMAINS => [@domains],
                    DOMAINS_COUNT => $domains_count,
                    RESOLUTION => (!defined($resolution) || ($resolution eq '') ? '-' : $resolution),
                    R_FACTOR => (!defined($r_factor) || ($r_factor eq '') ? '-' : $r_factor),
                    SPACE_GROUP => (!defined($space_group) || ($space_group eq '') ? '-' : $space_group),
                    NUMBER_OF_CHAINS => (!defined($number_of_chains) || ($number_of_chains eq '') ? '-' : $number_of_chains),
                    LIGANDS => (!defined($ligands) || ($ligands eq '') ? '-' : $ligands),
                    NUMBER_OF_RESIDUES => (!defined($number_of_residues) || ($number_of_residues eq '') ? '-' : $number_of_residues),
                    MISSING => (!defined($missing) || ($missing eq '') ? '-' : $missing),
                    CHANGES => (!defined($changes) || ($changes eq '') ? '-' : $changes),
                    COMMENTS => (!defined($comments) || ($comments eq '') ? '-' : $comments),
                    NUMBER_OF_RESIDUES_SEQ => sequence_length($sequence),
            	}
            );
            $index++;
        }
    }
    
    return @structures;
}

1;