###########################################################################
#
# dbutil.pm -- gateway to utilities for reading/writing to different databases
# Copyright (C) 2008  DL Consulting Ltd
#
# A component of the Greenstone digital library software
# from the New Zealand Digital Library Project at the 
# University of Waikato, New Zealand.
#
# 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 dbutil;

use strict;


sub open_infodb_write_handle
{
  my $infodb_type = shift(@_);
  my $infodb_file_path = shift(@_);
  
  if ($infodb_type eq "sqlite") 
  {
    require dbutil::sqlite;
    return &dbutil::sqlite::open_infodb_write_handle($infodb_file_path, @_);
  } 
  elsif ($infodb_type eq "gdbm-txtgz") 
  {
    require dbutil::gdbmtxtgz;
    return &dbutil::gdbmtxtgz::open_infodb_write_handle($infodb_file_path, @_);
  } 
  elsif ($infodb_type eq "jdbm") 
  {
    require dbutil::jdbm;
    return &dbutil::jdbm::open_infodb_write_handle($infodb_file_path, @_);
  } 
  elsif ($infodb_type eq "mssql") 
  {
    require dbutil::mssql;
    return &dbutil::mssql::open_infodb_write_handle($infodb_file_path, @_);
  } 

  # Use GDBM if the infodb type is empty or not one of the values above
  require dbutil::gdbm;
  return &dbutil::gdbm::open_infodb_write_handle($infodb_file_path, @_);
}


sub close_infodb_write_handle
{
  my $infodb_type = shift(@_);
  my $infodb_handle = shift(@_);

  if ($infodb_type eq "sqlite")
  {
    require dbutil::sqlite;
    return &dbutil::sqlite::close_infodb_write_handle($infodb_handle, @_);
  }
  elsif ($infodb_type eq "gdbm-txtgz")
  {
    require dbutil::gdbmtxtgz;
    return &dbutil::gdbmtxtgz::close_infodb_write_handle($infodb_handle, @_);
  }
  elsif ($infodb_type eq "jdbm")
  {
    require dbutil::jdbm;
    return &dbutil::jdbm::close_infodb_write_handle($infodb_handle, @_);
  }
  elsif ($infodb_type eq "mssql")
  {
    require dbutil::mssql;
    return &dbutil::mssql::close_infodb_write_handle($infodb_handle, @_);
  }
    
  # Use GDBM if the infodb type is empty or not one of the values above
  require dbutil::gdbm;
  return &dbutil::gdbm::close_infodb_write_handle($infodb_handle, @_);
}


sub get_default_infodb_type
{
  # The default is GDBM so everything works the same for existing collections
  # To use something else, specify the "infodbtype" in the collection's collect.cfg file
  return "gdbm";
}


sub get_infodb_file_path
{
  my $infodb_type = shift(@_);
  my $collection_name = shift(@_);
  my $infodb_directory_path = shift(@_);

  if ($infodb_type eq "sqlite")
  {
    require dbutil::sqlite;
    return &dbutil::sqlite::get_infodb_file_path($collection_name, $infodb_directory_path, @_);
  }
  elsif ($infodb_type eq "gdbm-txtgz")
  {
    require dbutil::gdbmtxtgz;
    return &dbutil::gdbmtxtgz::get_infodb_file_path($collection_name, $infodb_directory_path, @_);
  }
  elsif ($infodb_type eq "jdbm")
  {
    require dbutil::jdbm;
    return &dbutil::jdbm::get_infodb_file_path($collection_name, $infodb_directory_path, @_);
  }
  elsif ($infodb_type eq "mssql")
  {
    #==================================================================================================#
    # Updated by Jeffrey (2008/08/25 Monday)
    # After look into the run-time code, it seems we should still create a database file.
    # Since the run-time code is always try to read a database file, the easiest way here is not
    # to change the whole structure, but to give whatever the system is looking for. 
    #==================================================================================================#
    # Added by Jeffrey (2008/08/15 Friday)
    # No file path required for MS SQL, it is a server-client connection.
    # At the moment the information is hard coded in dbutil::mssql::open_infodb_write_handle
    # the this might need some tidy up sometime.
    #==================================================================================================#
    require dbutil::mssql;
    return &dbutil::mssql::get_infodb_file_path($collection_name, $infodb_directory_path, @_); 
  }
  
  # Use GDBM if the infodb type is empty or not one of the values above
  require dbutil::gdbm;
  return &dbutil::gdbm::get_infodb_file_path($collection_name, $infodb_directory_path, @_);
}


sub read_infodb_file
{
  my $infodb_type = shift(@_);
  my $infodb_file_path = shift(@_);
  my $infodb_map = shift(@_);

  if ($infodb_type eq "sqlite")
  {
    require dbutil::sqlite;
    return &dbutil::sqlite::read_infodb_file($infodb_file_path, $infodb_map, @_);
  }
  elsif ($infodb_type eq "gdbm-txtgz")
  {
    require dbutil::gdbmtxtgz;
    return &dbutil::gdbmtxtgz::read_infodb_file($infodb_file_path, $infodb_map, @_);
  }
  elsif ($infodb_type eq "jdbm")
  {
    require dbutil::jdbm;
    return &dbutil::jdbm::read_infodb_file($infodb_file_path, $infodb_map, @_);
  }
  elsif ($infodb_type eq "mssql")
  {
    require dbutil::mssql;
    return &dbutil::mssql::read_infodb_file($infodb_file_path, $infodb_map, @_);
  }
  
  # Use GDBM if the infodb type is empty or not one of the values above
  require dbutil::gdbm;
  return &dbutil::gdbm::read_infodb_file($infodb_file_path, $infodb_map, @_);
}


sub read_infodb_entry
{
  my $infodb_type = shift(@_);
  my $infodb_file_path = shift(@_);
  my $infodb_key = shift(@_);

  # !! TEMPORARY: Slow and naive implementation that just reads the entire file and picks out the one value
  # !! This will soon be replaced with database-specific versions that will use dbget etc.
  my $infodb_map = {};
  &read_infodb_file($infodb_type, $infodb_file_path, $infodb_map);
  return $infodb_map->{$infodb_key};
}


sub write_infodb_entry
{
  my $infodb_type = shift(@_);
  my $infodb_handle = shift(@_);
  my $infodb_key = shift(@_);
  my $infodb_map = shift(@_);

  if ($infodb_type eq "sqlite")
  {
    require dbutil::sqlite;
    return &dbutil::sqlite::write_infodb_entry($infodb_handle, $infodb_key, $infodb_map, @_);
  }
  elsif ($infodb_type eq "gdbm-txtgz")
  {
    require dbutil::gdbmtxtgz;
    return &dbutil::gdbmtxtgz::write_infodb_entry($infodb_handle, $infodb_key, $infodb_map, @_);
  }
  elsif ($infodb_type eq "jdbm")
  {
    require dbutil::jdbm;
    return &dbutil::jdbm::write_infodb_entry($infodb_handle, $infodb_key, $infodb_map, @_);
  }
  elsif ($infodb_type eq "mssql")
  {
    require dbutil::mssql;
    return &dbutil::mssql::write_infodb_entry($infodb_handle, $infodb_key, $infodb_map, @_);
  }

  # Use GDBM if the infodb type is empty or not one of the values above
  require dbutil::gdbm;
  return &dbutil::gdbm::write_infodb_entry($infodb_handle, $infodb_key, $infodb_map, @_);
}


sub write_infodb_rawentry
{
  my $infodb_type = shift(@_);
  my $infodb_handle = shift(@_);
  my $infodb_key = shift(@_);
  my $infodb_val = shift(@_);

  if ($infodb_type eq "sqlite")
  {
    require dbutil::sqlite;
    return &dbutil::sqlite::write_infodb_rawentry($infodb_handle, $infodb_key, $infodb_val, @_);
  }
  elsif ($infodb_type eq "gdbm-txtgz")
  {
    require dbutil::gdbmtxtgz;
    return &dbutil::gdbmtxtgz::write_infodb_rawentry($infodb_handle, $infodb_key, $infodb_val, @_);
  }
  elsif ($infodb_type eq "jdbm")
  {
    require dbutil::jdbm;
    return &dbutil::jdbm::write_infodb_rawentry($infodb_handle, $infodb_key, $infodb_val, @_);
  }
  elsif ($infodb_type eq "mssql")
  {
    require dbutil::mssql;
    return &dbutil::mssql::write_infodb_rawentry($infodb_handle, $infodb_key, $infodb_val, @_);
  }

  # Use GDBM if the infodb type is empty or not one of the values above
  require dbutil::gdbm;
  return &dbutil::gdbm::write_infodb_rawentry($infodb_handle, $infodb_key, $infodb_val, @_);
}



sub delete_infodb_entry
{
  my $infodb_type = shift(@_);
  my $infodb_handle = shift(@_);
  my $infodb_key = shift(@_);

  if ($infodb_type eq "sqlite")
  {
    require dbutil::sqlite;
    return &dbutil::sqlite::delete_infodb_entry($infodb_handle, $infodb_key, @_);
  }
  elsif ($infodb_type eq "gdbm-txtgz")
  {
    require dbutil::gdbmtxtgz;
    return &dbutil::gdbmtxtgz::delete_infodb_entry($infodb_handle, $infodb_key, @_);
  }
  elsif ($infodb_type eq "jdbm")
  {
    require dbutil::jdbm;
    return &dbutil::jdbm::delete_infodb_entry($infodb_handle, $infodb_key, @_);
  }
  elsif ($infodb_type eq "mssql")
  {
    require dbutil::mssql;
    return &dbutil::mssql::delete_infodb_entry($infodb_handle, $infodb_key, @_);
  }

  # Use GDBM if the infodb type is empty or not one of the values above
  require dbutil::gdbm;
  return &dbutil::gdbm::delete_infodb_entry($infodb_handle, $infodb_key, @_);
}


# ---- GENERAL FUNCTIONS --------

sub convert_infodb_hash_to_string
{
  my $infodb_map = shift(@_);

  my $infodb_entry_value = "";
  foreach my $infodb_value_key (keys(%$infodb_map))
  {
    foreach my $infodb_value (@{$infodb_map->{$infodb_value_key}})
    {
      $infodb_entry_value .= "<$infodb_value_key>" . $infodb_value . "\n";
    }
  }

  return $infodb_entry_value;
}


sub convert_infodb_string_to_hash
{
  my $infodb_entry_value = shift(@_);
  my $infodb_map = ();
  
  if (!defined $infodb_entry_value) {
	print STDERR "Warning: No value to convert into a infodb hashtable\n";
  }
  else {
    while ($infodb_entry_value =~ /^<(.*?)>(.*)$/mg)
    {
        my $infodb_value_key = $1;
		my $infodb_value = $2;

		if (!defined($infodb_map->{$infodb_value_key}))
		{
		  $infodb_map->{$infodb_value_key} = [ $infodb_value ];
		}
		else
		{
		  push(@{$infodb_map->{$infodb_value_key}}, $infodb_value);
		}
	}
  }

  return $infodb_map;
}


1;
