<?php
//Hay que repasar la tabla "categorias-productos" para quitar el código


/*
	Clase ArmiSync es la encargada de sincronizar el programa con la web
	@version 1.0 ==> Sólo traspasamos las funciones creadas aquí adaptándolas para que aprovechen las nuevas funciones mejoradas de webStore
					Hacemos el insert on opdate agrupando listas de datos para optimizar
					Queda pendiente que en el futuro se deje esta clase más abierta para poder hacer lecturas de otras fuentes con otro formato
*/
class SuperArmiSync{
	private $tableLine = 1;			//==> zero based, línea dónde está el nombre de la tabla
	private $specificationLine = 2;	//==> zero based, línea dónde están los campos a importar
	private $firstLine = 3;			// ==> zero based, línea dónde empezamos la lectura
	private $charDelimitier = '|';	//==> Separador de campos (en csv es una coma o punto y coma)
	private $charEnclousure = '';	//==> Caracter que encierra cada campo (en csv es una comilla doble)
	private $charEscape = '\\';		//==> Caracter de escape
	private $fichas_procesadas = 0;
	private $total_fichas_procesadas = 0;
	private $limite_fichas_procesadas = 20000;
	private $modo_debug = false; // ==> Esto falla porque en este punto del código no se pueden ejecutar evaluaciones, sólo asignaciones
	private $buffer = 700;
	private $inPath = _WS_SINCRO_DIR_. 'in/';
	private $outPath = _WS_SINCRO_DIR_. 'out/';
	private $finishedPath = _WS_SINCRO_DIR_. 'finished/';
	private $dictionaryPath = _WS_SINCRO_DIR_. 'dictionary/';
	private $fileFilter = '*.{ok,OK}';
	private $sentenceBuffer = 500;
	const LAST_MODIFIED_PATH = _WS_DIR_ . 'dynamic/file_index/lf';	//==> Ruta dónde guardaremos la cadena de "última modificación para la cabacera"
	private $encodingInput = NULL;	// ==> No es necesario al leer los ficheros con la clase "WsFileObject" porque esta ya hace la lectura convirtiedno
	private $encodingOutput = NULL;
	
	//Constructor y destructor
	function __construct($debugMode = false) {
		$this->modo_debug = $debugMode;
		//Reinicializamos el sincronizador
		$this->reset();
	}

	function __destruct() {

	}
	
	public function setEncodingInput($data = NULL){
		//Nos aseguramos de que $data sea un array de más de 1 elemento:
		if (is_array($data)){
			if (count($data) > 1){
				$this->encodingInput = $data;
			}
		}
	}
	public function setEncodingOutput($data = NULL){
		//Nos aseguramos de que $data sea un array de más de 1 elemento:
		if (is_array($data)){
			if (count($data) > 1){
				$this->encodingOutput = $data;
			}
		}
	}
	
	public function setLinesLimit($limit){
		//Seteamos el limite de líneas a leer
		if ($limit > 0){
			$this->limite_fichas_procesadas = $limit;
		}
	}
	public function getLinesLimit(){
		return($this->limite_fichas_procesadas);
	}
	
	public function setBuffer($buffer){
		//Seteamos el límite del buffer que leemos
		if ($buffer > 0){
			$this->buffer = $buffer;
		}
	}
	public function getBuffer(){
		return($this->buffer);
	}
	
	public function setIn($newPath){
		$this->inPath = $newPath;
	}
	public function setOut($newPath){
		$this->outPath = $newPath;
	}
	public function setFinishedPath($newPath){
		$this->finishedPath = $newPath;
	}
	public function setDictionary($newPath){
		$this->dictionaryPath = $newPath;
	}
	public function setSentenceBuffer($newBuffer){
		$this->sentenceBuffer = $newBuffer;
	}
	
	public function reset(){
		//Puesta a 0 de todos los contadores
		$this->fichas_procesadas = 0;
		$this->total_fichas_procesadas = 0;
	}
	
	public function comprobar_existe_tabla_lecturas() {
		$bd = new BaseDatos();
		if ($bd->isConectado()){
			$bd->setConsultaSQL("show tables like 'ultima_lectura'");
			if (!$fila = $bd->getFila()){
				$sql = "CREATE TABLE ultima_lectura ( fichero CHAR(150), ultima_linea INT )";
				$bd->setConsultaSQL($sql);
			}
		}
	}
	
	public function  fullImport(){
		//Comprobamos que exista la tabla de lecturas
		$this->comprobar_existe_tabla_lecturas();
		$this->procesa_ficheros();
	}
	
	
	
	public function procesa_ficheros() {
		var_dump($this->encodingInput);
		//Inicializamos las variables
		$correcto = true;
		$inicio = explode(" ", microtime());
		$siguienteFichero = '';
		$inicioLectura = 0;
		//Esto son estados que utilizo cuando leo el fichero para saber en que parte de él me encuentro
		$nombre_tabla = '';
		$bd = new BaseDatos();
		if ($bd->isConectado()) {
			//Antes de empezar, vamos a ver si se nos quedó alguna lectura en el tintero
			$sentencia_sql = 'SELECT fichero, ultima_linea FROM ultima_lectura WHERE 1';
			$bd->setConsultaSQL($sentencia_sql);
			//echo($sentencia_sql . "<br />\n");
			$lastFile = '';
			if ($fila = $bd->getFila()) {
				$inicioLectura = $fila['ultima_linea'];
				$siguienteFichero = $fila['fichero'];
				echo('Vamos a continuar ' . $siguienteFichero . ', por la línea ' . $inicioLectura . '<br />');
				//En este caso vamos a procesar este fichero:
				$lastFile = $this->procesa_fichero($siguienteFichero, $inicioLectura);
				echo($lastFile . '<br />');
				$siguienteFichero = '';
				//Si no hay fichero el inicio de la lectura es 0, en caso contrario hay que sumar 3 par saltarnos las tres primeras líneas:
				$inicioLectura = ($lastFile == '') ? 0 : ($inicioLectura);
			}
			//Vamos a ir a por los ficheros de entrada
			//Sacamos los archivos en un array
			//echo(realpath( $this->inPath ) . $this->fileFilter);
			$array_archivos = glob(realpath( $this->inPath ) . '/' . $this->fileFilter, GLOB_BRACE);
			asort($array_archivos);
			//print_r($array_archivos);
			echo('Se van a procesar ' . count($array_archivos) . '<br />');
			//Recorremos el directorio
			for ($i = 0; ($i < count($array_archivos)) && ($this->total_fichas_procesadas < $this->limite_fichas_procesadas); $i++) {
				//Procesamos el fichero:
				$siguienteFichero = $array_archivos[$i];
				echo('Vamos a procesar de inicio ' . $siguienteFichero . ', por la línea ' . $inicioLectura . '<br />');
				$lastFile = $this->procesa_fichero($siguienteFichero, $inicioLectura);
				echo("siguiente\n<br />");
			}
			//Si tenemos lastFile, entonces hay que guardarla en la última lectora, en caso contrario limpiamos dicha tabla:
			if ($lastFile != ''){
				echo('Nos hemos dejado líneas sin leer de: ' . $lastFile . ', vamos a indicarlo en la tabla<br />');
				$sentencia_sql = 'DELETE FROM `ultima_lectura` WHERE `fichero` != "' . $lastFile . '";';
				//echo('Ejecutamos la sentencia: ' . $sentencia_sql . '<br />');
				$bd->setConsultaSQL($sentencia_sql);
				//Aquí vamos a tener en cuenta que cuando se hacen los insert hay que sumar el índice de "firstLine" si no, no hay que sumar nada
				$sentencia_sql = 'INSERT INTO `ultima_lectura` (`fichero`, `ultima_linea`) '
				. ' VALUES (\'' . $lastFile . '\', \'' . ($inicioLectura + $this->total_fichas_procesadas + $this->firstLine) . '\') '
				. ' ON DUPLICATE KEY UPDATE '
				. ' `fichero` = VALUES(`fichero`), '
				. ' `ultima_linea` = \'' . ($inicioLectura + $this->total_fichas_procesadas) . '\';'; 
				//$sentencia_sql = 'INSERT INTO `ultima_lectura` (`fichero`, `ultima_linea`) 
				//	VALUES (\'' . $lastFile . '\', \'' . ($inicioLectura + $this->total_fichas_procesadas + $this->firstLine) . '\')';
				//echo($sentencia_sql . '<br />');
				$bd->setConsultaSQL($sentencia_sql);
			}else{
				//vaciamos la tabla
				$sentencia_sql = 'TRUNCATE TABLE `ultima_lectura`;';
				//echo('Ejecutamos la sentencia: ' . $sentencia_sql . '<br />');
				$bd->setConsultaSQL($sentencia_sql);
			}
			
			//$directorio->close();
			$fin = explode(' ', microtime());
			$fin[0] = $fin[0] - $inicio[0];
			$fin[1] = $fin[1] - $inicio[1];
			//$error .= "<p>La inserción de " . $numero_linea . " ha tardado " . $fin[1] + $fin[0] . "</p>";
			//mostrarLog("$error<br />\n");
			//Si se han hecho lecturas, guardamos la fecha de última modificaición:
			if (count($array_archivos) > 0){
				static::setLastInserted();
			}
		}
		static::bdOptimize();
	}

	private function procesa_fichero($ruta, $inicio = 0){
		$retorno = $ruta;
		//Variables locales de la función:
		$nombreTabla = '';	//==> nombre de la tabla 
		//Creamos un objeto splfileobect:
		$file = new WSplFileObject($ruta);
		//Nos vamos directamente a la segunda línea que es la que tiene el nombre de la tabla:
		$file->seek($this->tableLine);
		//Leemos de manera normal:
		$nombreTabla = $file->fgets();
		if ($this->encodingInput != NULL){
			$nombreTabla = iconv($this->encodingInput[0], $this->encodingInput[1], $nombreTabla);
		}
		$nombreTabla = $this->cleanTableName($nombreTabla);
		echo('Estamos en la tabla: ' . $nombreTabla . ' <br />');
		//Con el nombre de la tabla tenemos que ver si tiene un tratamiento especial:
		$processed = false;
		//La idea es que se añadan procesos para cada tabla a este objeto, para ello creamos un método con el "nombreTabla_procesarFichero"
		//si en el nombre de la tabla hay un guión, se pasa a guión bajo _
		$nombreFuncion = str_replace('-', '_', $nombreTabla) . '_procesarFichero';
		if (method_exists($this, $nombreFuncion) ){
			$retorno = $this->$nombreFuncion($ruta, $inicio);
			$processed = true;
		}
		
		if ( ($nombreTabla != '') && $this->createDictionary($nombreTabla) && (!$processed) ){
			//Seguimos ahora cogiendo la línea que contiene la especificación de campos a guardar:
			$file->seek($this->specificationLine);
			$specification = trim($file->fgets());
			if ($this->encodingInput != NULL){
				$specification = iconv($this->encodingInput[0], $this->encodingInput[1], $specification);
			}
			echo('Especificación a utilizar: ' . $specification . '<br />');
			if ($specification != ''){
				//los campos recibidos los necesitamos en un array:
				$specification = explode($this->charDelimitier, $specification);
				//De aquí tenemos que sacar los campos que existen así como la clave primaria:
				//En primer lugar nos quedamos en con un array sólo con los campos que hay en la tabla
				$structureTemplate = $this->generateStructureTemplate($specification, $nombreTabla);
				$specification = $this->applyDataStructureTemplate($specification, $structureTemplate);
				//Llegó el momento de recorrer las líneas para ver los borrados y los updates:
				$arrayBorrado = array(); // ==> Es un array que contrendrá todos los arrays de borrado
				$arrayUpdates = array(); // ==> Es un array que contrendrá todos los arrays de alta/mod
				$arrayBorradoActual = array(); // => Array que contiene el borrado para un bloque de tamaño = al buffer establecido
				$arrayUpdatesActual = array(); // => Array que contiene el alta/mod para un bloque de tamaño = al buffer establecido
				//vamos a leer las líneas, ojo, que podemos haber recibido una línea de inicio:
				$inicio = ($inicio == 0) ? $this->firstLine : $inicio;
				echo('Nos colocamos en la línea: ' . $inicio . '<br />');
				$file->seek($inicio);
				while( (!$file->eof()) && ($this->total_fichas_procesadas < $this->limite_fichas_procesadas) ){
					//Aquí es dónde, en un futuro, implementaremos la lectura de un fichero en CSV
					$linea = $file->fgets();
					if ($this->encodingInput != NULL){
						$linea = iconv($this->encodingInput[0], $this->encodingInput[1], $linea);
						if ($ficheroTest = fopen($this->outPath . 'test.txt', 'a+' )){
							fwrite($ficheroTest, $linea);
							fclose($ficheroTest);
						}
					}
					$linea = trim(addslashes($linea));	// ==> COgemos la línea pero añadiendo un \ antes de los caracteres que puedan jorobar las select
					//TODO  ==> OJO A LA CODIFICACIÓN
					//En línea tenemos si es un alta/modificación o un borrado:
					$contenidoLinea = explode($this->charDelimitier, $linea);
					if (count($contenidoLinea) > 2){
						$tipoMovimiento = $contenidoLinea[0];
						$indexSize = $contenidoLinea[1];
						$contenidoLinea = array_slice($contenidoLinea, 2);
						//Aplicamos la plantilla
						$contenidoLinea = $this->applyDataStructureTemplate($contenidoLinea, $structureTemplate);
						if (strtoupper($tipoMovimiento) == 'B'){
							//En el array de borrado vamos a insertar tantos arrays como campos contenga la tabla, metiendo en el primer campo del cada uno 
							//	en el índice 0 de cada array
							/*if (count($arrayBorradoActual) == 0){
								foreach($specification as $spfKey => $spfValue){
									$arrayBorradoActual[] = array($spfValue);
								}
							}
							//Ahora tenemos que añadir a cada array del contenido los valores obtenidos:
							foreach($contenidoLinea as $camposKey => $camposValue){
								$arrayBorradoActual[$camposKey][] = $camposValue;
							}*/
							$arrayBorradoActual[] = ' (\'' . implode('\',\'', $contenidoLinea) . '\') ';	// ==> Aquí van las insert
						}else{
							//Todo lo que no sea un borrado es un alta/mod
							//preparamos el array dónde almacenar el elemento:
							$arrayUpdatesActual[] = ' (\'' . implode('\',\'', $contenidoLinea) . '\') ';	// ==> Aquí van las insert
						}
						//Antes de seguir con las líneas vamos a ver si se ha llenado el buffer o no:
						if (count($arrayUpdatesActual) >= $this->sentenceBuffer){
							$arrayUpdates[] = $arrayUpdatesActual;
							$arrayUpdatesActual = array();
						}
						if (count($arrayBorradoActual) >= $this->sentenceBuffer){
							$arrayBorrado[] = $arrayBorradoActual;
							$arrayBorradoActual = array();
						}
						/*if (count($arrayBorradoActual) > 0){
							if (count($arrayBorradoActual[0]) >= $this->sentenceBuffer){
								$arrayBorrado[] = $arrayBorradoActual;
								$arrayBorradoActual = array();
							}
						}*/
					}
					$this->total_fichas_procesadas++;
				}
				//Es importante que llegados a este punto indiquemos si el fichero se ha leído por completo o no:
				if ($file->eof()){
					//echo('Hemos llegado al final del fichero<br />');
					$retorno = '';
				}

				//Si tenemos algo en los buffer arrays de update o borrado hay que pasarlo:
				if (count($arrayUpdatesActual) > 0){
					$arrayUpdates[] = $arrayUpdatesActual;
					$arrayUpdatesActual = array();
				}
				if (count($arrayBorradoActual) > 0){
					$arrayBorrado[] = $arrayBorradoActual;
					$arrayBorradoActual = array();
				}

				//Tenemos estructuradas las sentencias a ejecutar en varios arrays, ahora es el momento de ir ejecutándolas:
				//Hacemos los updates:
				//echo('<pre>');
				foreach($arrayUpdates as $clave => $listaSentencias){
					//Cada vuelta del bucle tenemos una lista de sentencias que tenemos que recorrer:
					$cadenaInsert = '';
					$cadenaUpdate = '';
					$coma = '';
					foreach($listaSentencias as $subClave => $sentencia){
						$cadenaInsert .= $coma . "\n" . $sentencia;
						//Para los updates hay que recorrer el array:
						$coma = ',';
					}
					$coma = '';
					//Para el update del insert necesito sacar los campos:
					foreach($specification as $claveTabla => $nombreCampo){
						$cadenaUpdate .= $coma . ' `' . $nombreCampo . '` = VALUES(`' . $nombreCampo . '`) ';
						$coma = ',';
					}
					//Aquí tenemos ahora las cadenas:
					$sentencia = 'INSERT INTO `' . $nombreTabla . '` (`' . implode('`, `', $specification) . '`) '
					. ' VALUES '
					. $cadenaInsert
					. ' ON DUPLICATE KEY UPDATE ' . "\n"
					. $cadenaUpdate . ";\n";
					//ha llegado el momento de ejecutar la sentencia:
					if ($sentencia != ''){
						$bd = new BaseDatos();
						if ($bd->isConectado()) {
							$bd->setConsultaSQL($sentencia);
						}
						//echo("$sentencia<br />\n");
					}
				}
				//echo('</pre>');
				foreach($arrayBorrado as $clave => $listaSentencias){
					$where = implode(",\n", $listaSentencias);
					$campos = '(`' . implode('`,`', $specification) . '`) ';
					$sentencia = 'DELETE FROM `' . $nombreTabla . '` WHERE ' . $campos . ' IN (' . $where . ');';

					if ($sentencia != ''){
						$bd = new BaseDatos();
						if ($bd->isConectado()) {
							$bd->setConsultaSQL($sentencia);
						}
					}
					echo('<pre>');
					//var_dump($listaSentencias);
					echo($sentencia . "\n");
					echo('</pre>');
				}
			}
		}
		//Si el retorno está vacío, hay que llevarse el fichero a leídos:
		if ($retorno == ''){
			//Movemos el fichero a la carpeta de leídos:
			$nombreFichero = basename($ruta);
			$ruta_destino = $this->finishedPath . $nombreFichero;
			//Por si las moscas, elimibamos el fichero de destino:
			if (file_exists($ruta_destino)){
				unlink($ruta_destino);
			}
			if (copy($ruta, $ruta_destino)) {
				//Si se ha podido copiar a la ruta de destino, lo borramos
				unlink($ruta);
				echo('Fichero procesado: ' . $ruta . '<br />');
			}
		}
		
		//Cabe la posibilidad de que necesitemos ejecutar un proceso posterior a la inserción (por ejemplo para reindexar articulos:
		$nombreFuncion = 'post_' . str_replace('-', '_', $nombreTabla) . '_procesarFichero';
		if (method_exists($this, $nombreFuncion) ){
			$retorno = $this->$nombreFuncion($ruta, $inicio);
			$processed = true;
		}
		
		
		return($retorno);
	}
	
	private function cleanTableName($originalName){
		//La idea es personalizar esta función para poder adaptar el nombre de la tabla al formato necesario:
		//Lo primero es quitar la palabra "tabla" del nombre de la misma:
		return(trim(substr($originalName, 5)));
	}
	
	private function createDictionary($table_name){
		$correcto = false;
		$ruta_fichero = realpath($this->dictionaryPath) . '/' . $table_name;
		if (!file_exists($ruta_fichero)) {
			echo('No existe el diccionario de la tabla: ' . $table_name . ', vamos a crearlo <br />');
			$bd = new BaseDatos();
			if ($bd->isConectado()) {
				//Antes de empezar, vamos a ver si se nos quedó alguna lectura en el tintero
				$sentencia_sql = 'SHOW FIELDS FROM `' . $table_name . '`;';
				//echo('consulta: ' . $sentencia_sql . '<br />');
				if ($bd->setConsultaSQL($sentencia_sql)){
					$cadena = '';
					while ($datos = $bd->getFila()) {
						$cadena .= $datos[0] . $this->charDelimitier;
					}
					$cadena = rtrim($cadena, $this->charDelimitier);
					echo('La estructura de la tabla a guardar es: ' . $cadena . '<br />');
					$fich = fopen($ruta_fichero, 'wt');
					if ($fich != NULL) {
						fwrite($fich, $cadena, strlen($cadena));
						$correcto = true;
					}
					fclose($fich);
				}				
			}
		}else{
			$correcto = true;
		}
		if (!$correcto){
			echo('Error<br />');
		}
		//Limpiamos la cache de archivos
		clearstatcache();
		return($correcto);
	}
	
	private function generateStructureTemplate($receivedStructure, $tableName) {
		$resultado = array();
		//Tenemos en cuenta si hay o no datos
		$ruta_fichero = realpath($this->dictionaryPath) . '/' . $tableName;
		$fich = @fopen($ruta_fichero, 'r');
		if ($fich != NULL) {
			$cadena = fgets($fich);
			if ($this->encodingInput != NULL){
				$cadena = iconv($this->encodingInput[0], $this->encodingInput[1], $cadena);
			}
			//Ahora vamos a transformar lo leído en un array
			$plantilla = explode($this->charDelimitier, $cadena);
			foreach ($receivedStructure as $indice => $valor_dato) {
				$encontrado = array_search(trim($valor_dato), $plantilla);
				if ($encontrado === FALSE) {
					$resultado[] = false;
				} else {
					$resultado[] = true;
				}
			}
		} else {
			echo("Error al intentar leer el diccionario de datos de la tabla $tableName<br />\n");
		}
		return($resultado);
		fclose($fich);
	}
	
	public function applyDataStructureTemplate($dataList, $template) {
		foreach ($dataList as $indice => $valor_dato) {
			if (isset($template[$indice])){
				if ($template[$indice]) {
					$resultado[] = $dataList[$indice];
				}
			}
		}
		return($resultado);
	}
	
	public static function setLastInserted(){
		//Si no existe el directorio lo creamos
		$fileDir = basename(ArmiSync::LAST_MODIFIED_PATH);
		if (!is_dir($fileDir)) {
			mkdir($fileDir);         
		}
		//Si existe el fichero, lo borramos ==> Esto lo comentamos porque al usar el método 'w' para el fichero lo estamos creando o limpiando
		//if (file_exists(ArmiSync::LAST_MODIFIED_PATH)){
		//	unlink(ArmiSync::LAST_MODIFIED_PATH);
		//}
		//Y guardamos el el fichero :
		if ($file = fopen(ArmiSync::LAST_MODIFIED_PATH, 'w')){
			fwrite($file, gmdate('D, d M Y H:i:s'));
			fclose($file);
		}
	}
	public static function getLastInserted(){
		$fecha = '';
		if (file_exists(ArmiSync::LAST_MODIFIED_PATH)){
			if ($file = fopen(ArmiSync::LAST_MODIFIED_PATH, 'r')){
				$fecha = fgets($file);
				fclose($file);
			}
		}else{
			$fecha = gmdate('D, d M Y H:i:s');
		}
		return($fecha);
	}
	
	public static function bdOptimize(){
		//Esta función va a optimizar la base de datos para mantener la integridad referencial y quitar los datos inservibles:
		$bd = new BaseDatos();
		if ($bd->isConectado()) {
			//Hay que borrar localidades sin nombre, sin provincia o sin código
			$sentenciaSql = 'DELETE FROM `localidad` 
							WHERE  `provincia` = \'\' OR `codigo` = \'\' OR `nombre` = \'\' OR `codigo` = \'0\' OR `provincia` = \'0\';';
			$bd->setConsultaSQL($sentenciaSql);
			$sentenciaSql = 'DELETE FROM `traducarti` 
							WHERE  `traduccion` = \'\';';
			$bd->setConsultaSQL($sentenciaSql);
			//Limpiamos las entradas en la sesión que sean mayores de 7 días
			$sentenciaSql = 'DELETE FROM `sesion` WHERE TIMESTAMPDIFF(DAY,ultimo_acceso,now()) > 7 LIMIT 100';
			$bd->setConsultaSQL($sentenciaSql);
			
			//Vamos a asegurarnos de que los códigos de programa y web estén correctamente casados:
			$sentenciaSql = 'UPDATE `mc_pedcli` `pedidos` SET `cliente`= 
				( SELECT codigo FROM `cliente` WHERE `cliente`.`codigo_sinlib` = `pedidos`.`cliente_sinlib` LIMIT 1)
				WHERE `pedidos`.`cliente_sinlib` > 0 AND `pedidos`.`cliente` = 0;';
			$bd->setConsultaSQL($sentenciaSql);
		}
	}
	
	public function categorias_productos_procesarFichero($ruta, $inicio){
		echo('Función personalizada <br />');
		$retorno = $ruta;
		//Hay que irse a la línea que nos dicen:
		$inicio = ($inicio == 0) ? $this->firstLine : $inicio;
		//Nos colocamos en esa línea y leemos como siempre, pero cogiendo los campos de otra manera:
		echo('Nos colocamos en la línea: ' . $inicio . '<br />');
		$file = new WSplFileObject($ruta);
		$file->seek($inicio);
		$bd = new BaseDatos();
		while( (!$file->eof()) && ($this->total_fichas_procesadas < $this->limite_fichas_procesadas) ){
			$linea = $file->fgets();
			if ($this->encodingInput != NULL){
				$linea = iconv($this->encodingInput[0], $this->encodingInput[1], $linea);
			}
			$linea = addslashes(trim($linea));	// ==> Cogemos la línea pero añadiendo un \ antes de los caracteres que puedan jorobar las select
			//Esta línea tiene este formato:
			//A|1|330337|50-29999,54-19999,17-09999,|
			//como siempre, pasamos a array, valor en posición 2 es el artículo, valor en posición 3 es la asociación
			$datos = explode("|", $linea);
			$articulo = $datos[2];
			$categoriasProducto = $datos[3];
			//Lo primero es eliminar todas las tuplas de la tabla "categorias_producto" para ese producto
			$sentencia_sql = 'DELETE FROM `categorias-productos` WHERE articulo = ' . $articulo;
			if ($bd->isConectado()) {
				$bd->setConsultaSQL($sentencia_sql);
				//mysqli_query($con, $sentencia_sql);
			}
			//Ahora necesitamos formar las tupas de cateogrias
			$categoriaOrden = explode(",", $categoriasProducto);
			$sentenciaPartida = "";
			foreach($categoriaOrden as $claveCategoria => $valorCategoria){
				if (trim($valorCategoria) != ''){
					$parElementos = explode("-", $valorCategoria);
					$categoria = $parElementos[0];
					$orden = $parElementos[1];
					$sentenciaPartida .= ( ($sentenciaPartida != '') ? ',' : '' ) . '(' . $articulo . ',' . $categoria . ',' . $orden . ')';
				}
			}
			//Si tenemos valores en la sentencia partida vamos a montar una select en condiciones:
			if ($sentenciaPartida != ''){
				$sentencia_sql = 'INSERT INTO `categorias-productos`
									(articulo, categoria, orden)
									VALUES
									' . $sentenciaPartida;
				//echo("La sentencia es: $sentencia_sql<br />\n");
				if ($bd->isConectado()) {
					$bd->setConsultaSQL($sentencia_sql);
					//mysqli_query($con, $sentencia_sql);
				}
			}
			$this->total_fichas_procesadas++;
		}
		
		//Es importante que llegados a este punto indiquemos si el fichero se ha leído por completo o no:
		if ($file->eof()){
			echo('Hemos llegado al final del fichero<br />');
			$retorno = '';
		}
		
		return($retorno);
	}
	
	public function post_articulo_procesarFichero($ruta, $inicio){
		//Hay que reindexar la tabla de productos:
		Busqueda::indexProducts();
	}
	
	public function post_ml_pedcli_procesarFichero($ruta, $inicio){
		//Tenemos que casar las líneas sin cabecera con dichas cabeceras:
		LineaPedidoCliente::associateEmptyLinesCode();
	}
	
	public function post_mc_pedcli_procesarFichero($ruta, $inicio){
		//Tenemos que casar las líneas sin cabecera con dichas cabeceras:
		LineaPedidoCliente::associateEmptyLinesCode();
	}
	
	//-------------------------------------------------------
	//Hasta aquí procesos de importación, vamos a exportar:
	//-------------------------------------------------------
		//Los procesos de exportación se van a realizar por tabla porque cada uno tendrá su idiosincrasia
	public function generar_fichero_usuario(){
		
		//Vamos a abrir el fichero de salida:
		$fileName = 'cli' . (new DateTime())->format("jB") . '.pen';
		//Vamos a cargar los clientes que estén pendientes sinlib por si no hay no creo el fichero
		$sentencia_sql = 'SELECT ' . Usuario::getCampos(true) . ' 
			FROM ' . Usuario::getNombreTabla(true) . ' 
			WHERE `' . Usuario::getAlias() . '`.`pendiente_sinlib` = 1 ;';
		//echo("hola<br />\n");
		$bd = new BaseDatos();
		if ($bd->isConectado()){
			//echo("Conectado<br />\n");
			$bd->setConsultaSQL($sentencia_sql);
			if ($bd->getNumeroFilas() > 0){
				//echo("Hay filas<br />\n");
				//Abrimos el fichero y guardamos:
				//tabla
				//Campos separados por el separador
				//Valores separados por el separador
				if ($file = fopen($this->outPath . $fileName, 'w' )){
					//echo("Fichero" . $this->outPath . $fileName . "creado<br />\n");
					//Vamos a recoger los códigos de los clientes que tenemos para actualizar el no-web
					$codigos = array();
					$cadena = 'cliente';
					if ($this->encodingOutput != NULL){
						$cadena = iconv($this->encodingOutput[0], $this->encodingOutput[1], $cadena);
					}
					fwrite($file, $cadena . "\n");
					$cadena = implode('|', BaseDatos::dameCampos('cliente'));
					if ($this->encodingOutput != NULL){
						$cadena = iconv($this->encodingOutput[0], $this->encodingOutput[1], $cadena);
					}
					fwrite($file, $cadena . "|\n");
					while ($fila = $bd->getFila(PDO::FETCH_ASSOC)){
						$data = implode('|', $fila);
						if ($this->encodingOutput != NULL){
							$data = iconv($this->encodingOutput[0], $this->encodingOutput[1], $data);
						}
						fwrite($file, $data . "|\n");
						$codigos[] = $fila[Usuario::getAlias() . '_' . 'codigo'];
					}
					fclose($file);
				}
				$sentencia_sql = 'UPDATE ' . Usuario::getNombreTabla(true)
									. ' SET `' . Usuario::getAlias() . '`.`pendiente_sinlib` = 3 '
									. ' WHERE `' . Usuario::getAlias() . '`.`codigo` IN (\'' . implode ( '\',\'', $codigos) . '\');';
				//echo("Sentencia de actualización<br/> \n");
				//echo("$sentencia_sql<br/> \n");
				$bd->setConsultaSQL($sentencia_sql);
			}
		}
	}
	
	public function generar_fichero_pedido(){
		//Vamos a abrir el fichero de salida:
		$fileName = 'ped' . (new DateTime())->format("jB") . '.pen';
		//Vamos a cargar los clientes que estén pendientes sinlib por si no hay no creo el fichero
		$sentencia_sql = 'SELECT ' . PedidoCliente::getCampos(true) . ' 
			FROM ' . PedidoCliente::getNombreTabla(true) . ' 
			WHERE `' . PedidoCliente::getAlias() . '`.`pendiente_sinlib` = 1 AND `' . PedidoCliente::getAlias() . '`.`cliente_sinlib` > 0;';
		//echo("$sentencia_sql<br />\n");
		$bd = new BaseDatos();
		if ($bd->isConectado()){
			//echo("Conectado<br />\n");
			$bd->setConsultaSQL($sentencia_sql);
			if ($bd->getNumeroFilas() > 0){
				//echo("Hay filas<br />\n");
				//Abrimos el fichero y guardamos:
				//tabla
				//Campos separados por el separador
				//Valores separados por el separador
				if ($file = fopen($this->outPath . $fileName, 'w' )){
					//echo("Fichero" . $this->outPath . $fileName . "creado<br />\n");
					//Vamos a recoger los códigos de los clientes que tenemos para actualizar el no-web
					$codigos = array();
					$cadena = 'mc_pedcli|ml_pedcli';
					if ($this->encodingOutput != NULL){
						$cadena = iconv($this->encodingOutput[0], $this->encodingOutput[1], $cadena);
					}
					fwrite($file, $cadena . "\n");
					$cadena = 'tipo_dato|' . implode('|', BaseDatos::dameCampos('mc_pedcli'));
					if ($this->encodingOutput != NULL){
						$cadena = iconv($this->encodingOutput[0], $this->encodingOutput[1], $cadena);
					}
					fwrite($file, $cadena . "|\n");
					//Necesitamos la especificación de las líneas:
					$cadena = 'tipo_dato|' . implode('|', BaseDatos::dameCampos('ml_pedcli'));
					if ($this->encodingOutput != NULL){
						$cadena = iconv($this->encodingOutput[0], $this->encodingOutput[1], $cadena);
					}
					fwrite($file, $cadena . "|\n");
					while ($fila = $bd->getFila(PDO::FETCH_ASSOC)){
						$data = 'C|' . implode('|', $fila);
						if ($this->encodingOutput != NULL){
							$data = iconv($this->encodingOutput[0], $this->encodingOutput[1], $data);
						}
						fwrite($file, $data . "|\n");
						//En este punto cargamos las líneas de este pedido de la misma forma
						$codigo = $fila[PedidoCliente::getAlias() . '_' . 'codigo'];
						$codigos[] = $codigo;	// ==> Códigos que vamos a borrar
						//cargamos las líneas:
						$sentencia_lineas = 'SELECT ' . LineaPedidoCliente::getCampos(true) . ' 
							FROM ' . LineaPedidoCliente::getNombreTabla(true) . ' 
							WHERE `' . LineaPedidoCliente::getAlias() . '`.`mc_pedcli` = ' . $codigo . ';';
						//echo("$sentencia_lineas<br />\n");
						$bdLineas = new BaseDatos();
						if ($bdLineas->isConectado()){
							$bdLineas->setConsultaSQL($sentencia_lineas);
							while ($fila = $bdLineas->getFila(PDO::FETCH_ASSOC)){
								$data = 'L|' . implode('|', $fila);
								if ($this->encodingOutput != NULL){
									$data = iconv($this->encodingOutput[0], $this->encodingOutput[1], $data);
								}
								fwrite($file, $data . "|\n");
							}
						}
					}
					fclose($file);
					//Ahora vamos a ponerle al fichero su nombre
					rename($this->outPath . $fileName, $this->outPath . $fileName . '.ok');
				}
				$sentencia_sql = 'UPDATE ' . PedidoCliente::getNombreTabla(true)
									. ' SET `' . PedidoCliente::getAlias() . '`.`pendiente_sinlib` = 3 '
									. ' WHERE `' . PedidoCliente::getAlias() . '`.`codigo` IN (' . implode ( ',', $codigos) . ');';
				$bd->setConsultaSQL($sentencia_sql);
			}
		}
	}
}
