package C4::SQLHelper;

# Copyright 2009 Biblibre SARL
#
# This file is part of Koha.
#
# Koha 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.
#
# Koha 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
# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
# Suite 330, Boston, MA  02111-1307 USA


use List::MoreUtils qw(first_value);
use C4::Context;
use C4::Dates qw(format_date_in_iso);
use C4::Debug;
use strict;
use warnings;
require Exporter;
use vars qw($VERSION @ISA @EXPORT_OK %EXPORT_TAGS);

BEGIN {
	# set the version for version checking
	$VERSION = 0.5;
	require Exporter;
	@ISA    = qw(Exporter);
@EXPORT_OK=qw(
	InsertInTable
	SearchInTable
	UpdateInTable
	GetPrimaryKey
);
	%EXPORT_TAGS = ( all =>[qw( InsertInTable SearchInTable UpdateInTable GetPrimaryKey)]
				);
}

my $tablename;
my $hash;

=head1 NAME

C4::SQLHelper - Perl Module containing convenience functions for SQL Handling

=head1 SYNOPSIS

use C4::SQLHelper;

=head1 DESCRIPTION

This module contains routines for adding, modifying and Searching Data in MysqlDB 

=head1 FUNCTIONS

=over 2

=back


=head2 SearchInTable

=over 4

  $hashref = &SearchInTable($tablename,$data, $orderby);

=back

$data may contain 
	- string
	- data_hashref : will be considered as an AND of all the data searched
	- data_array_ref on hashrefs : Will be considered as an OR of Datahasref elements

$orderby is a hashref with fieldnames as key and 0 or 1 as values (ASCENDING or DESCENDING order)

=cut

sub SearchInTable{
    my ($tablename,$filters,$orderby) = @_; 
    my $dbh      = C4::Context->dbh; 
    my $sql      = "SELECT * from $tablename"; 
    my $row; 
    my $sth; 
    my ($keys,$values)=_filter_fields($filters,$tablename, "search"); 
    if ($filters) { 
        $sql.= do { local $"=' AND '; 
                qq{ WHERE @$keys } 
               }; 
    } 
    if ($orderby){ 
        my @orders=map{ "$_".($$orderby{$_}? " DESC" : "") } keys %$orderby; 
        $sql.= do { local $"=', '; 
                qq{ ORDER BY @orders} 
               }; 
    } 
     
    $debug && warn $sql," ",join(",",@$values); 
    $sth = $dbh->prepare($sql); 
    $sth->execute(@$values); 
    my $results = $sth->fetchall_arrayref( {} ); 
    return $results;
}

=head2 InsertInTable

=over 4

  $data_id_in_table = &InsertInTable($tablename,$data_hashref);

=back

  Insert Data in table
  and returns the id of the row inserted
=cut

sub InsertInTable{
    my ($tablename,$data) = @_;
    my $dbh      = C4::Context->dbh;
    my ($keys,$values)=_filter_fields($data,$tablename);

    my $query = do { local $"=',';
    qq{
            INSERT $tablename
            SET  @$keys
        };
    };

	$debug && warn $query, join(",",@$values);
    my $sth = $dbh->prepare($query);
    $sth->execute( @$values);

	return $dbh->last_insert_id(undef, undef, $tablename, undef);
}

=head2 UpdateInTable

=over 4

  $status = &UpdateInTable($tablename,$data_hashref);

=back

  Update Data in table
  and returns the status of the operation
=cut

sub UpdateInTable{
    my ($tablename,$data) = @_;
	my $field_id=GetPrimaryKey($tablename);
    my $id=$$data{$field_id};
    my $dbh      = C4::Context->dbh;
    my ($keys,$values)=_filter_fields($data,$tablename,0);

    my $query = do { local $"=',';
    qq{
            UPDATE $tablename
            SET  @$keys
            WHERE  $field_id=?
        };
    };
	$debug && warn $query, join(",",@$values,$id);

    my $sth = $dbh->prepare($query);
    return $sth->execute( @$values,$id);

}

=head2 GetPrimaryKey

=over 4

  $primarykeyname = &GetPrimaryKey($tablename)

=back

	Get the Primary Key field name of the table
=cut

sub GetPrimaryKey($) {
	my $tablename=shift;
	my $hash_columns=_columns($tablename);
	return  first_value { $$hash_columns{$_}{'Key'} =~/PRI/}  keys %$hash_columns;
}

=head2 _get_columns

=over 4

_get_columns($tablename)

=back

Given a tablename 
Returns a hashref of all the fieldnames of the table
With 
	Key
	Type
	Default

=cut

sub _columns($) {
	my $tablename=shift;
	$debug && warn $tablename;
	my $dbh=C4::Context->dbh;
	my $sth=$dbh->prepare(qq{SHOW COLUMNS FROM $tablename });
	$sth->execute;
    return $sth->fetchall_hashref(qw(Field));
}

=head2 _filter_fields

=over 4

_filter_fields

=back

Given 
	- a tablename
	- a hashref of data 
	- an indicator on operation

Returns a ref of key array to use in SQL functions
and a ref to value array

=cut

sub _filter_fields{
	my ($data_to_filter,$tablename,$research)=@_;
	warn "$tablename research $research";
    my @keys; 
	my @values;
	my $columns= _columns($tablename);
	#Filter Primary Keys of table
    my $elements=join "|",grep {$$columns{$_}{'Key'} ne "PRI"} keys %$columns;
	if (ref($data_to_filter) eq "HASH"){
		foreach my $field (grep {/\b($elements)\b/} keys %$data_to_filter){
			## supposed to be a hash of simple values, hashes of arrays could be implemented
			$$data_to_filter{$field}=format_date_in_iso($$data_to_filter{$field}) if ($$columns{$field}{Type}=~/date/ && $$data_to_filter{$field} !~C4::Dates->regexp("iso"));
			my $strkeys= " $field = ? ";
			if ($field=~/code/ && $research){
				$strkeys="( $strkeys OR $field='' OR $field IS NULL) ";
			}
			push @values, $$data_to_filter{$field};
			push @keys, $strkeys;
		}
	} elsif (ref($data_to_filter) eq "ARRAY"){
		foreach my $element (@$data_to_filter){
			my (@localkeys,@localvalues)=_filter_fields($element);
			push @keys, join(' AND ',@localkeys);
			push @values, @localvalues;
		}
	} 
	else{
			my @operands=split / /,$data_to_filter;
			foreach my $operand (@operands){
				my @localvalues=($operand,"\%$operand\%") ;
				foreach my $field (keys %$columns){
					my $strkeys= " ( $field = ? OR $field LIKE ? )";
					if ($field=~/code/){
						$strkeys="( $strkeys OR $field='' OR $field IS NULL) ";
					}
					push @values, @localvalues;
					push @keys, $strkeys;
				}
			}
	}

	return (\@keys,\@values);
}

1;

