B4A=true
Group=Default Group
ModulesStructureVersion=1
Type=Class
Version=12.8
@EndOfDesignText@
'-----------------------------------------------------------------------------------
' Modulo de Clase: DeviceLinker.bas
' Propósito: Gestionar el GUID del dispositivo y verificar/ligar el dispositivo
'            a un almacén y ruta en el servidor de forma segura.
'-----------------------------------------------------------------------------------
'	Ejemplo de uso:
'	==== En config.properties ====
'		sql.verify_device=Select nvl(CAT_RU_IDTELEFONO, 0) As CAT_RU_IDTELEFONO from kelloggs.CAT_RUTAS where cat_ru_idalmacen = ? And cat_ru_ruta = ?
'		sql.registarMovil=update kelloggs.CAT_RUTAS set CAT_RU_IDTELEFONO = ? where cat_ru_idalmacen = ? And cat_ru_ruta = ?
'	==== En Class_Globals ====
'	Dim linker As C_deviceLinker    '<<<< Class_Globals
'	==== En B4XPage_Created ====
'	linker.Initialize(Me, "Linker", True) '<<<<<<  B4XPage_Created
'	==== En donde se quiera llamar ====
'	linker.VerifyDevice(Subs.traeAlmacen, Subs.traeRuta)
'	Sub Linker_Response(Status As String) ' << AÑADE ESTE SUB [New Query]
'		If Starter.Logger Then LogColor($"Respuesta de DeviceLinker para la verificación del dispositivo: ${Status}"$, Colors.Magenta) '
'		Select Status
'			Case "OK"
'				Log("########################################################")
'				ToastMessageShow("Dispositivo verificado y vinculado correctamente.", False)
'				' Aquí puedes añadir lógica adicional si la verificación es exitosa, por ejemplo, habilitar ciertos botones o continuar con el flujo normal.
'			Case "REGISTRO_COMPLETO"
'				Log("########################################################")
'				ToastMessageShow("¡Registro completo!", False)
'			Case "SIN_REGISTRO"
'				Log("########################################################")
'				ToastMessageShow("¡Dispositivo sin registro!", False)
'				linker.linkDevice(Subs.traeAlmacen, e_ruta.text)    '<<<<<<<<< Si no esta registrado lo registramos.
'			Case "REGISTRANDO"
'				ToastMessageShow("¡Registro en proceso!", True)
'			Case "YA_REGISTRADO"
'				Log("########################################################")
'				ToastMessageShow("¡¡El dispositivo ya esta registrado con otra ruta!!", True)
'			Case Else ' Otros estados que tu servidor pueda devolver (ej. "UNAUTHORIZED", "PENDING_APPROVAL")
'				Log("########################################################")
'				ToastMessageShow($"Verificación del dispositivo: ${Status}"$, True)
'				' Puedes decidir si bloquear la funcionalidad o mostrar un mensaje específico según el estado.
'		End Select
'	End Sub

Sub Class_Globals
	' Configuración del servidor y comandos
	Private Const GUID_KEY_ALIAS As String = "DeviceGUID" ' << ALIAS PARA EL GUID EN EL KEYSTORE

	' Objetos y variables internas
	Private kvs As KeyValueStore ' << ALMACENAMIENTO SEGURO PARA EL GUID
	Private CallBack As Object ' << OBJETO DE CALLBACK PARA ENVIAR RESPUESTAS
	Private EventName As String ' << NOMBRE DEL EVENTO DE RESPUESTA
	Public reqManager As DBRequestManager ' << GESTOR DE PETICIONES AL SERVIDOR jRDC
	Private logger As Boolean ' << PARA CONTROLAR LOS LOGS DE ESTA CLASE
	Dim lastJobDoneError As String = ""
	Dim almacen As String = ""
	Dim ruta As String = ""
	Dim deviceId As String = ""
	' Se requieren las siguientes librerías:
	' - KeyValueStore2
	' - DBRequestManager
	' - XUI
	Dim dlDB As SQL
End Sub

' Inicializa la clase DeviceLinker.
' PageObject: El módulo (ej. C_Principal) que inicializa esta clase y manejará sus respuestas (normalmente "Me").
' NameOfEvent: El prefijo para el evento de respuesta (ej. "DeviceLinker_Response").
' AppLogger: Booleano para activar/desactivar los logs internos de esta clase, siguiendo el Starter.Logger de la app.
Public Sub Initialize (PageObject As Object, NameOfEvent As String, AppLogger As Boolean)
	CallBack = PageObject
	EventName = NameOfEvent
	logger = AppLogger ' Asignamos el estado del logger de la aplicación.

	' Inicializamos el KeyValueStore para almacenamiento seguro del GUID
	kvs.Initialize(File.DirInternal, "DeviceKeyStore.b4xkey")
	If logger Then Log("B4XKeyStore 'DeviceKeyStore' inicializado.")
	
	dlDB.Initialize(File.DirInternal, "deviceLink.db", True)
	dlDB.ExecNonQuery("CREATE TABLE IF NOT EXISTS Registro (GUID_KEY_ALIAS TEXT)") ' Creamos la tabla si no existe

	' Inicializamos el DBRequestManager para las comunicaciones con el servidor.
	' 'Me' indica que el evento JobDone de esta clase se encargará de las respuestas de reqManager.
'	reqManager.Initialize(Me, Starter.DBReqServer)
'	reqManager.Initialize(Me, "http://keymon.net:9010/DB2") 'Servidor de pruebas
'	reqManager.Initialize(Me, "http://keymon.net:1781") 'Servidor productivo
	reqManager.Initialize(Me, Starter.DBReqServer) 'Servidor productivo

	If logger Then Log("DBRequestManager para DeviceLinker inicializado.")
	If logger Then Log("DeviceLinker inicializado y listo para operar.")
End Sub

' Verifica y liga el dispositivo con un almacén y una ruta en el servidor.
' Almacen: El identificador del almacén.
' Ruta: El identificador de la ruta.
Public Sub verifyDevice(Almacen_ As String, Ruta_ As String)
	Private verificar As Boolean = True ' La verificacion se realiza por default
	Private tv As Cursor = Starter.skmt.ExecQuery("select * from cat_variables where CAT_VA_DESCRIPCION = 'VERIFY_DEVICE'")
	If tv.RowCount > 0 Then
		tv.Position = 0
		If tv.GetString("CAT_VA_VALOR") = 0 Then verificar = False
	End If
	If verificar Then ' Si VERIFY_DEVICE no está en CERO ... verificamos.
		Dim DeviceId_ As String = GetDeviceGUID ' Obtenemos o generamos el GUID del dispositivo.
		LogColor(DeviceId_, Colors.red)
		almacen = Almacen_
		ruta = Ruta_
		deviceId = DeviceId_
		If DeviceId_ = "" Then
			If logger Then LogColor("Error: GUID del dispositivo no pudo ser obtenido o generado.", Colors.Red)
			' Enviar una respuesta de error al callback si no se pudo obtener el GUID.
			If SubExists(CallBack, EventName & "_Response") Then ' [New Query]
				CallSub2(CallBack, EventName & "_Response", "GUID_ERROR") ' Dispara el evento Linker_Response("GUID_ERROR")
			End If
			Return
		End If

		If logger Then Log($"Enviando solicitud de verificación para DeviceId: ${DeviceId_}, Almacen: ${Almacen_}, Ruta: ${Ruta_}"$)

		Dim cmd As DBCommand ' Creamos un comando para enviar al servidor.
		cmd.Initialize
		cmd.Name = "verify_device_MARDS"
		' Pasamos el almacén, la ruta y el GUID del dispositivo como parámetros.
		cmd.Parameters = Array As Object(Almacen_, Ruta_) ', DeviceId_
		Log($"Enviamos almacen: ${Almacen_} y ruta: ${Ruta_}"$)
    
		' Ejecutamos el comando en el servidor. 'Me' indica que DBRequestManager_JobDone en esta clase manejará la respuesta.
		reqManager.ExecuteQuery(cmd, 0, "verify_device")
	Else ' Si está en CERO (Verificacion deshabilitada), regresamos "OK".
		CallSub2(CallBack, EventName & "_Response", "OK")
	End If
End Sub

Sub linkDevice(Almacen_ As String, Ruta_ As String)
	Dim deviceId As String = GetDeviceGUID ' Obtenemos o generamos el GUID del dispositivo.
	Log("########################################################")
	Log("REGISTRANDO")
	Dim cmd As DBCommand ' Creamos un comando para enviar al servidor.
	cmd.Initialize
	cmd.Name = "registarMovil_MARDS"
	' Pasamos el almacén, la ruta y el GUID del dispositivo como parámetros.
	cmd.Parameters = Array As Object(deviceId, Almacen_, Ruta_)
	reqManager.ExecuteCommand(cmd, "registramosGUID")
	If SubExists(CallBack, EventName & "_Response") Then
		CallSub2(CallBack, EventName & "_Response", "REGISTRANDO") ' Dispara el evento Linker_Response("REGISTRANDO")
	End If
End Sub

'244500

' Obtiene el GUID único del dispositivo desde B4XKeyStore.
' Si no existe, lo genera y lo guarda utilizando Subs.GUID.
Private Sub GetDeviceGUID As String
'	If kvs.ContainsKey(GUID_KEY_ALIAS) = False Then
'		' Corrección: Usamos la función GUID ya existente en el módulo Subs [1].
'		Dim NewGUID As String = generaGUID
'		kvs.Put(GUID_KEY_ALIAS, NewGUID) ' Lo guardamos de forma segura en el KeyStore.
'		If logger Then LogColor($"Nuevo GUID generado y guardado: ${NewGUID}"$, Colors.Blue)
'		Return NewGUID
'	Else
'		Dim ExistingGUID As String = kvs.Get(GUID_KEY_ALIAS) ' Recuperamos el GUID existente.
'		If logger Then LogColor($"GUID existente cargado: ${ExistingGUID}"$, Colors.Blue)
'		Return ExistingGUID
'	End If

	If deviceLinked = False Then
		' Corrección: Usamos la función GUID ya existente en el módulo Subs [1].
		Dim NewGUID As String = generaGUID
		dlDB.ExecNonQuery($"insert into registro ('GUID_KEY_ALIAS') values ('${NewGUID}')"$) ' Guardamos nuevo registro
		If logger Then LogColor($"Nuevo GUID generado y guardado: ${NewGUID}"$, Colors.Blue)
		Return NewGUID
	Else
		Dim ExistingGUID As String
		Private e As Cursor = dlDB.ExecQuery("select GUID_KEY_ALIAS from registro")
		e.Position = 0
		ExistingGUID = e.GetString("GUID_KEY_ALIAS")
		If logger Then LogColor($"GUID existente cargado: ${ExistingGUID}"$, Colors.Blue)
		Return ExistingGUID
	End If
End Sub

' Callback para manejar las respuestas del DBRequestManager de esta clase.
Public Sub JobDone(Job As HttpJob) ' El nombre del sub debe ser 'JobDone' o el que se haya especificado en reqManager.Initialize(Me, ApiUrl)
	If logger Then Log("INICA JOBDONE DEVICELINKER - " & Job.Tag)
	If reqManager.reqsList.IsInitialized Then 'Si tenemos lista de requests, la procesamos.
		If reqManager.reqsList.IndexOf(Job.tag) <> -1 Then
			reqManager.reqsList.RemoveAt(reqManager.reqsList.IndexOf(Job.tag))
			LogColor($">>>>>> Recibimos y quitamos ${Job.tag.As(String).ToUpperCase}"$, Colors.Blue)
		End If
		LogColor(">>>>>> " & reqManager.reqsList.Size & " - " & reqManager.reqsList, Colors.Blue)
	End If
	If Job.Success = False Then
		lastJobDoneError = Job.ErrorMessage
		LogColor("############################################", Colors.red)
		LogColor("######         JobError: " & Job.Tag & "        ######" & CRLF & "####         " & Job.ErrorMessage, Colors.red)
		LogColor("############################################", Colors.red)
		' Enviar una respuesta de error HTTP al callback.
		If SubExists(CallBack, EventName & "_Response") Then
			CallSub2(CallBack, EventName & "_Response", "HTTP_ERROR") ' Dispara el evento Linker_Response("HTTP_ERROR")
		End If
	Else 'If Job Success then ...
		lastJobDoneError = ""
		If Job.JobName = "DBRequest" Then ' Asegurarse de que sea una respuesta de DBRequestManager.
			' Primero verificamos Job.Success para saber si la comunicación HTTP fue exitosa [New Query]
			If Job.Success Then
				Dim result As DBResult = reqManager.HandleJob(Job) ' Procesamos el HttpJob para obtener el DBResult.
				If logger Then LogColor($"Petición exitosa al servidor. Registros devueltos: ${result.Rows.Size}"$, Colors.Green)


'				If result.Tag = "hist_cliente_promos" Then 'query tag
'					Starter.skmt.BeginTransaction
'					For Each records() As Object In result.Rows
'						Dim HCCP_CLIENTE As String = records(result.Columns.Get("HCCP_CLIENTE"))
'						Starter.skmt.ExecNonQuery2("INSERT INTO HIST_CLIENTE_CANT_PROMOS(HCCP_CLIENTE, HCCP_PROMO, HCCP_CANT, HCCP_CANT_VENDIDA) VALUES (?,?,?,?)", Array As Object (HCCP_CLIENTE))
'					Next
'					Starter.skmt.TransactionSuccessful
'					Starter.skmt.EndTransaction
'					' ToastMessageShow(" Historico Clientes Promociones Actualizado." , True)
'				End If

				If result.Tag = "verify_device" Then 'query tag
					' Aquí es donde la lógica de la aplicación interpreta el éxito/falla de la operación en el servidor.
					If result.Rows.Size > 0 Then
						Subs.logJobDoneResultados(result)
						For Each records() As Object In result.Rows
							Dim Status As String = "" 'records(result.Columns.Get("ESTATUS"))
							Dim CAT_RU_IDTELEFONO As String = records(result.Columns.Get("CAT_RU_IDTELEFONO_REPARTO"))
							deviceId = GetDeviceGUID
							Log($"|${deviceId}|${CAT_RU_IDTELEFONO}|"$)
'							If deviceId <> CAT_RU_IDTELEFONO Then Status = "YA_REGISTRADO" ' Ya existe OTRO registro.
							If CAT_RU_IDTELEFONO = 0 Or CAT_RU_IDTELEFONO.Length < 5 Then
								Status = "SIN_REGISTRO"
								Log("########################################################")
								Log("REGISTRANDO")
'								Dim cmd As DBCommand ' Creamos un comando para enviar al servidor.
'								cmd.Initialize
'								cmd.Name = "registarMovil"
'								' Pasamos el almacén, la ruta y el GUID del dispositivo como parámetros.
'								cmd.Parameters = Array As Object(deviceId, almacen, ruta)
'								reqManager.ExecuteCommand(cmd, "registramosGUID")
								If SubExists(CallBack, EventName & "_Response") Then
									CallSub2(CallBack, EventName & "_Response", "SIN_REGISTRO")
								End If
							else if CAT_RU_IDTELEFONO = deviceId Then
								Status = "OK"
								Starter.skmt.ExecNonQuery2("delete from CAT_VARIABLES where CAT_VA_DESCRIPCION = ?", Array As Object ("LIGADO"))
								Starter.skmt.ExecNonQuery2("INSERT INTO CAT_VARIABLES(CAT_VA_DESCRIPCION, CAT_VA_VALOR) VALUES (?,?)", Array As Object ("LIGADO", 1))
								Log("OK")
								LogColor("#########  GUARDAMOS  ###########", Colors.red)
							Else
								Status = "YA_REGISTRADO"
								Starter.skmt.ExecNonQuery2("delete from CAT_VARIABLES where CAT_VA_DESCRIPCION = ?", Array As Object ("LIGADO"))
								Starter.skmt.ExecNonQuery2("INSERT INTO CAT_VARIABLES(CAT_VA_DESCRIPCION, CAT_VA_VALOR) VALUES (?,?)", Array As Object ("LIGADO", 0))
								Log("YA_REGISTRADO")
								LogColor("#########  GUARDAMOS  ###########", Colors.red)
							End If
							' Invocamos el callback en el módulo principal con el estado.
						Next
					Else
						Status = "NO_EXISTE_RUTA"
					End If
					If SubExists(CallBack, EventName & "_Response") Then
						CallSub2(CallBack, EventName & "_Response", Status) ' Dispara el evento Linker_Response("OK") o Linker_Response("YA_REGISTRADO")
					End If
				End If
				
				If result.Tag = "registramosGUID" Then
					Log("########################################################")
					Log("REGISTRO_COMPLETO")
					LogColor("#########  GUARDAMOS  ###########", Colors.red)
					Subs.logJobDoneResultados(result)
					Starter.skmt.ExecNonQuery2("delete from CAT_VARIABLES where CAT_VA_DESCRIPCION = ?", Array As Object ("LIGADO"))
					Starter.skmt.ExecNonQuery2("INSERT INTO CAT_VARIABLES(CAT_VA_DESCRIPCION, CAT_VA_VALOR) VALUES (?,?)", Array As Object ("LIGADO", 1))
					If SubExists(CallBack, EventName & "_Response") Then
						CallSub2(CallBack, EventName & "_Response", "REGISTRO_COMPLETO") ' Dispara el evento Linker_Response("REGISTRO_COMPLETO")
					End If
				End If
			End If
		End If
		Job.Release ' Muy importante liberar el HttpJob para evitar fugas de memoria.
	End If
End Sub

'Genera un GUID (globally unique identifier)
Sub generaGUID As String
	Dim sb As StringBuilder
	sb.Initialize
	For Each stp As Int In Array(8, 4, 4, 4, 12)
		If sb.Length > 0 Then sb.Append("-")
		For n = 1 To stp
			Dim c As Int = Rnd(0, 16)
			If c < 10 Then c = c + 48 Else c = c + 55
			sb.Append(Chr(c))
		Next
	Next
	Return sb.ToString
End Sub

'Regresa true si existe registro
Sub deviceLinked As Boolean
	Private e As Cursor = dlDB.ExecQuery("select * from registro")
	If e.RowCount > 0 Then
		Return True
	Else
		Return False
	End If
End Sub