package lib::sql::DBH;
use DBI;
use lib::Exception;
use strict;
use vars qw /$ETERNAL $DBH/;

$ETERNAL = undef;
$DBH     = undef;


##
# __PACKAGE__->spawn ( param1 => $param1, ..., param_n => $param_n);
# ------------------------------------------------------------------
#   Initialises the DBHAccessor with the proper database
#   parameters.
##
sub spawn
{
    my $class = shift;
    $class = ref $class || $class;

    defined ($ETERNAL) and return $ETERNAL;

    my $self = bless { @_ }, $class;

    $ETERNAL = $self;
    return $self;
}


# Returns a dbh to play with
sub get
{
    my $self = shift;
    if (not defined $self or not ref $self) { $self = $ETERNAL }
    unless (defined $ETERNAL)
    {
	throw (new lib::sql::Exception ( code   => "MUST_SPAWN_DBH",
					 info   => "Cannot return \$dbh because " . ref $self . " has not been spawn" ) );
    }
    
    if (defined $DBH) { return $DBH }
    else
    {
	my $driver   = $self->{driver}   || "mysql";
        my $database = $self->{database} || "test";
        my $host     = $self->{host};
        my $port     = $self->{port};
	
	my $user     = $self->{user}     || "root";
	my $password = $self->{password} || undef;
        $database or throw (new lib::Exception ( code => "NO_DATABASE_NAME",
						 info   => "Cannot return \$dbh because no database name was specified" ) );
        
	my $dsn = undef;
	if ($driver eq 'mysql')       { $dsn = "DBI:$driver:database=$database" }
	elsif ($driver eq 'Pg') { $dsn = "DBI:$driver:dbname=$database"   }
	else
	{
	    throw (new lib::Exception ( code => 'DRIVER_NOT_SUPPORTED',
					info => "Driver $driver is not supported by " . ref $self . "!" ) );
	}
	$dsn   .= ":host=$host" if (defined $host and $host);
	$dsn   .= ":port=$port" if (defined $port and $port);
	my $dbh = undef;
	$@ = undef;
	eval
	{
	    $dbh = DBI->connect ($dsn, $user, $password, { RaiseError => 1, AutoCommit => 1 });
	};
	if ($@)
	{
	    throw (new lib::Exception ( code   => "CANNOT_CONNECT",
					info   => $@ ) );
	}
	
	$DBH = $dbh;
	return $dbh;
    }
}


# destroy the dababase handlers
sub kill
{
    if (defined $DBH) { $DBH->disconnect };
    $DBH = undef;
    $ETERNAL = undef;
}


# returns a string that restores this class state.
sub freeze
{
    my @res = ("# database connection information",
	       "# ---", "");
    
    my $obj = $lib::sql::DBH::ETERNAL;

    # if the object is defined, then we wanna write some Perl
    # code that's gonna restore its state later.
    if (defined $obj)
    {
	push @res, 'lib::sql::DBH->spawn (';
	push @res, join ",\n", map { "                      $_ => \"$obj->{$_}\"" } keys %{$obj};
	push @res, '                     );';
    }
    else { push @res, "# there is no database connection information to store" }
    push @res, ("","");
    return join "\n", @res;
}


1;
