package TaskManager::DB;

use strict;
use warnings;

use DBI;

use TaskManager::Constants;
use TaskManager::System;

use Common::Log;

use constant DB_MYSQL_GONE_AWAY => 2006;
use constant DB_MYSQL_LOST_CONNECT => 2013;


our $dbh;
our $error;

our @ISA = qw(Exporter);
our @EXPORT = qw(
	connect_db
	disconnect_db
);


END {
	drop_log("Still having db connection on exit for pid=$$")
		if $dbh;
}

sub unbreakable_sleep {
    my $timeout = shift;
    my $endtime = time()+$timeout;
    while (1) {
        my $tosleep = $endtime - time();
        
        last unless ( $tosleep>0 );
        
        sleep($tosleep);
    }
}

sub connect_db {

	if( $dbh && $dbh->ping() ) {
#		drop_log("Use current DB connection for pid=$$");
		return $dbh;
	}

	$dbh->disconnect()
		if( $dbh );

	my $i = 0;
	do {
		#sleep $CFG->{'DB_CONNECTION_INTERVAL'}
        unbreakable_sleep($CFG->{'DB_CONNECTION_INTERVAL'})
			if $i++;
		$dbh = DBI->connect("DBI:mysql:$CFG->{DB_NAME}:$CFG->{DB_HOST}",$CFG->{'DB_USER'},$CFG->{'DB_PASSWORD'},{AutoCommit=>1});
	}
	while( !$dbh && $i < $CFG->{'DB_CONNECTION_ATTEMPTS'} );

	make_die("Can't connect to mysql server for $CFG->{DB_CONNECTION_ATTEMPTS} attempts: ".$DBI::errstr)
		unless( $dbh );

	if( $i>1 ) {
		drop_log("DB connection reestablished at $i attempt for pid=$$");
	}
	else {
#		drop_log("Connected to DB for pid=$$");
	}

	return $dbh;
}


sub disconnect_db {

	return undef
		unless $dbh;

	$dbh->disconnect();
	$dbh = undef;

#	drop_log("Disconnected from DB for pid=$$");

	return undef;
}


sub do_query {
	my $query = shift || die "No query given!";

	connect_db();

	my $i = 0;
	my $is_fatal = 0;
	my $err;

	do {
		$err = 0;
		$error = '';

		if( $i ) {
			drop_log("Lost connection, reconnecting for $i time for $$..");
			disconnect_db();
			unbreakable_sleep($CFG->{'DB_CONNECTION_INTERVAL'}) if $CFG->{'DB_CONNECTION_INTERVAL'};
			connect_db();
		}

		eval {
			$err = $dbh->err
				unless $dbh->do($query, undef, @_);
		};

		if( ($err && $err!=DB_MYSQL_GONE_AWAY && $err!=DB_MYSQL_LOST_CONNECT) || $@ ) {
			$is_fatal = 1;
			$error = $dbh->errstr || $@;
			drop_log("Error $err : $error for $$");
		}

		$i++;
	}
	while( $err && !$is_fatal && $i<$CFG->{'DB_CONNECTION_ATTEMPTS'} );

	$error .= ", can't run for $CFG->{DB_CONNECTION_ATTEMPTS} attempts!"
		if( $err==DB_MYSQL_GONE_AWAY || $err==DB_MYSQL_LOST_CONNECT );

	write_log($CFG->{'DB_LOGFILE'}, $$."\t".$query.',('.__params_str(@_).')'.($err ? '; ERR:'.$err : '').($error ? '; ERROR:'.$error : ''), {'script_name'=>0})
		if $CFG->{'DB_LOGFILE'};

	make_die($error)
		if $is_fatal;

	return $err ? 0 : 1;
}


sub __params_str {
	my $str = '';
	$str .= ($str ? ',' : '').(defined $_ ? $_ : '')
		for(@_);
	return $str;
}


sub select_query {
	my $query = shift || die "No query given!";

	connect_db();

	my $i = 0;
	my $is_fatal = 0;
	my $err;
	my $sth;

	do {
		$err = 0;
		$error = '';

		if( $i ) {
			drop_log("Lost connection, reconnecting for $i time for $$..");
			disconnect_db();
			connect_db();
		}

		eval {
			$err = $dbh->err
				unless ( $sth = $dbh->prepare($query) );

			unless( $err ) {
				$err = $sth->err
					unless $sth->execute(@_);
			}
		};

		if( ($err && $err!=DB_MYSQL_GONE_AWAY && $err!=DB_MYSQL_LOST_CONNECT) || $@ ) {
			$is_fatal = 1;
			$error = $dbh->errstr || $sth->errstr || $@ || "MYSQLERRCODE:$err";
		}

		$i++;
	}
	while( $err && !$is_fatal && $i<$CFG->{'DB_CONNECTION_ATTEMPTS'} );

	$error .= ", can't run for $CFG->{DB_CONNECTION_ATTEMPTS} attempts!"
		if( $err==DB_MYSQL_GONE_AWAY || $err==DB_MYSQL_LOST_CONNECT );

	write_log($CFG->{'DB_LOGFILE'}, $$."\t".$query.',('.__params_str(@_).')'.($err ? '; ERR:'.$err : '').($error ? '; ERROR:'.$error : ''), {'script_name'=>0})
		if $CFG->{'DB_LOGFILE'};

	make_die($error)
		if $is_fatal;

	my $REZ = [];
	while( my $hash = $sth->fetchrow_hashref() ) {
		push @$REZ, $hash;
	}

	$sth->finish();

	return $REZ;
}


1;
