###############################################################################
#
# DBDrivers/FUSEKI.pm -- utility functions for providing (flat-file) DB like
#                        functionality using Apache Fuseki 
#
# A component of the Greenstone digital library software from the New Zealand
# Digital Library Project at the University of Waikato, New Zealand.
#
# Copyright (C) 2009-2010 DL Consulting Ltd.
# Copyright (C) 2009-2015 New Zealand Digital Library Project
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 675 Mass
# Ave, Cambridge, MA 02139, USA.
#
###############################################################################

package DBDrivers::FUSEKI;

# Pragma
use strict;

# Libraries
use util;
use parent 'DBDrivers::BaseDBDriver';

sub BEGIN
{
    if (!defined $ENV{'GSDLHOME'} || !defined $ENV{'GSDLOS'}) {
        die("Error! Environment must be prepared by sourcing setup.bash\n");
    }
    @DBDrivers::FUSEKI::ISA = ( 'DBDrivers::BaseDBDriver' );
}

sub new
{
    my $class = shift(@_);
    my $self = DBDrivers::BaseDBDriver->new(@_);
    $self->{'default_file_extension'} = 'ttl';

#    # The SQLite path we are using
#    $self->{'sqlite_executable'} = '';

#    # Support
#    $self->{'supports_set'} = 1;

    my $collection = $ENV{'GSDLCOLLECTION'};
    $self->{'collection'} = $collection;

    my $tmp_dir = &util::get_collectlevel_tmp_dir();
    $self->{'tmp_dir'} = $tmp_dir;
    
    my $url_prefix = &util::get_full_greenstone_url_prefix();

    #$self->{'is_part_of_col'} = "<$url_prefix/collection/$collection>";
    #$self->{'is_part_of_col_classify'} = "gsdlembedded:/classify";
    
    
    my $prefix_block = <<"EOT";
\@prefix dc:            <http://purl.org/dc/elements/1.1/> .

\@prefix gsdlembedded:  <http://greenstone.org/gsembedded#> .
\@prefix gsdlextracted: <http://greenstone.org/gsdlextracted#> .
\@prefix collection:          <$url_prefix/collection/$collection> .
\@prefix collection-classify: <$url_prefix/collection/$collection/classify/> .

EOT
    $self->{'prefix_block'} = $prefix_block;
    
    bless($self, $class);
    return $self;
}

# -----------------------------------------------------------------------------
#   FUSEKI IMPLEMENTATION
# -----------------------------------------------------------------------------

## Protected

## @function _getExecutable
#
sub _getExecutable
{
    my $self = shift(@_);

    print STDERR "DBDriver::FUSEKI::_getExecutable() not implemeted yet!!!!!\n";
    return undef;
    
#    my $sqlite3_exe = $self->{'sqlite_executable'};
#    if ($sqlite3_exe eq '') {
#	# Try a full path first - SQLite3 may live in the Greenstone bin
#	# directory
#	$sqlite3_exe = &util::filename_cat($ENV{'GSDLHOME'}, 'bin', $ENV{'GSDLOS'}, 'sqlite3' . &util::get_os_exe());
#	if (!-e "$sqlite3_exe") {
#	    print "Warning! Didn't find SQLite3 on expected path, \"$sqlite3_exe\", let's hope its on the system path.\n";
#	    $sqlite3_exe = 'sqlite3' . &util::get_os_exe();
#	}
#	$self->{'sqlite_executable'} = $sqlite3_exe;
#    }
#    return $sqlite3_exe;
}
## _getExecutable() => string ##


## @function _readInfoDBCmd(string, string) => string
#
sub _readInfoDBCmd
{
    my $self = shift(@_);
    my $infodb_file_path = shift(@_);
    my $sqlcmd = shift(@_);

    print STDERR "DBDriver::FUSEKI::_readInfoDBCmd() not implemeted yet!!!!!\n";
    
    my $result = "";
#    $self->debugPrint('("' . $infodb_file_path . '", "' . $sqlcmd . '")');
#    my $sqlite3_exe = $self->_getExecutable();
#    my $infodb_handle = undef;
#    my $cmd = "\"$sqlite3_exe\" \"$infodb_file_path\" \"$sqlcmd\"";
#    if (!open($infodb_handle, "$cmd |")) {
#	print STDERR "Unable to execute: $cmd\n";
#	print STDERR "$!\n";
#    }
#    else {
#	binmode($infodb_handle, ":utf8");
#	my $line;
#	while (defined($line=<$infodb_handle>)) {
#	    $result .= $line;
#	}
#	close($infodb_handle);
#    }

    return $result;
}
## _readInfoDBCmd(string, string) => string ##


## @function _turtleSafe(string) => string
#
sub _turtleSafe
{
    my $self = shift(@_);
    my $value = shift(@_);

	
    # Escape any single quotes in the value
    $value =~ s/\"/\\\"/g;
    return $value;
}
## _turtleSafe(string) => string ##


## @function _turtleIngest(string,string) => void
#
sub _turtleIngest    
{
    my ($self) = shift @_;
    my ($subj_key,$turtle_rec_str) = @_;

    my $ttl_output_file = "$subj_key.ttl";

    my $tmp_dir = $self->{'tmp_dir'};
    my $tmp_ttl_filename    = &FileUtils::filenameConcatenate($tmp_dir,$ttl_output_file);
    my $tmp_ttl_filename_cc = &util::makeFilenameJavaCygwinCompatible($tmp_ttl_filename);

    if (open(TTLOUT, ">$tmp_ttl_filename_cc")) {
	# encode the unicode aware characters in the string as utf8
	# before writing out the resulting bytes
	binmode(TTLOUT,":utf8");

	print TTLOUT $self->{'prefix_block'};
	
	print TTLOUT $turtle_rec_str;    
	close(TTLOUT);

	my $collection = $self->{'collection'};

#	my $cmd = "gs-triplestore-add $collection \"$tmp_ttl_filename_cc\"";
	my $cmd = "gs-triplestore-add3 $collection \"$tmp_ttl_filename_cc\"";
		
	my $status = system($cmd);
	if ($status != 0) {
	    print STDERR "Error: failed to run:\n  $cmd\n";
	}
	
#	print STDERR "**** temporarily supressing deletion of: $tmp_ttl_filename_cc\n";
	unlink $tmp_ttl_filename_cc;
    }
    else {
	print STDERR "*** Failed to generate $tmp_ttl_filename_cc: $!\n";
    }    
}
## _turtleIngest(string,string) => void
    

## Public


# Handled by BaseDBDriver
# sub get_infodb_file_path {}


## @function open_infodb_write_handle(string, string) => handle
#
sub open_infodb_write_handle
{
    my $self = shift(@_);
    my $infodb_file_path = shift(@_);
    my $opt_append = shift(@_);

    if (!defined $opt_append) {
	$opt_append = '';
    }

    print STDERR "DBDriver::FUSEKI::open_infodb_write_handle() not implemeted yet!!!!!\n";
 
    
    # $self->debugPrint('("' . $infodb_file_path . '","' . $opt_append . '")');

    # my $sqlite3_exe = $self->_getExecutable();
    my $infodb_handle = undef;

 
    # if(!open($infodb_handle, "| \"$sqlite3_exe\" \"$infodb_file_path\"$nul_device"))
    # {
    # 	print STDERR "Error! Failed to open pipe to \"" . $sqlite3_exe . "\" \"" . $infodb_file_path . "\"\n" . $! . "\n";
    # 	return undef;
    # }

    # binmode($infodb_handle,":utf8");

    # # If we are *not* appending, drop the table for a fresh start
    # if ($opt_append ne "append") {
    # 	print $infodb_handle "DROP TABLE IF EXISTS data;\n";
    # 	print $infodb_handle "DROP TABLE IF EXISTS document_metadata;\n";
    # }
    # print $infodb_handle "CREATE TABLE IF NOT EXISTS data (key TEXT PRIMARY KEY, value TEXT);\n";
    # print $infodb_handle "CREATE TABLE IF NOT EXISTS document_metadata (id INTEGER PRIMARY KEY, docOID TEXT, element TEXT, value TEXT);\n";
    # # These are crucial for efficiency when importing large amounts of data
    # print $infodb_handle "CREATE INDEX IF NOT EXISTS dmd ON document_metadata(docOID);\n";
    # print $infodb_handle "CREATE INDEX IF NOT EXISTS dme ON document_metadata(element);\n";
    # # This is very important for efficiency, otherwise each command will be act-
    # # ioned one at a time
    # print $infodb_handle "BEGIN TRANSACTION;\n";

    return $infodb_handle;
}
## open_infodb_write_handle(string, string) => handle ##


## @function close_infodb_write_handle(handle) => void
#
sub close_infodb_write_handle
{
    my $self = shift(@_);
    my $infodb_handle = shift(@_);

    print STDERR "DBDriver::FUSEKI::close_infodb_write_handle() not implemeted yet!!!!!\n";
    
#    $self->debugPrint('(<handle>)');
#    # Close the transaction we began when opening the file
#    print $infodb_handle "END TRANSACTION;\n";
#    close($infodb_handle);
}
## close_infodb_write_handle(handle) => void ##


## @function read_infodb_file(string, hashmap) => void
#
sub read_infodb_file
{
    my $self = shift(@_);
    my $infodb_file_path = shift(@_);
    my $infodb_map = shift(@_);

    print STDERR "DBDriver::FUSEKI::read_infodb_file() not implemeted yet!!!!!\n";
     
#    $self->debugPrint('("' . $infodb_file_path . '", <hashmap>)');
#    my $keys_str = $self->_readInfoDBCmd($infodb_file_path,"SELECT key FROM data ORDER BY key;");
#    my @keys = split(/\n/,$keys_str);
#    foreach my $k (@keys) {
#	my $k_safe = $self->_sqliteSafe($k);
#	my $select_val_cmd = "SELECT value FROM data WHERE key='$k_safe';";
#	my $val_str = $self->_readInfoDBCmd($infodb_file_path,$select_val_cmd);
#	$infodb_map->{$k} = $val_str;
#    }
}
## read_infodb_file(string, hashmap) => void ##


## @function read_infodb_keys(string, hashmap) => void
#
sub read_infodb_keys
{
    my $self = shift(@_);
    my $infodb_file_path = shift(@_);
    my $infodb_map = shift(@_);

    print STDERR "DBDriver::FUSEKI::read_infodb_keys() not implemeted yet!!!!!\n";
    
#    $self->debugPrint('("' . $infodb_file_path . '", <hashmap>)');
#    my $keys_str = $self->_readInfoDBCmd($infodb_file_path,"SELECT key FROM data;");
#    my @keys = split(/\n/,$keys_str);
#    foreach my $key (@keys)
#    {
#	$infodb_map->{$key} = 1;
#    }
}
## read_infodb_keys(string, hashmap) => void ##


## @function read_infodb_rawentry(string, string) => string
#
sub read_infodb_rawentry
{
    my $self = shift(@_);
    my $infodb_file_path = shift(@_);
    my $infodb_key = shift(@_);

    print STDERR "DBDriver::FUSEKI::read_infodb_raw() not implemeted yet!!!!!\n";
    return undef;
    
#    $self->debugPrint('("' . $infodb_file_path . '", "' . $infodb_key . '")');
#    my $key_safe = $self->_sqliteSafe($infodb_key);
#    my $select_val_cmd = "SELECT value FROM data WHERE key='$key_safe';";
#    my $val_str = $self->_readInfoDBCmd($infodb_file_path,$select_val_cmd);
#    return $val_str
}
## read_infodb_rawentry(string, string) => string ##


## @function read_infodb_entry(string, string) => hashmap
#
sub read_infodb_entry
{
    my $self = shift(@_);
    my $infodb_file_path = shift(@_);
    my $infodb_key = shift(@_);

    print STDERR "DBDriver::FUSEKI::read_infodb_entry() not implemeted yet!!!!!\n";
    return undef;
    
#    $self->debugPrint('("' . $infodb_file_path . '", "' . $infodb_key . '")');
#    my $val_str = $self->read_infodb_rawentry($infodb_file_path,$infodb_key);
#    my $rec_hash = $self->convert_infodb_string_to_hash($val_str);
#    return $rec_hash;
}
## read_infodb_entry(string, string) => hashmap ##



	
## @function write_infodb_entry(handle, string, hashmap) => void
#
sub write_infodb_entry
{
    my $self = shift(@_);
    my $infodb_handle = shift(@_);
    my $infodb_key = shift(@_);
    my $infodb_map = shift(@_);

    print STDERR "    Inserting triples for classifier key $infodb_key\n";
    
    # my $turtle_rec_str = ":classify/$infodb_key\n";
    my $turtle_rec_str = "collection-classify:$infodb_key\n";


    #my $is_part_of_col = $self->{'is_part_of_col'};
    #my $is_part_of_col_classify = $self->{'is_part_of_col_classify'};
    
    # $turtle_rec_str .= "    dc:PRelation.isPartOf $is_part_of_col ;\n";
    $turtle_rec_str .= "    dc:Relation.isPartOf collection: ;\n";
    $turtle_rec_str .= "    dc:Relation.isPartOf collection-classify: ;\n";
    
    foreach my $infodb_value_key (keys(%$infodb_map)) {
	my $turtle_pred = "$infodb_value_key";
	if ($turtle_pred =~ m/^[a-z]\./) {
	    $turtle_pred =~ s/\./:/ # just need to change the first one
	}
	else
	{
	    $turtle_pred = "gsdlembedded:" . $turtle_pred;
	}
	
	foreach my $infodb_value (@{$infodb_map->{$infodb_value_key}}) {
	    my $turtle_obj_safe = $self->_turtleSafe($infodb_value);
     	    $turtle_rec_str .= "    $turtle_pred \"$turtle_obj_safe\" ;\n";
     	}
    }
    $turtle_rec_str .= "  .\n";
    
    $self->_turtleIngest($infodb_key,$turtle_rec_str);
    

    # The following is done in SQLITE DBDriver
    # Consider doing the same for the Fuseki Triplestore???
    
    # # If this infodb entry is for a document, add all the interesting document
    # # metadata to the "document_metadata" table (for use by the dynamic
    # # classifiers)
    # if ($infodb_key !~ /\./ && $infodb_entry_value =~ /\<doctype\>doc\n/) {
    # 	print $infodb_handle "DELETE FROM document_metadata WHERE docOID='" . $safe_infodb_key . "';\n";
    # 	foreach my $infodb_value_key (keys(%$infodb_map)) {
    # 	    # We're not interested in most of the automatically added document metadata
    # 	    next if ($infodb_value_key eq "archivedir" ||
    # 		     $infodb_value_key eq "assocfilepath" ||
    # 		     $infodb_value_key eq "childtype" ||
    # 		     $infodb_value_key eq "contains" ||
    # 		     $infodb_value_key eq "docnum" ||
    # 		     $infodb_value_key eq "doctype" ||
    # 		     $infodb_value_key eq "Encoding" ||
    # 		     $infodb_value_key eq "FileSize" ||
    # 		     $infodb_value_key eq "hascover" ||
    # 		     $infodb_value_key eq "hastxt" ||
    # 		     $infodb_value_key eq "lastmodified" ||
    # 		     $infodb_value_key eq "metadataset" ||
    # 		     $infodb_value_key eq "thistype" ||
    # 		     $infodb_value_key =~ /^metadatafreq\-/ ||
    # 		     $infodb_value_key =~ /^metadatalist\-/);
    # 	    foreach my $infodb_value (@{$infodb_map->{$infodb_value_key}}) {
    # 		print $infodb_handle "INSERT INTO document_metadata (docOID, element, value) VALUES ('" . $safe_infodb_key . "', '" . $self->_sqliteSafe($infodb_value_key) . "', '" . $self->_sqliteSafe($infodb_value) . "');\n";
    # 	    }
    # 	}
    # }
}
## write_infodb_entry(handle, string, hashmap) => void ##


## @function write_infodb_rawentry(handle, string, string) => void
#
sub write_infodb_rawentry
{
    my $self = shift(@_);
    my $infodb_handle = shift(@_);
    my $infodb_key = shift(@_);
    my $infodb_val = shift(@_);

    print STDERR "DBDriver::FUSEKI::write_infodb_rawentry() not implemeted yet!!!!!\n";
    
#    $self->debugPrint('(<handle>, "' . $infodb_key . '", "' . $infodb_val . '")');
#    my $safe_infodb_key = $self->_sqliteSafe($infodb_key);
#    print $infodb_handle "INSERT OR REPLACE INTO data (key, value) VALUES ('" . $safe_infodb_key . "', '" . $self->_sqliteSafe($infodb_val) . "');\n";
}
## write_infodb_rawentry(handle, string, string) => void ##


## @function set_infodb_entry(string, string, hashmap) => integer
#
sub set_infodb_entry
{
    my $self = shift(@_);
    my $infodb_file_path = shift(@_);
    my $infodb_key = shift(@_);
    my $infodb_map = shift(@_);

    print STDERR "DBDriver::FUSEKI::set_infodb_entry() not implemeted yet!!!!!\n";
    
#    $self->debugPrint('("' . $infodb_file_path . '", "' . $infodb_key . '", <hashmap>)');
#    my $infodb_handle = $self->open_infodb_write_handle($infodb_file_path, "append");
#    if (!defined $infodb_handle) {
#	print STDERR "Error: Failed to open infodb write handle\n";
#	return -1;
#    }
#    else {
#	$self->write_infodb_entry($infodb_handle,$infodb_key,$infodb_map);
#	$self->close_infodb_write_handle($infodb_handle);
#    }

    # Not currently checking for errors on write to DB
    return 0;
}
## set_infodb_entry(string, string, hashmap) => integer ##


## @function delete_infodb_entry(handle, string) => void
#
sub delete_infodb_entry
{
    my $self = shift(@_);
    my $infodb_handle = shift(@_);
    my $infodb_key = shift(@_);

    print STDERR "DBDriver::FUSEKI::delete_infodb_entry() not implemeted yet!!!!!\n";
    
#    $self->debugPrint('(<handle>, "' . $infodb_key . '")');
#    # Delete the key from the "data" table
#    my $safe_infodb_key = $self->_sqliteSafe($infodb_key);
#    print $infodb_handle "DELETE FROM data WHERE key='" . $safe_infodb_key . "';\n";
#    # If this infodb entry is for a document, delete the "document_metadata"
#    # table entry also (for use by the dynamic classifiers)
#    if ($infodb_key !~ /\./) {
#	# Possible for there not to be a docOID matching this infodb_key
#	# (entries are only made when <doctype> == doc
#	# Attempt to delete it, and don't complain if one isn't found
#	print $infodb_handle "DELETE FROM document_metadata WHERE docOID='" . $safe_infodb_key . "';\n";
#    }
}
## delete_infodb_entry(handle, string) => void ##


1;
