<?php

/*
  Clase SuperConfigFiles
  24/01/2019 ==> Se ejecuta el widget o módulo directamente desde aquí, justo después de lanzar la función remota. El resultado de la función remota se añade en un campo de "data" llamado response
  @version 1.1
 */

class SuperRemoteRequest {
	private static $last_error = '';
	private static $WS_data_update = '';
    //Constructor y destructor
    public function __construct() {
    }

    public function __destruct() {
        
    }
	
	//----------------------------------------------------
	// Por ahora los parámetros que vamos a recibir son:
	//	controller ==> Conrolador a lanzar (clase, controlador u módulo
	//	method ==> Método que se va a lanzar ==> Esta función le añadirá siempre la palabra "Remote" delante convirtiendo el método el camel case
	//	data ==> datos que le vamos a pasar al método
	//----------------------------------------------------
	public static function call($struct, $encoded = true){
		//----------------------------------------------------
		//Vamos a capturar la salida para no devolverla:
		ob_start();
		$retorno = new BasicObject();
		$retorno->response = false;
		//Añadido por Pedro el 19/Junio/2019 ==> Necesitamos decodificar la cadena antes, por tanto, por retrocompatibilidad añado el parámetro "encoded"
		//$struct contiene una estructura de datos en formato Json pero en base64, hay que decodificar:
		if ($encoded){
			$struct = base64_decode($struct);
		}
		//Esto se quitó porque para codificar usamos una función custom en jasvascript
		//Resulta que javascript nos envía json en utf8, pero necesitamos los caracteres especiales pasados a unicode (\uXXX), para solucionarlos codificamos con json y decodificamos
		/*$struct = json_encode($struct);
		$struct = json_decode($struct);*/
		//echo("$struct\n");
		//Antes de seguir vamos a registrar una función de autoload nueva que añadirá a las clases disponibles las de los módulos
			//DE esta manera conseguiremos dos cosas:
				//1º.-> La clase autoload original de webStore no lanzará excepciones
				//2º.-> Vamos a ver si se está intentando ejecutar una función remota correspondiente a algún módulo
				
		if ($struct = static::json_validate($struct)){
			//Ahora sólo vamos a ver si existe data o es nulo:
			$data = NULL;
			if (isset($struct->data)){
				$data = $struct->data;
			}
			//Pedro 19/Junio/2019 ==> Vamos a recibir el nombre de la función de "update" para que sea dinámico:
			if (isset($struct->WS_data_update)){
				self::checkWS_data_update($struct->WS_data_update);
			}
			if (isset($struct->controller)){
				$controller = $struct->controller;
				//Antes de seguir es necesario que registremos los módulos en el autoload para que se puedan ejecutar de manera dinámica. Tenemos que evitar
					//que el registro se haga dos veces:
				$funciones = spl_autoload_functions();
				if (!isset($funciones['RemoteRequest::moduleAutoload'])){
					spl_autoload_register('RemoteRequest::moduleAutoload');
				}
				//Ya podemos seguir
				
				if (class_exists($controller)){
					//Nos pueden haber mandado un método diferente:
					$method = 'remoteRequest';
					if (isset($struct->method)){
						$method = $struct->method;
						//Por seguridad convertimos el nombre del méotdo:
						$method = 'Remote' . ucfirst($method);
					}
					//Vamos a ver si la clase especificada tiene un método así:
					if (method_exists($controller, $method)){
						//ya sólo queda llamar a la función pasándole el objeto:
						//$retorno->data = $controller::$method($data);
						//Modificado por Pedro el 24/Enero/2018 ==> Esta clase se va a encargar de ejecutar los widgets o módulos a actualizar
						$retorno->response = $controller::$method($data);
						
						/*	//Modificado por Pedro el 14/Febrero/2019 ==> esto se ejecutará SIEMPRE, aunque no haya método, por eso lo comentamos aquí
						//Ahora vamos a crearnos el objeto que va a ser devuelto:
						$retorno->data = array();
						if (isset($data->components)){
							$components = $data->components;
							//si los componentes son un array, vamos a recorrerlo
							if (is_array($components)){
								foreach($components as $clave => $componente){
									if (isset($componente->component)){
										$componente = $componente->component;
										//En componente tengo una cadena de texto con la que podemos lanzar un widget:
										$retorno->data[] = Module::loadEncryptedComponent($componente);
									}
								}
							}
						}*/
						//var_dump($retorno->data);
					}/*else{
						echo('El controlador definido no contiene el método especificado');
					}*/
				}else{
					echo('El controlador definido no existe');
				}
			}/*else{
				echo('Controlador no definido');
			}*/
			
			//Modificado por Pedro el 14/Febrero/2019 ==> esto se ejecutará SIEMPRE, aunque no haya método, por eso lo comentamos aquí
			//Aquí es dónde vamos a ejecutar el widget remoto:
			$retorno->data = array();
			if (isset($data->components)){
				$components = $data->components;
				//si los componentes son un array, vamos a recorrerlo
				if (is_array($components)){
					foreach($components as $clave => $componente){
						if (isset($componente->component)){
							$componente = $componente->component;
							CabeceraCesta::getCart();
							//En componente tengo una cadena de texto con la que podemos lanzar un widget:
							$retorno->data[] = Module::loadEncryptedComponent($componente);
						}
					}
				}
			}
		}else{
			echo(static::getLastError());
		}
		//Capturamos cualquier salida que hubiese
		$salida = ob_get_contents();

		//Si tenemos algo en salida, lo guardamos en el objeto:
		$retorno->msgs = json_encode($salida);
		//Creamos un objeto Json
		$retorno = json_encode($retorno);
		//Limpiamos la salida de pantalla
		ob_clean();
		//Vamos a indicar que el retorno es json:
		header('Content-Type: application/json');
		echo($retorno);
	}
	
	public static function json_validate($string){
		// decode the JSON data
		$result = json_decode($string);

		// switch and check possible JSON errors
		switch (json_last_error()) {
			case JSON_ERROR_NONE:
				$error = ''; // JSON is valid // No error has occurred
				break;
			case JSON_ERROR_DEPTH:
				$error = 'The maximum stack depth has been exceeded.';
				break;
			case JSON_ERROR_STATE_MISMATCH:
				$error = 'Invalid or malformed JSON.';
				break;
			case JSON_ERROR_CTRL_CHAR:
				$error = 'Control character error, possibly incorrectly encoded.';
				break;
			case JSON_ERROR_SYNTAX:
				$error = 'Syntax error, malformed JSON.';
				break;
			// PHP >= 5.3.3
			case JSON_ERROR_UTF8:
				$error = 'Malformed UTF-8 characters, possibly incorrectly encoded.';
				break;
			// PHP >= 5.5.0
			case JSON_ERROR_RECURSION:
				$error = 'One or more recursive references in the value to be encoded.';
				break;
			// PHP >= 5.5.0
			case JSON_ERROR_INF_OR_NAN:
				$error = 'One or more NAN or INF values in the value to be encoded.';
				break;
			case JSON_ERROR_UNSUPPORTED_TYPE:
				$error = 'A value of a type that cannot be encoded was given.';
				break;
			default:
				$error = 'Unknown JSON error occured.';
				break;
		}

		if ($error !== '') {
			// throw the Exception or exit // or whatever :)
			exit($error);
			self::$last_error = $error;
		}else{
			self::$last_error = '';
		}

		// everything is OK
		return $result;
	}
	public static function getLastError(){
		return(self::$last_error);
	}
	
	public static function generateRandomString($len = 6){
		$result = "";
		$chars = "abcdefghijklmnopqrstuvwxyz012345678901_";
		$charArray = str_split($chars);
		for($i = 0; $i < $len; $i++){
			$randItem = array_rand($charArray);
			$result .= "".$charArray[$randItem];
		}
		return ($result);
	}
	public static function checkWS_data_update($value = ''){
		if ($value == ''){
			self::$WS_data_update = (self::$WS_data_update == '') ? ('ws' . self::generateRandomString()) : self::$WS_data_update;
		}else{
			self::$WS_data_update = $value;
		}
	}
			
	public static function get_WS_data_update(){ 
		self::checkWS_data_update();
		return(self::$WS_data_update);
	}
	
	public static function WS_data_update(){
		//Nos aseguramos de tener un valor de dataUpdate:
		self::checkWS_data_update();
		?>
	<script>
	function <?=RemoteRequest::get_WS_data_update();?>(controller, method, data, event, callback){
		//Nos aseguramos de que las variables tengan un valor
		controller = (typeof controller != 'undefined') ? controller : '';
		method = (typeof method != 'undefined') ? method : '';
		event = (typeof event != 'undefined') ? event : '';
		callback = (typeof callback != 'undefined') ? callback : function(){};

		//Si los campos obligatorios están inicializados vamos allá
		if ( (controller != '') && (method != '')){
			var components = new Array();
			//Vamos a cargar los objetos de este evento a recargarlos y a ejecutar el callback
			if (event != ''){
				//Pedro 22/Abril/2019 ==> Modificamos el componente para que se puedan añadir más campos a actualizar utilizando un pipe como separador: |
				var eventArray = event.split('|');
				eventArray.forEach(function (currentValue, index, completeArray) {
					if (currentValue != ''){
						//console.log("$('[data-update" + currentValue + "]')");
						$('[data-update' + currentValue + ']').each(function() {
							components.push({'component' : $(this).data('configuration')});
						});
					}
				});
			}
			//console.log(components);
			//Si hay componentes se añaden a los datos que tenemos
			//if (components.length > 0){ //==> Aunque no haya componentes, hay que ejecutar la función remota
			data['components'] = components; 
			//console.log("Data es: ", data);
			var dataToSend = {
				controller : controller,
				method : method,
				data: data,
			};
			//console.log("Antes ", dataToSend);
			//Vamos a pasar los datos en base64:
			dataToSend = JSON.stringify(dataToSend);
			//console.log("Despues ", dataToSend);
			//dataToSend = btoa(dataToSend);
			dataToSend = b64EncodeUnicode(dataToSend);
			//Vamos a enviar la petición:
			//console.log("enviamos la petición");
			$.ajax({
				url: '<?=Shop::$configuracion->url;?>remote_request.php',
				type: 'post',
				dataType: 'text',
				contentType: 'application/json',
				success: function (data) {
					//
					//	24/Enero/2019 ==> A partir de ahora data es una estructura como esta:
					//	{
					//		response: respuesta que nos manda la función remota ejecutada, puede ser cualquier cosa (objeto, array, dato, etc.)
					//		data: lista de objetos a insertar en el HTML
					//		msgs: mensaje devuelto por el sistema
					//	}
					//	//Adatamos la función para que procese estos datos
					//
					//Tenemos que coger los datos recibidos y procesarlos
					//Básicamente tengo el texto que debo meter en cada widget:
					//console.log('index------------------------------------');
					//console.log(data);
					data = JSON.parse(data);
					//console.log(data);
					//	data = null;
					//console.log(JSON.parse(data.response));
					//console.log(data.response);
					//console.log(data.msgs);
					//console.log(data.data);
					//console.log("inicio actualización de datos");
					if (data != null){
						//data = JSON.parse(data.data);
						var i = 0;
						//No hay que hacer nada si no hay evento asociado:
						if (event != ''){
							var listaComponentes = data.data
							//Insertamos el contenido de cada componente en su sitio
							if (Array.isArray(listaComponentes)){
								var limit = listaComponentes.length;
								for(i = 0; i < limit; i++){
									//console.log(listaComponentes[i]);
									insertComponentData(listaComponentes[i]);
								}
							}else{
								insertComponentData(listaComponentes);
							}
							//console.log("Terminado de actualizar");
							function insertComponentData(data){
								//console.log("Insetamos componentes");
								//console.log(data);
								//Si no hay hash, o content, no podemos hacer nada:
								if (typeof data != 'undefined'){
									//Vamos a buscar el componente:
									//console.log('[data-configuration="' + data.hash + '"]');
									//console.log(data.content);
									$('[data-configuration="' + data.hash + '"]').html($(data.content).html());
								}
							}
						}
						//Ejecutamos el callback
						callback(data);
					}
				},
				error : function(e, x, settings, exception) {
					console.log(e);
				},
				data: JSON.stringify(dataToSend)
			});
			//}
		}
	}	
</script>
		
		<?php
	}
	
	public static function moduleAutoload($clase){
		$return = false;
		$clase = strtolower($clase);
		//Vamos a ver si el módulo está activo:
		if (Module::is_active($clase)){
			//Hacemos el include del fichero:
			require _WS_MODULES_DIR_ . $clase . '/' . $clase. '.php';
			$return = true;
		}
		return($return);
	}
}


