package oEdtk::DBAdmin;

use DBI;
use oEdtk::Config	qw(config_read);
use POSIX			qw(strftime);
use Text::CSV;
use strict;
use warnings;
use oEdtk::logger	qw(logger $LOGGERLEVEL $WARN2LOGGER);
#use Data::Dumper qw(Dumper);

use Exporter;

our $VERSION		= 1.5099;
our @ISA			= qw(Exporter);
our @EXPORT_OK		= qw(
				insert_tData
				csv_import
				create_lot_sequence
				create_SCHEMA
				create_table_ACQUIT
				create_table_DATAGROUPS
				create_table_FILIERES
				create_table_INDEX
				create_table_LOTS
				create_table_PARA
				create_table_REFIDDOC
				create_table_SUPPORTS
				create_table_TRACKING
				db_connect
				db_backup_agent
				historicize_table
				copy_table
				move_table
				@INDEX_COLS
				@TRACKER_COLS
				);


sub move_table (@){
	&logger (4, "method oEdtk::DBAdmin::move_table is deprecated you should use oEdtk::DBAdmin::copy_table");

	copy_table (@_);
1;
}


sub copy_table ($$$;$){ 
	my ($dbh, $table_source, $table_cible, $create_option) = @_;
	$create_option ||= "";

	# check if source is empty
	my $sql_check_source = "SELECT SIGN(COUNT(*)) FROM ".$table_source;
	my $sth = $dbh->prepare($sql_check_source);
	$sth->execute();
	my $result = $sth->fetchrow_array;
	unless ($result){
		&logger (4, "Source $table_source is empty, copy aborted");
		return 1;
	}

	# preparing data copy from source to cible
	&logger (4, "Data from $table_source will be copyed into $table_cible");

	if ($create_option =~/-create/i) {
		my $sql_create	= "CREATE TABLE ".$table_cible." AS SELECT * FROM ".$table_source;
		# en cas d'erreur, DIE pour protéger toute autre opération sur les bases (db_backup_agent, ...)
		$dbh->do($sql_create, undef, ) or die $dbh->errstr;	

	} else {
		my $sql_insert	= "INSERT INTO  ".$table_cible." SELECT * FROM ".$table_source;
		# en cas d'erreur, DIE pour protéger toute autre opération sur les bases (db_backup_agent, ...)
		$dbh->do($sql_insert, undef, ) or die $dbh->errstr;	
	}
	
	&logger (4, "Insert done into $table_cible");
1;
}

sub insert_tData {
# Exemple d'appel
#		my $ret = insert_tData(
#			dbh => $dbh, 
#			table => $cfg->{'EDTK_DBI_TRACKING'}, 
#			tCols => \@cols,
#			tData => \@tData
#		);
	
	my (%p) = @_;

#	print @{$p{'tData'}} ."\n";

#	print Dumper($p{'table'});
#	print Dumper($p{'tData'});
	

	my $sql = "INSERT INTO " . ${p{'table'}} . " (" . join(',', @{$p{'tCols'}})
			. ") VALUES (" . join(',', ('?') x @{$p{'tCols'}}) . ")";

	my $sth = ${p{'dbh'}}->prepare_cached($sql);
	$sth->execute(@{$p{'tData'}});

	1;
}


sub _load_csv_mysql_local_infile ($$$$) {
	my ($dbh, $tablename, $fi, $params) = @_;

	# on récupère la ligne d'en-tete qui contient les champs et l'ordre d'insertion des champs
	open(my $fh, '<', $fi) or die "ERROR: Cannot open index file \"$fi\": $!\n";
	my $line = <$fh>;
	chomp ($line);
	$line =~s/\"+//g;
	close($fh);

	# https://www.perlmonks.org/?node_id=1058110  => insert data inline

	# https://metacpan.org/pod/DBD::mysql
	# https://tecfa.unige.ch/guides/mysql/man/manuel_LOAD_DATA.html
	# https://dev.mysql.com/doc/refman/8.0/en/load-data.html
	# https://stackoverflow.com/questions/59993844/error-loading-local-data-is-disabled-this-must-be-enabled-on-both-the-client
	# https://dzone.com/articles/mysql-57-utf8mb4-and-the-load-data-infile
	# => mysql> set global local_infile=true; + dans la config DSN ";mysql_local_infile=true"

	my $sql = "LOAD DATA LOCAL INFILE ? INTO TABLE $tablename CHARACTER SET latin1 "
				. " FIELDS TERMINATED BY '" . $params->{'sep_char'} . "' ENCLOSED BY '" . $params->{'quote_char'} . "' "
				. " LINES TERMINATED BY '" . '\n' . "'" 
				. "($line)";

	##  OPTIONALLY ENCLOSED BY '"'
	##  REPLACE 

	&logger (7,"_load_csv_mysql_local_infile trying : $sql");

	my $count = $dbh->do($sql,undef,$fi) // 0;
	&logger (4, "$count lines inserted into $tablename");

	# on dégage la ligne d'en-tête qu'on aurait pu ignorer...
	my $sth = $dbh->prepare("DELETE FROM $tablename WHERE ED_IDLDOC='ED_IDLDOC'");
	print $sth->execute();

	1;
}

sub csv_import ($$$;$){
	# insertion d'un fichier csv dans une table
	# csv_import($dbh, "EDTK_ACQ", $ARGV[0], 
	#		{sep_char => ',' ,						# ',' is default value
	#		quote_char => '"',						# '"' is default value
	#		header => 'ED_SEQLOT,ED_LOTNAME,...',	# default value, no header, read header from csv file
	#		ignore_first_line => 1,					# ignore first line : option, could be used in consonction with header du change it
	# 		mode => 'merge';						# 'insert' is default value
	#		});	
	
###### VERSION ORACLE DU MERGE, DIFFÉRENTE DE CELLE DE POSTGRESQL
	#MERGE INTO table_name USING table_reference ON (condition)
	#  WHEN MATCHED THEN
	#  UPDATE SET column1 = value1 [, column2 = value2 ...]
	#  WHEN NOT MATCHED THEN
	#  INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...

	#MERGE INTO Table1 T1
	#  USING (SELECT Id, Meschamps FROM Table2) T2
	#    ON ( T1.Id = T2.Id ) -- Condition de correspondance
	#WHEN MATCHED THEN -- Si Vraie
	#  UPDATE SET T1.Meschamps = T2.Meschamps
	#WHEN NOT MATCHED THEN -- Si faux
	#  INSERT (T1.ID, T1.MesChamps) VALUES ( T2.ID, T2.MesChamps);

###### VERSION POSTGRESQL
	#MERGE INTO table [[AS] alias]
	#USING [table-ref | query]
	#ON join-condition
	#[WHEN MATCHED [AND condition] THEN MergeUpdate | DELETE | DO NOTHING | RAISE ERROR]
	#[WHEN NOT MATCHED [AND condition] THEN MergeInsert | DO NOTHING | RAISE ERROR]
	#MergeUpdate is
	#
	#UPDATE SET { column = { expression | DEFAULT } |
	#( column [, ...] ) = ( { expression | DEFAULT } [, ...] ) }
	#[, ...]
	#(yes, there is no WHERE clause here)
	#MergeInsert is
	#INSERT [ ( column [, ...] ) ]
	#{ DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] )
	#[, ...]} 

	my ($dbh, $table, $in, $params) = @_;
	$params->{'mode'}		= $params->{'mode'} || "insert";
	$params->{'sep_char'} 	= $params->{'sep_char'} || ",";
	$params->{'quote_char'}	= $params->{'quote_char'}||'"' ;

	if ( $dbh->{'Name'} =~ m/mysql_local_infile\=true/i) {
		&logger (7, "'mysql_local_infile=true' detected in DSN by csv_import, trying local infile if SHOW GLOBAL VARIABLES LIKE 'local_infile' apply");
		return _load_csv_mysql_local_infile($dbh, $table, $in, $params);
	}
	
	open(my $fh, '<', $in) or die "ERROR: Cannot open index file \"$in\": $!\n";
	my $csv = Text::CSV->new({ binary => 1, sep_char => $params->{'sep_char'}, 
							quote_char => $params->{'quote_char'}});

	my $lines_inserted;
	my $line;
	if (defined $params->{'header'}){
		$line = $params->{'header'};
	} else {
		$line = <$fh>;
	}
	$csv->parse($line);
	my @cols = $csv->fields();

	if (defined $params->{'ignore_first_line'}){
		$line = <$fh>;
	}

	while (<$fh>) {
		$lines_inserted++;
		$csv->parse($_);
		my @data = $csv->fields();

		# s'assurer qu'on insère pas des valeurs null (contraintes ???) ou pas ?
		for (my $i=0 ; $i<=$#data ; $i++ ){
			$data[$i]=$data[$i] || "";
		}
		my ($sql, $seqlot);
		
		if 	($params->{'mode'}=~/merge/i) {
			$sql = "SELECT " . $cols[0] . " FROM " . $table 
				. " WHERE " . $cols[0] . " =?  ";
			&logger (8, "Select into $table : $sql");

			my $sth = $dbh->prepare_cached($sql);
			$sth->execute($data[0]);

			$seqlot = $sth->fetchrow_hashref();
		}
		
		if (defined $seqlot->{'ED_SEQLOT'}) {
			$sql = "UPDATE " . $table . " SET " . join ('=? , ', @cols) . "=? "
				. " WHERE " . $cols[0] . " =?  ";
			&logger (8, "Update into $table : $sql");

			my $sth = $dbh->prepare_cached($sql);
			$sth->execute(@data, $data[0]);
		
		} else {
			$sql = "INSERT INTO " . $table . " (" . join(',', @cols)
				. ") VALUES (" . join(',', ('?') x @cols) . ")";
			&logger (8, "Insert into $table : $sql");

			my $sth = $dbh->prepare_cached($sql);
			$sth->execute(@data);
		}

	}
	close($fh);
	return $lines_inserted;
}


sub _db_connect1 {
	my ($cfg, $dsnvar, $dbargs) = @_;
	my $dbh;
	my $dsn = $cfg->{$dsnvar};

	&logger (5, "Connecting to DSN $dsn, $dsnvar");

	# gestion de la connexion dans une boucle temporisée, pour effectuer 3 tentatives de connexion avec incrément de pause
	for (my $i=0;$i<3;$i++){
		sleep ($cfg->{EDTK_WAITRUN}*$i);
		eval {
			 $dbh=DBI->connect($dsn, $cfg->{"${dsnvar}_USER"}, $cfg->{"${dsnvar}_PASS"}, $dbargs); ## xxxx
		};

		if ($@){
			# en cas d'incident de connexion, on ne dit rien, on essaie encore
			&logger (4, "DBI connection missmatch to $dsnvar, we try 3 times");
			&logger (4, "Error message was : $@");

		} else {
			# si ça semble bon on sort
			$i=4;
		}
	}
	return $dbh;	
}


sub db_connect {
	my ($cfg, $dsnvar, $dbargs) = @_;

	# This avoids problems with PostgreSQL where in some cases, the column
	# names are lowercase instead of uppercase as we assume everywhere.
	$dbargs->{'FetchHashKeyName'} = 'NAME_uc';

	# Connect to the database.
	my $dbh = _db_connect1($cfg, $dsnvar, $dbargs);

    # If we could not connect to the database server, try
	# to connect to the backup database server if there is one.
	if (!defined $dbh) {
		if (defined $cfg->{"${dsnvar}_BAK"}) { # il faudrait ajouter le paramétrage dans la base de backup (procédure de création de cette base)
			&logger (4, "Could not connect to main database server: " . $DBI::errstr || "undefined");
			$dbh = _db_connect1($cfg, "${dsnvar}_BAK", $dbargs);
			
			if (!defined $dbh) {
				die "ERROR: Could not connect to backup database server : " . $DBI::errstr || "undefined" ."\n";
			}
		} else {
			die "ERROR: Could not connect to database server : " . $DBI::errstr || "undefined" ."\n";
		}
	}
	return $dbh;
}


our @TRACKER_COLS = (
	['ED_TSTAMP',	'VARCHAR2(14) NOT NULL'],	# Timestamp of event
	['ED_SNGL_ID',	'VARCHAR2(25) NOT NULL'],	#xx ED_IDLDOC Single ID : format YWWWDHHMMSSPPPP.U (compuset se limite ? 16 digits : 15 entiers, 1 decimal)
	['ED_SEQ',		'INTEGER NOT NULL'],		# Sequence
	['ED_USER',		'VARCHAR2(10) NOT NULL'],	# user for the Job or request 

	['ED_APP',		'VARCHAR2(20) NOT NULL'],	#xx ED_REFIDDOC Application name
	['ED_CORP',		'VARCHAR2(8) NOT NULL'],	# Entity related 
	['ED_ACCOUNT',	'VARCHAR2(8)'],				# Administrative account 
	['ED_MOD_ED',	'CHAR'],					# Editing mode (Undef, Batch, Tp, Web, Mail, probinG)
	['ED_JOB_EVT',	'CHAR'],					# Level of the event (Job (default), Spool, Document, Line, Warning, Error, Halt (critic), Reject)
	['ED_OBJ_TYP',	'VARCHAR2(3)'],				# To define the object concerned
	['ED_OBJ_COUNT','INTEGER'],					# Number of objects attached to the event
	['ED_CHILD_TYP','VARCHAR2(3)'],				# To define the object concerned
	['ED_CHILD_ID',	'VARCHAR2(32)'],			#xx ED_IDLDOC Single ID : format YWWWDHHMMSSPPPP.U (compuset se limite ? 16 digits : 15 entiers, 1 decimal)
	['ED_PARENT_ID','VARCHAR2(32)'],			#xx ED_IDLDOC Single ID : format YWWWDHHMMSSPPPP.U (compuset se limite ? 16 digits : 15 entiers, 1 decimal)

	['ED_HOST',		'VARCHAR2(32)'],			# Hostname for input stream of this document (max length for smtp is 31, could be 255...)
	['ED_SOURCE',	'VARCHAR2(128)'],			# Input stream of this document
	['ED_MESSAGE',	'VARCHAR2(256)']			# Treatment message

);

sub create_table_TRACKING {
	my ($dbh, $table, $maxkeys) = @_;


	my $sql = "CREATE TABLE if not exists $table ("
			. join(', ', map {"$$_[0] $$_[1]"} @TRACKER_COLS) . ", ";


#	my $sql = "CREATE TABLE if not exists $table";
#	$sql .= "( ED_TSTAMP    VARCHAR2(14) NOT NULL";	# Timestamp of event
#	$sql .= ", ED_SEQ       INTEGER NOT NULL";		# Sequence
#	$sql .= ", ED_SNGL_ID   VARCHAR2(25) NOT NULL";	#xx ED_IDLDOC Single ID : format YWWWDHHMMSSPPPP.U (compuset se limite ? 16 digits : 15 entiers, 1 decimal)
#	$sql .= ", ED_APP       VARCHAR2(20) NOT NULL";	#xx ED_REFIDDOC Application name
#	$sql .= ", ED_USER      VARCHAR2(10) NOT NULL";	# user for the Job or request 
#	$sql .= ", ED_CORP      VARCHAR2(8) NOT NULL";	# Entity related 
#	$sql .= ", ED_ACCOUNT   VARCHAR2(8) ";			# Administrative account 
#	$sql .= ", ED_MOD_ED    CHAR";					# Editing mode (Undef, Batch, Tp, Web, Mail, probinG)
#	$sql .= ", ED_JOB_EVT   CHAR";					# Level of the event (Job (default), Spool, Document, Line, Warning, Error, Halt (critic), Reject)
#	$sql .= ", ED_OBJ_TYP   VARCHAR2(3)";				# To define the object concerned
#	$sql .= ", ED_OBJ_COUNT INTEGER";				# Number of objects attached to the event
#	$sql .= ", ED_CHILD_TYP VARCHAR2(3)";			# To define the object concerned
#	$sql .= ", ED_CHILD_ID  VARCHAR2(25) ";			#xx ED_IDLDOC Single ID : format YWWWDHHMMSSPPPP.U (compuset se limite ? 16 digits : 15 entiers, 1 decimal)
#	$sql .= ", ED_PARENT_ID VARCHAR2(25) ";			#xx ED_IDLDOC Single ID : format YWWWDHHMMSSPPPP.U (compuset se limite ? 16 digits : 15 entiers, 1 decimal)
#
#	$sql .= ", ED_SOURCE  VARCHAR2(128)";			# Input stream of this document
#	$sql .= ", ED_MESSAGE VARCHAR2(256)";			# Input stream of this document	# 	alter table EDTK_TRACKING add ED_MESSAGE VARCHAR2(256)
#													# 									alter table EDTK_TRACK_2012 add ED_MESSAGE VARCHAR2(256);
#	$sql .= ", ED_HOST VARCHAR2(32)";				# Hostname for input stream of this document (max length for smtp is 31, could be 255...)

	foreach my $i (0 .. $maxkeys) {
		$sql .= " ED_K${i}_NAME VARCHAR2(8),";	# Name of key $i
		$sql .= "ED_K${i}_VAL VARCHAR2(128)";	# Value of key $i
		$sql .= "," unless ($i == $maxkeys);
	}
#	$sql .= ", PRIMARY KEY (ED_SNGL_ID, ED_JOB_EVT, ED_APP)"
	$sql .= " )";	#, CONSTRAINT pk_$ENV{EDTK_DBI_TABLENAME} PRIMARY KEY (ED_TSTAMP, ED_PROC, ED_SEQ)";

#	warn "sql cmd = $sql\n";


	$dbh->do(_sql_fixup($dbh, $sql)) or &logger (4, $dbh->errstr);
}


sub _drop_table_TRACKING {
	my ($dbh, $table) = @_;

	$dbh->do("DROP TABLE $table") or &logger (4, $dbh->errstr);
}


sub historicize_table ($$$){
	my ($dbh, $table, $suffixe) = @_;
	my $table_cible =$table."_".$suffixe;
		
	copy_table ($dbh, $table, $table_cible, '-create');	

	my $sql = "TRUNCATE TABLE $table"; # LA CA DEVIENT UN 'MOVE'
	$dbh->do($sql, undef) or die &logger (-1, $dbh->errstr);	
}


sub db_backup_agent($){
	# purge sauvegardée des 3 tables de productions : EDTK_DBI_TRACKING EDTK_DBI_OUTMNGR EDTK_DBI_ACQUIT
	# en fonction du paramétrage EDTK_ENTIRE_YEARS_KEPT
	my ($dbh)	= shift;
	my $cfg		= config_read('EDTK_DB');
	unless (defined ($cfg->{'EDTK_ENTIRE_YEARS_KEPT'}) && $cfg->{'EDTK_ENTIRE_YEARS_KEPT'} > 0){
		&logger (4, "EDTK_ENTIRE_YEARS_KEPT not defined for optimization purge. db_backup_agent not needed.");
		return 1;
	}

	my $suffixe		= strftime ("%Y%m%d", localtime);
	$suffixe 		.="_BAK";
	my $cur_year	= strftime ("%Y", localtime);

	{ # isole le block pour les variables locales
		# CHECK IF EDTK_DBI_TRACKING HAS OLD STATS
		my $sql_check="SELECT COUNT(ED_TSTAMP) FROM ".$cfg->{'EDTK_DBI_TRACKING'}." WHERE ED_TSTAMP < ? ";
		my $check	= ($cur_year - $cfg->{'EDTK_ENTIRE_YEARS_KEPT'}) . "0101000000";
		my $sth		= $dbh->prepare($sql_check);

		$sth->execute($check);
		my $result = $sth->fetchrow_array;
		unless ($result){
			&logger (5, "db_backup_agent has nothing to do with ".$cfg->{'EDTK_DBI_TRACKING'});
		} else {
			my $cible = $cfg->{'EDTK_DBI_TRACKING'}."_".$suffixe;
			copy_table ($dbh, $cfg->{'EDTK_DBI_TRACKING'}, $cible, '-create'); 
			&logger (5, "db_backup_agent done with ".$cfg->{'EDTK_DBI_TRACKING'}." for data older than $check.");

			my $sql_clean = "DELETE FROM ".$cfg->{'EDTK_DBI_TRACKING'}." WHERE ED_TSTAMP < ? ";
			$dbh->do($sql_clean, undef, $check) or die $dbh->errstr;	
		}
	}

	{ # isole le block pour les variables locales
		# CHECK IF EDTK_DBI_OUTMNGR HAS OLD STATS
		my $sql_check="SELECT COUNT(ED_DTEDTION) FROM ".$cfg->{'EDTK_DBI_OUTMNGR'}." WHERE ED_DTEDTION < ? ";
		my $check	= ($cur_year - $cfg->{'EDTK_ENTIRE_YEARS_KEPT'}) . "0101";
		my $sth		= $dbh->prepare($sql_check);

		$sth->execute($check);
		my $result = $sth->fetchrow_array;
		unless ($result){
			&logger (5, "db_backup_agent has nothing to do with ".$cfg->{'EDTK_DBI_OUTMNGR'});
		} else {
			my $cible = $cfg->{'EDTK_DBI_OUTMNGR'}."_".$suffixe;
			copy_table ($dbh, $cfg->{'EDTK_DBI_OUTMNGR'}, $cible, '-create'); 

			my $sql_clean = "DELETE FROM ".$cfg->{'EDTK_DBI_OUTMNGR'}." WHERE ED_DTEDTION < ? ";
			$dbh->do($sql_clean, undef, $check) or die $dbh->errstr;	

			&logger (4, "db_backup_agent done with ".$cfg->{'EDTK_DBI_OUTMNGR'}." for data older than $check.");
		}
	}

	{ # isole le block pour les variables locales
		# CHECK IF EDTK_DBI_ACQUIT HAS OLD STATS
		my $sql_check="SELECT COUNT (ED_DTPOST) FROM ".$cfg->{'EDTK_DBI_ACQUIT'}." WHERE ED_DTPOST < ? ";
		my $check	= ($cur_year - $cfg->{'EDTK_ENTIRE_YEARS_KEPT'}) . "0101";
		my $sth		= $dbh->prepare($sql_check);

		$sth->execute($check);
		my $result = $sth->fetchrow_array;
		unless ($result){
			&logger (5, "db_backup_agent has nothing to do with ".$cfg->{'EDTK_DBI_ACQUIT'});
		} else {
			my $cible = $cfg->{'EDTK_DBI_ACQUIT'}."_".$suffixe;
			copy_table (	#		ignore_first_line => 1,					# ignore first line : option, could be used in consonction with header du change it
$dbh, $cfg->{'EDTK_DBI_ACQUIT'}, $cible, '-create'); 
	
			my $sql_clean = "DELETE FROM ".$cfg->{'EDTK_DBI_ACQUIT'}." WHERE ED_DTPOST < ? ";
			$dbh->do($sql_clean, undef, $check) or die $dbh->errstr;	

			&logger (4, "db_backup_agent done with ".$cfg->{'EDTK_DBI_ACQUIT'}." for data older than $check.");
		}
	}

1;
}


sub create_table_FILIERES {
	my $dbh = shift;
	my $table = "EDTK_FILIERES";

	my $sql = "CREATE TABLE $table";
	$sql .= "( ED_IDFILIERE VARCHAR2(5) NOT NULL";	# rendre UNIQUE filiere id   ALTER table edtk_filieres modify ED_IDFILIERE VARCHAR2(5) NOT NULL;
	$sql .= ", ED_IDMANUFACT VARCHAR2(16)";	  
	$sql .= ", ED_DESIGNATION VARCHAR2(64)";		# 
	$sql .= ", ED_ACTIF CHAR NOT NULL";				# Flag indiquant si la filiere est active ou pas 
	$sql .= ", ED_PRIORITE INTEGER UNIQUE";			# rendre UNIQUE Ordre d'execution des filieres ALTER table edtk_filieres modify  ED_PRIORITE INTEGER UNIQUE; 
	$sql .= ", ED_TYPED CHAR NOT NULL";				# 
	$sql .= ", ED_MODEDI CHAR NOT NULL";			# 
	$sql .= ", ED_IDGPLOT VARCHAR2(16) NOT NULL";	# alter table EDTK_FILIERES add ED_IDGPLOT VARCHAR2(16) 
	$sql .= ", ED_NBBACPRN INTEGER NOT NULL";		# 
	$sql .= ", ED_NBENCMAX INTEGER";
	$sql .= ", ED_MINFEUIL_L INTEGER"; 
	$sql .= ", ED_MAXFEUIL_L INTEGER"; 
	$sql .= ", ED_FEUILPLI INTEGER";
	$sql .= ", ED_MINPLIS INTEGER";
	$sql .= ", ED_MAXPLIS INTEGER NOT NULL";
	$sql .= ", ED_POIDS_PLI INTEGER";				# poids maximum du pli dans la filiere
	$sql .= ", ED_REF_ENV VARCHAR2(8) NOT NULL";
	$sql .= ", ED_FORMFLUX VARCHAR2(3) NOT NULL";
	$sql .= ", ED_SORT VARCHAR2(128) NOT NULL";
	$sql .= ", ED_DIRECTION VARCHAR2(4) NOT NULL";
	$sql .= ", ED_POSTCOMP VARCHAR2(8) NOT NULL";
#	$sql .= ", PRIMARY KEY (ED_IDFILIERE, ED_IDMANUFACT, ED_PRIORITE)"
	$sql .= " )";

	$dbh->do(_sql_fixup($dbh, $sql)) or &logger (4, $dbh->errstr);
}


sub create_table_LOTS {
	my $dbh = shift;
	my $table = "EDTK_LOTS";

	my $sql = "CREATE TABLE $table";
	$sql .= "( ED_IDLOT VARCHAR2(8) NOT NULL";		# rendre UNIQUE ?								-> ALTER table EDTK_LOTS modify ED_IDLOT VARCHAR2(8) NOT NULL
	$sql .= ", ED_PRIORITE INTEGER   UNIQUE"; 		# rendre UNIQUE 								-> ALTER table EDTK_LOTS modify ED_PRIORITE INTEGER UNIQUE;
	$sql .= ", ED_IDAPPDOC VARCHAR2(20)";			#xx ED_REFIDDOC ATTENTION cf structure index.xls-> ALTER table EDTK_LOTS modify ED_IDAPPDOC VARCHAR2(20) NULL
	$sql .= ", ED_REFIDDOC VARCHAR2(20) NOT NULL";	# 												-> alter table EDTK_LOTS add ED_REFIDDOC VARCHAR2(20)	-> ALTER table EDTK_LOTS modify ED_REFIDDOC VARCHAR2(20) NOT NULL
	$sql .= ", ED_CPDEST VARCHAR2(10)"; 			# xxx passer cpdet sur 10 partout				-> alter table EDTK_LOTS modify ED_CPDEST VARCHAR2(8);
	$sql .= ", ED_FILTER VARCHAR2(64)";				# 												-> alter table EDTK_LOTS add ED_FILTER VARCHAR2(64); 
	$sql .= ", ED_REFENC VARCHAR2(32)";				# a mettre en place pour ajouter des encarts spécifiques à certains lots (cf impact calcul lotissement) # 	-> ALTER table EDTK_LOTS modify ED_REFENC VARCHAR2(32);
	$sql .= ", ED_GROUPBY VARCHAR2(16)"; 
	$sql .= ", ED_LOTNAME VARCHAR2(64) NOT NULL";	# 												-> ALTER table EDTK_LOTS modify ED_LOTNAME VARCHAR2(64) NOT NULL;
	$sql .= ", ED_IDGPLOT VARCHAR2(16) NOT NULL";	
	$sql .= ", ED_IDMANUFACT VARCHAR2(16) NOT NULL";	
	$sql .= ", ED_CONSIGNE VARCHAR2(250) ";			# alter table EDTK_LOTS add ED_CONSIGNE VARCHAR2(250)
#	$sql .= ", PRIMARY KEY (ED_IDLOT, ED_PRIORITE, ED_REFIDDOC)"
	$sql .= " )";

	$dbh->do(_sql_fixup($dbh, $sql)) or &logger (4, $dbh->errstr);
}


sub create_table_REFIDDOC {
	my $dbh = shift;
	my $table = "EDTK_REFIDDOC";

	my $sql = "CREATE TABLE $table";
	$sql .= "( ED_REFIDDOC VARCHAR2(20) NOT NULL"; 
	$sql .= ", ED_CORP VARCHAR2(8) NOT NULL";		# Entity related to the document
	$sql .= ", ED_CATDOC CHAR NOT NULL";  
	$sql .= ", ED_PORTADR CHAR NOT NULL";  
	$sql .= ", ED_MASSMAIL CHAR NOT NULL";
	$sql .= ", ED_EDOCSHARE CHAR NOT NULL";  
	$sql .= ", ED_TYPED CHAR NOT NULL";  
	$sql .= ", ED_MODEDI CHAR NOT NULL";  
	$sql .= ", ED_PGORIEN VARCHAR2(2)";
	$sql .= ", ED_FORMATP VARCHAR2(2)"; 
	$sql .= ", ED_REFIMP_P1 VARCHAR2(16)"; 
	$sql .= ", ED_REFIMP_PS VARCHAR2(16)"; 
	$sql .= ", ED_REFIMP_REFIDDOC VARCHAR2(64)"; 
	$sql .= ", ED_MAIL_REFERENT VARCHAR2(300)";		# referent mail for doc validation
#	$sql .= ", PRIMARY KEY (ED_REFIDDOC, ED_CORP, ED_CATDOC)"
	$sql .= " )";

	$dbh->do(_sql_fixup($dbh, $sql)) or &logger (4, $dbh->errstr);
}


sub create_table_SUPPORTS {
	my $dbh = shift;
	my $table = "EDTK_SUPPORTS";

	my $sql = "CREATE TABLE $table";
	$sql .= "( ED_REFIMP VARCHAR2(16) UNIQUE";	# ALTER table EDTK_SUPPORTS modify ED_REFIMP VARCHAR2(16) UNIQUE;
	$sql .= ", ED_TYPIMP CHAR NOT NULL";  
	$sql .= ", ED_FORMATP VARCHAR2(2) NOT NULL";
	$sql .= ", ED_POIDSUNIT INTEGER NOT NULL";  
	$sql .= ", ED_FEUIMAX INTEGER";  
	$sql .= ", ED_POIDSMAX INTEGER";  
	$sql .= ", ED_BAC_INSERT INTEGER";  
	$sql .= ", ED_COPYGROUP VARCHAR2(16)";
	$sql .= ", ED_OPTCTRL VARCHAR2(8)"; 
	$sql .= ", ED_DEBVALID VARCHAR2(8)"; 
	$sql .= ", ED_FINVALID VARCHAR2(8)"; 
#	$sql .= ", PRIMARY KEY (ED_REFIMP, ED_TYPIMP)"
	$sql .= " )";

	$dbh->do(_sql_fixup($dbh, $sql)) or &logger (4, $dbh->errstr);
}


our @DISTRIB_COLS = (
	['ED_IDLDOC',	'VARCHAR2(25) NOT NULL'],# Identifiant du document dans le lot de mise en page ED_SNGL_ID porté à 25
	['ED_IDSEQPG',	'INTEGER NOT NULL'],	# Numéro de séquence de page [doc] dans le lot de mise en page
	['ED_SEQDOC',	'INTEGER NOT NULL'],	# Numéro de séquence du document dans le lot
	['ED_IDJOB',	'VARCHAR2(25)'],		# Identifiant du Job # 17/07/2020 ALTER table EDTK_INDEX add ED_IDJOB VARCHAR2(25);

	['ED_IDLOT',	'VARCHAR2(8)'],			# identifiant du lot						ALTER table EDTK_INDEX modify ED_IDLOT VARCHAR2(8) NOT NULL;
	['ED_SEQLOT',	'VARCHAR2(7)'],			# identifiant du lot de mise sous plis (sous-lot) ALTER table EDTK_INDEX modify ED_SEQLOT VARCHAR2(7); update edtk_index set ed_seqlot = substr('1'|| ed_seqlot,-7);
	['ED_DTLOT',	'VARCHAR2(10)'],		# date de la création du lot de mise sous plis
	['ED_IDFILIERE','VARCHAR2(5)'],			# identifiant de la filière de production     	ALTER table EDTK_INDEX modify ED_IDFILIERE VARCHAR2(5);
	['ED_SEQPGDOC',	'INTEGER'],				# numéro de séquence de page dans le document
	['ED_NBPGDOC',	'INTEGER'],				# nombre de page (faces) du document

	['ED_WORKFLOW', 'VARCHAR2(32)'],	    # Workflow sur lequel on a produit les metadonnées	++ NEW ++
	['ED_CHANEL_OUT', 'VARCHAR2(32)'],		# Canal de distribution/Output						++ NEW ++

);


sub create_table_DISTRIB {
	my $dbh   = shift;
	my $table = "EDTK_DISTRIB";

	my $sql	= "CREATE TABLE $table ("
			. join(', ', map {"$$_[0] $$_[1]"} @DISTRIB_COLS) 
			. " )";

	$dbh->do(_sql_fixup($dbh, $sql)) or &logger (4, $dbh->errstr);
}



sub create_table_PARA {
	my $dbh = shift;
	my $table = "EDTK_TEST_PARA";

	my $sql = "CREATE TABLE $table";
	$sql .= "( ED_PARA_REFIDDOC VARCHAR2(20) NOT NULL"; 
	$sql .= ", ED_PARA_CORP VARCHAR2(8) NOT NULL";	# Entity related to the document
	$sql .= ", ED_ID       INTEGER NOT NULL";		#
	$sql .= ", ED_TSTAMP   VARCHAR2(14) NOT NULL";	# Timestamp of event
	$sql .= ", ED_TEXTBLOC VARCHAR2(512)";
#	$sql .= ", PRIMARY KEY (ED_PARA_REFIDDOC, ED_PARA_CORP)"
	$sql .= " )";

	$dbh->do(_sql_fixup($dbh, $sql)) or &logger (4, $dbh->errstr);
}


sub create_table_DATAGROUPS {
	my $dbh = shift;
	my $table = "EDTK_TEST_DATAGROUPS";

	my $sql = "CREATE TABLE $table";
	$sql .= "( ED_DGPS_REFIDDOC VARCHAR2(20) NOT NULL"; 
	$sql .= ", ED_ID   INTEGER NOT NULL";
	$sql .= ", ED_DATA VARCHAR2(64)";
	$sql .= " )";

	$dbh->do(_sql_fixup($dbh, $sql)) or die &logger (-1, $dbh->errstr);
}


sub create_table_ACQUIT {
	my $dbh = shift;
	my $table = "EDTK_ACQ";

	my $sql = "CREATE TABLE $table";
	$sql .= "( ED_SEQLOT  VARCHAR2(7)  NOT NULL";	# identifiant du lot de mise sous plis (sous-lot) update edtk_acq set ed_seqlot = substr('1'|| ed_seqlot,-7);
	$sql .= ", ED_LOTNAME VARCHAR2(16) NOT NULL";	# alter table EDTK_LOTS add ED_LOTNAME VARCHAR2(16);  alter table EDTK_LOTS modify ED_LOTNAME VARCHAR2(16) NOT NULL;
	$sql .= ", ED_DTPRINT VARCHAR2(8)";				# date de d'impression
	$sql .= ", ED_DTPOST  VARCHAR2(8)  NOT NULL";	# date de remise en poste
	$sql .= ", ED_NBFACES INTEGER   	NOT NULL";	# nombre de faces du lot (faces comptables, comprenant les faces blanches de R°/V°)
	$sql .= ", ED_NBPLIS  INTEGER 		NOT NULL";	# nombre de documents du pli
	$sql .= ", ED_DTPOST2 VARCHAR2(8)";				# date de remise en poste		
	$sql .= ", ED_DTCHECK VARCHAR2(8)";				# date de check
	$sql .= ", ED_STATUS  VARCHAR2(4)";				# check status
#	$sql .= ", PRIMARY KEY (ED_SEQLOT, ED_LOTNAME)"
	$sql .= " )";

	$dbh->do(_sql_fixup($dbh, $sql)) or &logger (4, $dbh->errstr);
}


our @INDEX_COLS = (
	# SECTION COMPOSITION DE L'INDEX
	['ED_REFIDDOC',	'VARCHAR2(25) NOT NULL'],# identifiant dans le référentiel de document
	['ED_IDLDOC',	'VARCHAR2(25) NOT NULL'],# Identifiant du document dans le lot de mise en page ED_SNGL_ID porté à 25
	['ED_IDSEQPG',	'INTEGER NOT NULL'],	 # Numéro de séquence de page [doc] dans le lot de mise en page
	['ED_SEQDOC',	'INTEGER NOT NULL'],	 # Numéro de séquence du document dans le lot
	['ED_IDJOB',	'VARCHAR2(25)'],		 # Identifiant du Job # 17/07/2020 ALTER table EDTK_INDEX add ED_IDJOB VARCHAR2(25);


	# SECTION DOCUMENT
	['ED_CPDEST',	'VARCHAR2(10)'],		# Code postal Destinataire			ALTER table edtk_index modify ED_CPDEST VARCHAR2(10);
	['ED_VILLDEST',	'VARCHAR2(38)'],		# Ville destinataire				ALTER table edtk_index modify ED_VILLDEST VARCHAR2(30);
	['ED_IDDEST',	'VARCHAR2(25)'],		# Identifiant du destinataire dans le système de gestion
	['ED_NOMDEST',	'VARCHAR2(38)'],		# Nom destinataire					ALTER table edtk_index modify ED_NOMDEST VARCHAR2(38);
	['ED_IDEMET',	'VARCHAR2(10)'],		# identifiant de l'émetteur
	['ED_DTEDTION',	'VARCHAR2(8) NOT NULL'],# date d'édition, celle qui figure sur le document
	['ED_TYPPROD',	'VARCHAR(16)'],			# type de production associée au lot 
	['ED_PORTADR',	'CHAR'],				# indicateur de document porte adresse
	['ED_ADRLN1',	'VARCHAR2(38)'],		# ligne d'adresse 1
	['ED_CLEGED1',	'VARCHAR2(32)'],		# clef pour système d'archivage
	['ED_ADRLN2',	'VARCHAR2(38)'],		# ligne d'adresse 2
	['ED_CLEGED2',	'VARCHAR2(20)'],		# clef pour système d'archivage
	['ED_ADRLN3',	'VARCHAR2(38)'],		# ligne d'adresse 3
	['ED_CLEGED3',	'VARCHAR2(20)'],		# clef pour système d'archivage
	['ED_ADRLN4',	'VARCHAR2(38)'],		# ligne d'adresse 4
	['ED_CLEGED4',	'VARCHAR2(20)'],		# clef pour système d'archivage
	['ED_ADRLN5',	'VARCHAR2(38)'],		# ligne d'adresse 5
	['ED_CORP',		'VARCHAR2(8) NOT NULL'],# société émettrice de la page		ALTER table edtk_index modify ED_CORP VARCHAR2(8) NOT NULL;
	['ED_DOCLIB',	'VARCHAR2(32)'],		# merge library compuset associée ? la page
	['ED_REFIMP',	'VARCHAR2(16)'],		# référence de pré-imprimé ou d'imprimé ou d'encart
	['ED_ADRLN6',	'VARCHAR2(38)'],		# ligne d'adresse 6
	['ED_SOURCE',	'VARCHAR2(8) NOT NULL'],# Source de l'index ou entité de ED_CORP
	['ED_OWNER',	'VARCHAR2(10)'],		# propriétaire du document (utilisation en gestion / archivage de documents)
	['ED_HOST',		'VARCHAR2(32)'],		# Hostname de la machine d'origine de cette entrée
	['ED_IDIDX',	'VARCHAR2(8) NOT NULL'],# identifiant de l'index
	['ED_CATDOC',	'CHAR'],				# catégorie de document
	['ED_CODRUPT',	'VARCHAR2(8)'],			# code forçage de rupture	ALTER table edtk_index modify ED_CODRUPT VARCHAR2(8);

	# SECTION LOTISSEMENT DE L'INDEX 
	['ED_IDLOT',	'VARCHAR2(8)'],			# identifiant du lot						ALTER table EDTK_INDEX modify ED_IDLOT VARCHAR2(8) NOT NULL;
	['ED_SEQLOT',	'VARCHAR2(7)'],			# identifiant du lot de mise sous plis (sous-lot) ALTER table EDTK_INDEX modify ED_SEQLOT VARCHAR2(7); update edtk_index set ed_seqlot = substr('1'|| ed_seqlot,-7);
	['ED_DTLOT',	'VARCHAR2(10)'],		# date de la création du lot de mise sous plis
	['ED_IDFILIERE','VARCHAR2(5)'],			# identifiant de la filière de production     	ALTER table EDTK_INDEX modify ED_IDFILIERE VARCHAR2(5);
	['ED_SEQPGDOC',	'INTEGER'],				# numéro de séquence de page dans le document
	['ED_NBPGDOC',	'INTEGER'],				# nombre de page (faces) du document
	['ED_POIDSUNIT','INTEGER'],				# poids de l'imprim? ou de l'encart en mg
	['ED_NBENC',	'INTEGER'],				# nombre d'encarts du doc					ALTER table EDTK_INDEX add ED_NBENC integer;
	['ED_ENCPDS',	'INTEGER'],				# poids des encarts du doc					ALTER table EDTK_INDEX add ED_ENCPDS INTEGER;
	['ED_BAC_INSERT','INTEGER'],			# Appel de bac ou d'insert

	# SECTION EDITION DE L'INDEX
	['ED_TYPED',	'CHAR'],				# type d'édition (Noir / Black / Full Color)
	['ED_MODEDI',	'CHAR'],				# mode d'édition (Simplex / Duplex) => Recto / Verso 
	['ED_FORMATP',	'VARCHAR2(2)'],			# format papier  (A4 / A3 ...)
	['ED_PGORIEN',	'VARCHAR2(2)'],			# orientation de l'édition (POrtrait / ReversePortrait  / LAndscape / Reverse Landscape)
	['ED_FORMFLUX',	'VARCHAR2(3)'],			# format du flux d'édition (AFP / PDF / ...)
#	['ED_FORMDEF',	'VARCHAR2(8)'],			# Formdef AFP
#	['ED_PAGEDEF',	'VARCHAR2(8)'],			# Pagedef AFP
#	['ED_FORMS',	'VARCHAR2(8)'],			# Forms 

	# SECTION PLI DE L'INDEX
	['ED_IDPLI',	'INTEGER'],				# identifiant du pli
	['ED_NBDOCPLI',	'INTEGER NOT NULL'],	# nombre de documents du pli
	['ED_NUMPGPLI',	'INTEGER NOT NULL'],	# numéro de la page (face) dans le pli
	['ED_NBPGPLI',	'INTEGER'],				# nombre de pages (faces) du pli
	['ED_NBFPLI',	'INTEGER'],				# nombre de feuillets du pli
	['ED_LISTEREFENC','VARCHAR2(64)'],		# liste des encarts du pli
	['ED_PDSPLI',	'INTEGER'],				# poids du pli en mg
	['ED_TYPOBJ',	'CHAR'],				# type d'objet dans le pli	xxxxxx  conserver ?
	['ED_STATUS',	'VARCHAR2(8)'],			# status de lotissement (date de remise en poste ou status en fonction des versions)  # ALTER TABLE EDTK_INDEX ADD ED_STATUS VARCHAR2(8);  # attention très lourd a éxécuter ne pas faire en prod : UPDATE EDTK_INDEX SET ED_STATUS = ED_DTPOSTE;
	['ED_DTPOSTE',	'VARCHAR2(8)'],			# à supprimer : status de lotissement (date de remise en poste ou status en fonction des versions)  ALTER TABLE edtk_index rename ED_DTPOSTE to ED_STATUS VARCHAR2(8);

	# ADD
	['ED_WORKFLOW', 'VARCHAR2(32)'],	    # Workflow sur lequel on a produit les metadonnées	++ NEW ++
	['ED_CHANEL_OUT', 'VARCHAR2(32)'],		# Canal de distribution/Output						++ NEW ++
	['ED_COUNTRY',	'VARCHAR2(5)'],			# Code pays du Destinataire							++ NEW ++
	['ED_IDCONTRACT','VARCHAR2(16)'],		# Identifiant de Contrat							++ NEW ++
	['ED_IDPRODUCT','VARCHAR2(8)']			# Identifiant de Produit							++ NEW ++

);



sub create_table_INDEX {
	my ($dbh, $table) = @_;

	my $sql	= "CREATE TABLE $table ("
			. join(', ', map {"$$_[0] $$_[1]"} @INDEX_COLS) . ", "
			. " PRIMARY KEY (ED_IDLDOC, ED_SEQDOC, ED_IDSEQPG, ED_IDJOB)"	# rajouter ED_SEQLOT ?
			. " )";

	$dbh->do(_sql_fixup($dbh, $sql)) or &logger (4, $dbh->errstr );
}


sub create_lot_sequence {
	my $dbh = shift;

	$dbh->do('CREATE SEQUENCE EDTK_IDLOT MINVALUE 0 MAXVALUE 999 CYCLE') or &logger (4, $dbh->errstr);
}


sub create_SCHEMA {
	my ($dbh, $table, $maxkeys) = @_;
	my $cfg = config_read('EDTK_DB');

	#if ( $cfg->{'EDTK_DBI_DSN'} =~ m/SQLite/i) {
	if ( $dbh->{'Driver'}->{'Name'} =~ m/SQLite/i) {
		&logger (5, "SQLite doesn't support SEQUENCE value, ignoring.");
	} elsif ( $dbh->{'Driver'}->{'Name'} =~ m/mysql/i) {
		&logger (5, "MySQL support specific SEQUENCE value, ignoring.");

	} else {
		create_lot_sequence($dbh);
	}
	create_table_INDEX($dbh, $cfg->{'EDTK_DBI_OUTMNGR'});
	$dbh->do('CREATE INDEX ed_seqlot_idx ON ' . $cfg->{'EDTK_DBI_OUTMNGR'} . ' (ed_seqlot)');
#	$dbh->do('CREATE INDEX ed_seqlot_idx ON EDTK_INDEX (ed_seqlot)');
	# vérifier les propositions de clés primaires et les index (attention à ne pas faire n'importe quoi)
	create_table_TRACKING($dbh, $cfg->{'EDTK_DBI_TRACKING'}, $cfg->{'EDTK_MAX_USER_KEY'});
	create_table_DISTRIB($dbh);

	create_table_ACQUIT($dbh);
	create_table_FILIERES($dbh);
	create_table_LOTS($dbh);
	create_table_REFIDDOC($dbh);
	create_table_SUPPORTS($dbh);
}


sub _sql_fixup {
	my ($dbh, $sql) = @_;

	if ($dbh->{'Driver'}->{'Name'} ne 'Oracle') {
		$sql =~ s/VARCHAR2 *(\(\d+\))/VARCHAR$1/g;
	}
	return $sql;
}

1;
#
#
#10g SOC5> SELECT *
#  2  FROM v$version;
# 
#BANNER
#----------------------------------------------------------------
#Oracle DATABASE 10g Enterprise Edition Release 10.1.0.3.0 - Prod
#PL/SQL Release 10.1.0.3.0 - Production
#CORE    10.1.0.3.0      Production
#TNS FOR 32-bit Windows: Version 10.1.0.3.0 - Production
#NLSRTL Version 10.1.0.3.0 - Production
# 
#5 ligne(s) sélectionnée(s).
# 
#10g SOC5> DESC dvp
# Nom                            NULL ?    Type
# ------------------------------- -------- ----
# COL_NUM                                  NUMBER(12)
# 
#10g SOC5> SELECT *
#  2  FROM dvp;
# 
#   COL_NUM
#----------
#        10
#        12
#   1000000
#   5923146
# 
#4 ligne(s) sélectionnée(s).
# 
#10g SOC5> ALTER TABLE dvp RENAME COLUMN col_num TO col_renommee;
# 
#TABLE modifiée.
# 
#10g SOC5> DESC dvp
# Nom                            NULL ?    Type
# ------------------------------- -------- ----
# COL_RENOMMEE                             NUMBER(12)
#
