package OpenSRF::Application::Persist;
use base qw/OpenSRF::Application/;
use OpenSRF::Application;

use OpenSRF::Utils::SettingsClient;
use OpenSRF::EX qw/:try/;
use OpenSRF::Utils::Logger;
use JSON;
use DBI;

use vars qw/$dbh $sc $log/;

sub initialize {
	$log = 'OpenSRF::Utils::Logger';

	$sc = OpenSRF::Utils::SettingsClient->new;

	my $dbfile = $sc->config_value( apps => persist => app_settings => 'dbfile');
	unless ($dbfile) {
		throw OpenSRF::EX::PANIC ("Can't find my dbfile for SQLite!");
	}

	my $init_dbh = DBI->connect("dbi:SQLite:dbname=$dbfile","","");
	$init_dbh->{AutoCommit} = 1;
	$init_dbh->{RaiseError} = 0;

	$init_dbh->do( <<"	SQL" );
		CREATE TABLE storage (
			id	INTEGER PRIMARY KEY,
			name_id	INTEGER,
			value	TEXT
		);
	SQL

	$init_dbh->do( <<"	SQL" );
		CREATE TABLE store_name (
			id	INTEGER PRIMARY KEY,
			name	TEXT UNIQUE
		);
	SQL

}

sub child_init {
	$sc = OpenSRF::Utils::SettingsClient->new;

	my $dbfile = $sc->config_value( apps => persist => app_settings => 'dbfile');
	unless ($dbfile) {
		throw OpenSRF::EX::PANIC ("Can't find my dbfile for SQLite!");
	}

	$dbh = DBI->connect("dbi:SQLite:dbname=$dbfile","","");
	$dbh->{AutoCommit} = 1;
	$dbh->{RaiseError} = 0;

}

sub create_store {
	my $self = shift;
	my $client = shift;

	my $name = shift || '';

	
	my $continue = 0;
	try {
		_get_name_id($name);

	} catch Error with { 
		$continue++;
	};

	throw OpenSRF::EX::WARN ("Duplicate key:  object name [$name] already exists!  " . $dbh->errstr)
		unless ($continue);

	my $sth = $dbh->prepare("INSERT INTO store_name (name) VALUES (?);");
	$sth->execute($name);
	$sth->finish;

	unless ($name) {
		my $last_id = $dbh->last_insert_id(undef, undef, 'store_name', 'id');
		$name = 'AUTOGENERATED!!'.$last_id;
		$dbh->do("UPDATE store_name SET name = '$name' WHERE id = '$last_id';");
	}

	return $name;
}
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.slot.create',
	method => 'create_store',
	argc => 2,
);



sub add_item {
	my $self = shift;
	my $client = shift;

	my $name = shift or do {
		throw OpenSRF::EX::WARN ("No name specified!");
	};
	my $value = shift || '';

	my $name_id = _get_name_id($name);
	
	if ($self->api_name =~ /object/) {
		$dbh->do('DELETE FROM storage WHERE name_id = ?;', {}, $name_id);
	}

	$dbh->do('INSERT INTO storage (name_id,value) VALUES (?,?);', {}, $name_id, JSON->perl2JSON($value));

	return 0 if ($dbh->err);
	return $name;
}
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.object.set',
	method => 'add_item',
	argc => 2,
);
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.queue.push',
	method => 'add_item',
	argc => 2,
);
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.stack.push',
	method => 'add_item',
	argc => 2,
);

sub _get_name_id {
	my $name = shift or do {
		throw OpenSRF::EX::WARN ("No queue name specified!");
	};


	my $name_id = $dbh->selectrow_arrayref("SELECT id FROM store_name WHERE name = ?;", {}, $name);

	if (!ref($name_id) || !defined($name_id->[0])) {
		throw OpenSRF::EX::WARN ("Object name [$name] does not exist!");
	}

	return $name_id->[0];
}

sub destroy_store {
	my $self = shift;
	my $client = shift;

	my $name = shift;

	my $name_id = _get_name_id($name);

	$dbh->do("DELETE FROM storage WHERE name_id = ?;", {}, $name_id);
	$dbh->do("DELETE FROM store_name WHERE id = ?;", {}, $name_id);

}
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.slot.destroy',
	method => 'destroy_store',
	argc => 1,
);

sub _flush_by_name {
	my $name = shift;
	if ($name =~ /^AUTOGENERATED!!/) {
		my $name_id = _get_name_id($name);
		my $count = $dbh->selectrow_arrayref("SELECT COUNT(*) FROM storage WHERE name_id = ?;", {}, $name_id);
		if (!ref($count) || $$count[0] == 0) {
			$dbh->do("DELETE FROM store_name WHERE name = ?;", {}, $name);
		}
	}
}
	
sub pop_queue {
	my $self = shift;
	my $client = shift;

	my $name = shift or do {
		throw OpenSRF::EX::WARN ("No queue name specified!");
	};
	my $name_id = _get_name_id($name);

	my $value = $dbh->selectrow_arrayref('SELECT id, value FROM storage WHERE name_id = ? ORDER BY id ASC LIMIT 1;', {}, $name_id);
	$dbh->do('DELETE FROM storage WHERE id = ?;',{}, $value->[0]) unless ($self->api_name =~ /peek$/);

	_flush_by_name($name);

	return JSON->JSON2perl( $value->[1] );
}
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.queue.peek',
	method => 'pop_queue',
	argc => 1,
);
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.queue.pop',
	method => 'pop_queue',
	argc => 1,
);


sub store_size {
	my $self = shift;
	my $client = shift;

	my $name = shift or do {
		throw OpenSRF::EX::WARN ("No queue name specified!");
	};
	my $name_id = _get_name_id($name);

	my $value = $dbh->selectrow_arrayref('SELECT SUM(LENGTH(value)) FROM storage WHERE name_id = ?;', {}, $name_id);

	return JSON->JSON2perl( $value->[0] );
}
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.queue.size',
	method => 'shift_stack',
	argc => 1,
);
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.stack.size',
	method => 'shift_stack',
	argc => 1,
);
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.object.size',
	method => 'shift_stack',
	argc => 1,
);

sub store_depth {
	my $self = shift;
	my $client = shift;

	my $name = shift or do {
		throw OpenSRF::EX::WARN ("No queue name specified!");
	};
	my $name_id = _get_name_id($name);

	my $value = $dbh->selectrow_arrayref('SELECT COUNT(*) FROM storage WHERE name_id = ?;', {}, $name_id);

	return JSON->JSON2perl( $value->[0] );
}
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.queue.length',
	method => 'shift_stack',
	argc => 1,
);
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.stack.depth',
	method => 'shift_stack',
	argc => 1,
);

sub shift_stack {
	my $self = shift;
	my $client = shift;

	my $name = shift or do {
		throw OpenSRF::EX::WARN ("No queue name specified!");
	};
	my $name_id = _get_name_id($name);

	my $value = $dbh->selectrow_arrayref('SELECT id, value FROM storage WHERE name_id = ? ORDER BY id DESC LIMIT 1;', {}, $name_id);
	$dbh->do('DELETE FROM storage WHERE id = ?;',{}, $value->[0]) unless ($self->api_name =~ /peek$/);

	_flush_by_name($name);

	return JSON->JSON2perl( $value->[1] );
}
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.stack.peek',
	method => 'shift_stack',
	argc => 1,
);
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.stack.pop',
	method => 'shift_stack',
	argc => 1,
);

sub get_object {
	my $self = shift;
	my $client = shift;

	my $name = shift or do {
		throw OpenSRF::EX::WARN ("No object name specified!");
	};

	my $name_id = _get_name_id($name);

	my $value = $dbh->selectrow_arrayref('SELECT name_id, value FROM storage WHERE name_id = ? ORDER BY id DESC LIMIT 1;', {}, $name_id);
	$dbh->do('DELETE FROM storage WHERE name_id = ?',{}, $value->[0]) unless ($self->api_name =~ /peek$/);

	_flush_by_name($name);

	return JSON->JSON2perl( $value->[1] );
}
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.object.peek',
	method => 'shift_stack',
	argc => 1,
);
__PACKAGE__->register_method(
	api_name => 'opensrf.persist.object.get',
	method => 'shift_stack',
	argc => 1,
);

1;
