#!/usr/bin/perl
package Persistent::Hash::Storage::MySQL;

use strict;
use Carp qw(croak);

use DBI;
use Data::Dumper;

use vars qw($VERSION);

$VERSION = '0.1';

sub LoadObjectInfo
{
	my $classname = shift;
	my $object_package = shift;
	my $id = shift;

	croak "Attempt to call LoadObjectInfo() as a function call" if not defined $classname;
	croak "No type passed to LoadObjectInfo()" if not defined $object_package;
	croak "No id passed to LoadObjectInfo()" if not defined $id;

	my $dbh = $object_package->DatabaseHandle();
	croak "Could not obtain a database handle!" if not defined $dbh;

	my $info_table = $object_package->INFO_TABLE();

	#Load the object informations
	my $load_info_query = "SELECT 
					type,
					time_created,
					time_modified
				FROM
					$info_table
				WHERE
					id = ?";
	my $load_info_sth = $dbh->prepare_cached($load_info_query) || die "Could not prepare $load_info_query: $DBI::errstr";
	$load_info_sth->execute($id) || die "Could not execute $load_info_query: $DBI::errstr";
	
	my ($type,$time_created,$time_modified) = $load_info_sth->fetchrow_array();
	$load_info_sth->finish();

	my $object_info = undef;

	if(defined $type)
	{
		$object_info = {};
		$object_info->{type} = $type,
		$object_info->{time_created} = $time_created;
		$object_info->{time_modified} = $time_modified;
	}
	
	return $object_info;
}
		
sub LoadObjectData
{
	my $classname = shift;
	croak "Attempt to call LoadObjectData() as a function call" if not defined $classname;

	my $object = shift;
	croak "No object passed to LoadObjectData()" if not defined $object;
	croak "Wrong object side!" if tied %$object; 

	return undef if not defined $object->{_object_id};

	my $data_table = $object->DATA_TABLE();

	my $dbh = $object->DatabaseHandle;
	croak "Could not obtain database handle!" if not defined $dbh;

	#Load the object data
	my $load_data_query = "SELECT 	
					data
				FROM
					$data_table
				WHERE
					id = ?";
	my $load_data_sth = $dbh->prepare_cached($load_data_query) || die "Could not prepare $load_data_query: $DBI::errstr";
	$load_data_sth->execute($object->{_object_id}) || die "Could not execute $load_data_query: $DBI::errstr";
	my $data = $load_data_sth->fetchrow();
	$load_data_sth->finish();


	$data = eval "+"."$data";


	return $data;
}		

sub LoadObjectIndex
{
	my $classname = shift;
	croak "Attempt to call LoadObjectIndex() as a function call" if not defined $classname;

	my $object = shift;
	croak "No object passed to LoadObjectIndex()" if not defined $object;
	croak "Wrong object side!" if tied %$object;

	my $dbh = $object->DatabaseHandle();
	croak "Could not obtain database handle" if not defined $dbh;

	my $index_table = $object->INDEX_TABLE();
	my $index_fields = $object->INDEX_FIELDS();

	my $loaded_fields = {};

	if(@$index_fields)
	{
		my $load_index_query = "SELECT
					".(join ',', @$index_fields)."
				FROM
					$index_table
				WHERE
					id = ?";
		my $load_index_sth = $dbh->prepare_cached($load_index_query) || die "Could not prepare $load_index_query: $DBI::errstr";
		$load_index_sth->execute($object->{_object_id}) || die "Could not execute $load_index_query: $DBI::errstr";
		$loaded_fields = $load_index_sth->fetchrow_hashref();
		$load_index_sth->finish();
	}

	return $loaded_fields;
}	

sub DeleteObject
{
	my $classname = shift;
	my $self = shift;
	
	my $object = tied %$self if tied %$self;
	croak "Wrong object side!" if not defined $object;

	croak "Attempt to call DeleteObject() as a function call." if not defined $classname;
	croak "No object passed to DeleteObject()" if not defined $object;

	return undef if not $self->Id();

	my $dbh = $self->DatabaseHandle();
	croak "Could not obtain a database handle!" if not defined $dbh;


	my $info_table = $object->INFO_TABLE();
	my $data_table = $object->DATA_TABLE();
	my $index_table = $object->INDEX_TABLE();
	my $hash_id = $object->{_object_id};

	my $time = time();

	my $delete_info_query = "DELETE FROM $info_table WHERE id = ?";	
	my $delete_info_sth = $dbh->prepare_cached($delete_info_query) || die "Could not prepare $delete_info_query: $DBI::errstr";
	$delete_info_sth->execute($hash_id) || die "Could not execute $delete_info_query: $DBI::errstr";

	my $delete_data_query = "DELETE FROM $data_table WHERE id = ?";
	my $delete_data_sth = $dbh->prepare_cached($delete_data_query) || die "Could not prepare $delete_data_query: $DBI::errstr";
	$delete_data_sth->execute($hash_id) || die "Could not execute $delete_info_query: $DBI::errstr";

	my $delete_index_query = "DELETE FROM $index_table WHERE id = ?";
	my $delete_index_sth = $dbh->prepare_cached($delete_index_query) || die "Could not prepare $delete_index_query: $DBI::errstr";
	$delete_index_sth->execute($hash_id) || die "Could not execute $delete_index_query: $DBI::errstr";
	
	return 1;
}

sub InsertObject
{
	my $classname = shift;
	my $self = shift;
	
	my $object = tied %$self if tied %$self;
	croak "Wrong object side!" if not defined $object;

	croak "Attempt to call InsertObject() as a function call." if not defined $classname;
	croak "No object passed to InsertObject()" if not defined $object;

	my $dbh = $self->DatabaseHandle();
	croak "Could not obtain a database handle!" if not defined $dbh;

	my $info_table = $self->INFO_TABLE();
	my $data_table = $self->DATA_TABLE();
	my $index_table = $self->INDEX_TABLE();
	my $time = time();

	my $object_data = $object->{_data};
	my $object_index_data = $object->{_index_data};
	my $index_fields = $object->INDEX_FIELDS();

	#Insert the info record
	my $insert_info_query = "INSERT INTO 
						$info_table(type,time_created,time_modified)
						values(?,?,?)";
	my $insert_info_sth = $dbh->prepare_cached($insert_info_query) || die "Could not prepare $insert_info_query: $DBI::errstr";
	$insert_info_sth->execute($self->Type(), $time,$time) || die "Could not execute $insert_info_query: $DBI::errstr";
	my $object_id = $insert_info_sth->{'mysql_insertid'};
	$insert_info_sth->finish();


	#Insert the data record
	if($object_data)
	{
		my $insert_data_query = "INSERT INTO
						$data_table(id,data)
						values(?,?)";
		my $insert_data_sth = $dbh->prepare_cached($insert_data_query) || die "Could not prepare $insert_data_query: $DBI::errstr";
		$insert_data_sth->execute(
					$object_id,
					$object->_FlattenData($object_data)||"{}") || die "Could not execute $insert_data_query: $DBI::errstr";
		$insert_data_sth->finish();
	}

	#Insert the index record
	if(keys %$object_index_data)
	{
		my $index_values = [(map $object->{_index_data}->{$_}, @$index_fields)];

		my $insert_index_query = "INSERT INTO
						$index_table(id,".(join ',', @$index_fields).")
						values(?,".(join ',', map('?', @$index_fields)).")";
		my $insert_index_sth = $dbh->prepare($insert_index_query) || die "Could not prepare $insert_index_query: $DBI::errstr";
		$insert_index_sth->execute(
					$object_id, 
					@$index_values) || die "Could not execute $insert_index_query: $DBI::errstr";
		$insert_index_sth->finish();
	}

	return $object_id;
}
	
sub UpdateObject
{
	my $classname = shift;
	my $self = shift;

	my $object = tied %$self if tied %$self;
	croak "Wrong object side!" if not defined $object; 

	croak "Attempt to call UpdateObject() as a function call." if not defined $classname;
	croak "No object passed to UpdateObject()" if not defined $object;

	my $dbh = $self->DatabaseHandle();
	croak "Could not obtain a database handle!" if not defined $dbh;

	my $info_table = $object->INFO_TABLE();
	my $data_table = $object->DATA_TABLE();
	my $index_table = $object->INDEX_TABLE();
	my $time = time();

	my $object_data = $object->{_data};
	my $object_index_data = $object->{_index_data};
	my $index_fields = $object->INDEX_FIELDS();

	#Update the info record.
	my $update_info_query = "UPDATE $info_table
					SET
						type = ?,
						time_created = ?,
						time_modified = ?
					WHERE 
						id = ?";


	my $update_info_sth = $dbh->prepare_cached($update_info_query) || die "Could not prepare $update_info_query: $DBI::errstr";

	$update_info_sth->execute(
				$self->Type(), 
				$self->TimeCreated(),
				$time,
				$object->{_object_id},
				) || die "Could not execute $update_info_query: $DBI::errstr";
	$update_info_sth->finish();

	#update the data record			

	if($object_data && $object->{_data_dirty})
	{
		my $update_data_query = "UPDATE $data_table
					SET
						data = ?
					WHERE	
						id = ?";


		my $update_data_sth = $dbh->prepare_cached($update_data_query) || die "Could not prepare $update_data_query: $DBI::errstr";
		$update_data_sth->execute(
				$object->_FlattenData($object_data), 
				$object->{_object_id}) || die "Could not execute $update_data_query: $DBI::errstr";

		$update_data_sth->finish();
	}

	#update the index record
	if($object_index_data && $object->{_index_dirty})
	{
		my $index_values = [(map $object->{_index_data}->{$_}, @$index_fields)];
		my $update_index_query = "UPDATE $index_table
					SET
						".(join ',', map("$_ = ?", (@$index_fields)))."
					WHERE id = ?";
		my $update_index_sth = $dbh->prepare($update_index_query) || die "Could not prepare $update_index_query: $DBI::errstr";
		$update_index_sth->execute(
					@$index_values, 
					$object->{_object_id}) || die "Could not execute $update_index_query: $DBI::errstr";
		$update_index_sth->finish();
	}

	return $object->{_object_id};
}	

666;