require "uuid"
local AsyncTasks = require "AsyncTasks"
local m_table = require "helpers.table"
local m_socket = require("socket")

module(..., package.seeall)

--Funcion que retorna los cambios existentes entre dos objetos
function CompareJsonObjects (new_data, old_data)
	assert(new_data, "CompareJsonObjects new_data is missing")
	assert(old_data, "CompareJsonObjects old_data is missing")

	if type(new_data) ~= "table" or type(old_data) ~= "table" then
		LogError("[CompareJsonObjects] Diff types - '%s' '%s' -> '%s' '%s'", v1, type(v1), v2, type(v2))
		return false, {}
	end

	CompareValues = function (val1, val2)
		local isSame = true
		if type(val1) == "nil" or type(val2) == "nil" then
			return false
		end
		if type(val2) == "table" and type(val1) == "table" then
			for key, value in pairs(val1) do 	-- Si agrega o modifica elementos nuevos
				local isSame = CompareValues(value, val2[key])
				if not isSame then
					return false
				end
			end
			for key, value in pairs(val2) do -- Si elimina elementos
				local isSame = CompareValues(value, val1[key])
				if not isSame then
					return false
				end
			end
		elseif type(val2) == "boolean" and type(val1) == "boolean" and tostring(val1) ~= tostring(val2) then
			isSame = false
		elseif val1 ~= val2 then
			isSame = false
		end
		return isSame
	end

	local has_changed = false
	local old_changes = {}
	local new_changes = {}
	for key, value in pairs(new_data) do
		if not CompareValues(value, old_data[key]) then
			old_changes[key] = old_data[key]
			new_changes[key] = value
			has_changed = true
		end
	end

	for key, value in pairs(old_data) do
		if not new_data[key] then
			old_changes[key] = value
			has_changed = true
		end
	end

	return has_changed, {beforeFields = old_changes, currentFields = new_changes}
end

function SplitString (inputstr, sep)
	if sep == nil then
		sep = "%s"
	end
	local t={} ; i=1
	for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
		t[i] = str
		i = i + 1
	end
	return t
end

--[[
	funcion que redondea un número. Ej:
		local number = 2.4686737060547
		local roundedNumber = round(number, 3)
		print(roundedNumber)  -- Imprime: 2.469
]]--
function Round (number, decimals)
	local power = 10 ^ decimals
	return math.floor(number * power + 0.5) / power
end

----------------------------------
--

function GetTimeArray (timeSec)
	assert(type(timeSec) == "number")
	local hours   = math.floor(timeSec / 3600)
	local minutes = math.floor((timeSec % 3600) / 60)
	local seconds = timeSec % 60
	local ms      = Round(math.floor((seconds % 1) * 1000), 3)
	seconds       = math.floor(seconds)
	local Fill = function(n)
		if n >= 10 then
			return tostring(n)
		end
		return "0" .. tostring(n)
	end
	return {
		formattedTime = string.format("%s:%s:%s.%s",
			Fill(hours),
			Fill(minutes),
			Fill(seconds),
			ms
		),
		{ hour    = hours   },
		{ minutes = minutes },
		{ seconds = seconds },
		{ ms      = ms      },
		{ time    = timeSec }
	}
end

function GetElapsedTimeSec (startTime)
	assert(startTime)
	local endTime = m_socket.gettime()
	local elapsedTimeSec = endTime - startTime
	return elapsedTimeSec, endTime
end

local m_elapsedTimeProcesses = {}
function StartElapsedTime (message, pProcessId)
	local processId = pProcessId or uuid.new()
	local startTime = m_socket.gettime()
	if m_elapsedTimeProcesses[processId] then
		m_elapsedTimeProcesses[processId] = nil
	end
	m_elapsedTimeProcesses[processId] = {processId = processId, startTime = startTime}
	if type(message) == "string" then
		message = LogDebug("%s - START --> elapsedTimeProcess: %s.", message, json.encode(m_elapsedTimeProcesses[processId]))
	end
	return processId, m_elapsedTimeProcesses[processId], message
end

function EndElapsedTime (processId, message)
	assert(processId)
	local elapsedTimeProcess = m_elapsedTimeProcesses[processId]
	--LogDebug("[HELPERS.UTILS.elapsedTime.EndElapsedTime] processId %s. elapsedTimeProcess: %s", processId, elapsedTimeProcess)
	--LogDebug("[HELPERS.UTILS.elapsedTime.EndElapsedTime] m_elapsedTimeProcesses: %s", json.encode(m_elapsedTimeProcesses))
	if not elapsedTimeProcess then
		return
	end
	elapsedTimeProcess.elapsedTimeSec, elapsedTimeProcess.endTime = GetElapsedTimeSec(elapsedTimeProcess.startTime)
	elapsedTimeProcess.humanElapsedTime = GetTimeArray(elapsedTimeProcess.elapsedTimeSec)
	elapsedTimeProcess.humanElapsedTime.id = processId
	if type(message) == "string" then
		message = LogDebug("%s - END - elapsedTimeProcess: %s.", message, json.encode(elapsedTimeProcess))
	end
	m_elapsedTimeProcesses[processId] = nil
	return processId, elapsedTimeProcess, message
end

function Uuid (self, withoutSeparator)
	local id = uuid.new()
	if withoutSeparator then
		id = id:gsub("-", "")
	end
	return id
end

function OSCapture (cmd, raw)
	--LogDebug("[HELPERS.UTILS.OSCapture] 1 - from CMD")
	local f = assert(io.popen(cmd, 'r'))
	local s = assert(f:read('*a'))
	f:close()
	if raw then return s end
	s = string.gsub(s, '^%s+', '')
	s = string.gsub(s, '%s+$', '')
	local vOSCapture = string.gsub(s, '[\n\r]+', ' ')
	LogDebug("[HELPERS.UTILS.OSCapture] cmd: '%s' - result: '%s'", cmd, vOSCapture)
	return vOSCapture
end

function Separate (self, s, separator)
	assert(type(s) == "string")
	assert(type(separator) == "string")
	-- sample regexp: "^(.+)@([^@]+)$"
	local regexp = string.format("^(.+)%s([^%s]+)$", separator, separator)
	local a, b = s:match(regexp)
	return (a or s), b
end

local m_cachedOSDistro
function GetOSDistro ()
	if m_cachedOSDistro then
		--LogDebug("[HELPERS.UTILS.GetOSDistro][from CACHE] OS_DISTRIBUTOR: '%s'",m_cachedOSDistro)
		return m_cachedOSDistro
	end
	local distro = os.getenv("OS_DISTRIBUTOR") or "ubuntu"
	m_cachedOSDistro = distro:lower()
	LogDebug("[HELPERS.utils:GetOSDistro] OS_DISTRIBUTOR : '%s' ", m_cachedOSDistro )
	return m_cachedOSDistro
end

function HasLicenseSystem ()
	return GetOSDistro() == "ubuntu"
end

function HasRepositoryService ()
	return GetOSDistro() == "ubuntu"
end

function ParseUrlQueryString (self, queryStr, TransformValuesFn)
	LogDebug("[HELPERS.utils:ParseUrlQueryString] Parsing Arguments: '%s', TransformValuesFn: %s", queryStr, TransformValuesFn)
	TransformValuesFn = TransformValuesFn or function(name, value)
		return name, value
	end
	local vars = {}
	local gsub = string.gsub
	local char = string.char
	local tonumber = tonumber
	local setVals = function(name,value)
		value = gsub (value, "%%(%x%x)", function(h) return char(tonumber(h,16)) end)
		value = gsub (value, "\r\n", "\n")
		--LogDebug("[HELPERS.utils:ParseUrlQueryString]     1- setVals -> name '%s', value: %s", name , value)
		vars[name]=value
	end
	gsub(queryStr,"([^%&]+)",
		function(spair)
			spair = gsub(spair,"([^%=]+)%=([^(%=)]*(%=*))", setVals, 1)
			gsub(spair,"([^%=]+)%=([^(%=)]+)", setVals, 1)
		end
	)
	local args = {}
	for k, v in pairs(vars) do
		LogDebug("%s = %s", k, v)
		local vname = gsub(k,"([^%.]+)%.", function(v)
			args[v] = args[v] or {}
			args = args[v]
			return ""
		end)
		if #vname then
			--LogDebug("  1-original    --> %s = %s", vname, v)
			local name, val = TransformValuesFn(vname, v)
			--LogDebug("  2-transformed --> %s = %s", vname, v)
			args[name] = val
		end
	end
	LogDebug("[HELPERS.utils.ParseUrlQueryString] Parsed Arguments: ")
	for name, value in pairs(args) do LogDebug("     ---> %s = %s %s", name, value, type(value)) end
	return args
end

function Sleep (self, timeSeconds)
	assert(type(timeSeconds) == "number")
	m_socket.sleep(timeSeconds)
end

function ExecuteAsyncTasks (self, url, taskId, method, headers, jsonData, cookies)
	assert(type(url) == "string")
	taskId = taskId or self:Uuid(true)
	method = method or "POST"
	headers = headers or {}
	if jsonData then
		headers["Content-Length"] = tostring(#jsonData)
		headers["Content-Type"] = "application/json"
	end
	cookies = cookies or {}
	--LogDebug("[HELPERS.utils.ExecuteAsyncTasks] url: %s\ntaskId: %s\nmethod: %s\nheaders: %s\njsonData: %s\ncookies: %s", url, taskId, method, json.encode(headers), jsonData, json.encode(cookies))
	local task = AsyncTasks{
		method  = method,
		url     = url,
		data    = jsonData,
		headers = headers,
		cookies = cookies
	}
	task:Add(taskId)
end

function ShowMemory (msg)
	msg = msg or ""
	local kb = math.floor(collectgarbage("count"))
	LogDebug("[HELPERS.utils:ShowMemory] Memory used: '%s' Kb %s.", kb, msg)
end

function CollectAllMemory ()
	LogDebug("[HELPERS.utils:CollectAllMemory] Starting collect all memory.")
	local n = 0
	repeat
		local count1 = math.floor(collectgarbage("count"))
		collectgarbage()
		local count2 = math.floor(collectgarbage("count"))
		n = n + 1
	until count1 == count2
	ShowMemory("CollectAllMemory took " .. tostring(n) .. " collect passes")
end

--[[
]]--
function IsUserFullSystemAccess (user)
	local isFullSystemAccess = type(user) == "table" and (user.UserGroup or ""):lower() == "full-system-access" or false
	--LogDebug("[HELPERS.utils:IsUserFullSystemAccess] ---> Id: '%s', VCC: '%s', UserGroup: '%s', VccGroup: '%s', isFullSystemAccess: '%s'.", user.Id, user.VCC, user.UserGroup, user.VccGroup, isFullSystemAccess)
	return isFullSystemAccess
end

-------------------------------------------------------------------------------
-- HTML sanitizer
-------------------------------------------------------------------------------
local function MakeSanitizerFunction (substitutions)
	assert(type(substitutions) == "table")
	local match = "["
	for k,v in pairs(substitutions) do
		match = match .. k
	end
	match = match .. "]"
	return function(text)
		return (string.gsub(text, match, function(char)
			return substitutions[char]
		end))
	end
end

-- javascript sanitizer
local ToJs = MakeSanitizerFunction{
	["\""] = [[\"]],
	["'"] = [[\']]
}

-- HTML
--http://www.w3.org/TR/xhtml1/#C_16
local ToHtml = MakeSanitizerFunction{
	["<"] = [[&lt;]],
	[">"] = [[&gt;]],
	["\""] = [[&quot;]],
	["&"] = [[&amp;]],
	["'"] = [[&#39;]]
}

--[[---------------------------------------------------------------------------------
	Elimino del html:
		1) Los siguientes tags:
			<base/>, <link>, <script></script>, <iframe></iframe>
			Aqu� se eliminan tando cuando se abre un tag y no se vuelve a cerrar <link>, o se abre y cierra en el mismo tag <base />,
			como cuando se abre y cierra con otro taga <script></script>

		2) Todos los eventos javascript y alungos atributos de los tags html por ej:
			onclick, onerror, onload, ..., ..., etc.
			href, ..., etc.

		3) Las posibles ejecuciones de javascript desde CSS, por ejemplo:
			background-color:expression("yellow";alert('peteco')); o 
			behavior: url("css/test.htc"); -- ejecucion de archivos htc
--]]
function CleanUpHtml (text, options)
	if not text then
		return ''
	end
	options = options or {}
	local canCleanJSElement = (type(options.canCleanJSElement) == "function" and options.canCleanJSElement) or function (_)
		return true
	end
	--LogDebug("[HELPERS.utils:CleanUpHtml] text %s options %s", text, json.encode(options))
	--Esta funcion elimina por completo el tag recidido por par�mtro y su contenido.
	local function cleanUpTags(text, tagList)
		-- si tag = "script", regexp va a quedar de la forma "<script.</script[^%s]*>", por ej: esto elimina los tags de tipo <script ...>...</script>
		local function clean(tag, text)
			local replacement = ""
			local regexp = "<" .. tag .. ".-</" .. tag .. "[%s]*>"
			text = text:gsub(regexp, replacement)
			--<%s*link.-> esto elimina los tags de tipo <link ... /> o los <link ... > (mal formados pero en fin pueden exisitir)
			regexp = "<%s*" .. tag .. ".->"
			text = text:gsub(regexp, replacement)
			return text
		end
		for _, tag in ipairs({"base", "link", "script", "iframe"}) do 
			if canCleanJSElement(tag) then
				text = clean(tag, text) -- elimino los tags por defecto
			end
		end 
		if type(tagList) == "table" then
			for _, tag in ipairs(tagList) do
				if canCleanJSElement(tag) then
					text = clean(string.lower(tag), text) -- elimino los tags recibidos por parametros
				end
			end 
		end
		return text
	end
	--Esta funcion elimina todos los eventos y propiedades en los tags html que puedan ejecutar javascript.
	local function cleanUpJavascriptEventsAndProperties(text)
		--En este for se van eliminando cada uno de los eventos, con cada uno de ellos se va armando una expresion regular case insensitive
		local elementsJSToClean = {
			--[[events]] "onabort", "onblur", "onchange", "onclick", "ondblclick", "onerror", "onfocus", "onkeydown", "onkeypress", 
				"onkeyup", "onload", "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onreset", "onselect", "onsubmit", "onunload",
			--[[Properties]] {name = "href", replacement = 'href="#1"'}, {name = "target"}}
		for index, property in ipairs(elementsJSToClean) do
			local event = property
			local replacement = ""
			if type(property) == "table" then
				event = property.name
				replacement = property.replacement or replacement
			end
			
			--LogDebug("[HELPERS.utils:CleanUpHtml] elementsJSToClean event %s", event)
			if canCleanJSElement(event) then
				--con el evento/metodo genera una expresion regualar para que pueda encontrar case insensitive
				--	ej: el evento "onblur" en la expresion regualr queda "[oO][nN][bB][lL][uU][rR]", entonces la expreg podria encontrar: onblur, ONBLUR, oBluR
				local eventRegExpBase = ""
				for position = 1, string.len(event) do
					local character = string.sub(event, position, position)
					eventRegExpBase = eventRegExpBase .. "[" .. character .. character:upper() .. "]"
				end
				--Al final de la funcion se le contacatena que '%s*=%s*':
				--	puede contener 0 o N espacios en blanco '%s*' luego si o si contiene un '=', despu�s antes del codigo javascript puede contener 0 o N espacios en blanco '%s*'.
				eventRegExpBase = eventRegExpBase .. '%s*=%s*'
				--agrego a la expresion regular para que elimine el codigo javascript que se encuentre entre comillas dobles '".-"'
				local eventRegExp = eventRegExpBase .. '".-"'
				text = string.gsub(text, eventRegExp, replacement)
				--agrego a la expresion regular para que elimine el codigo javascript que se encuentre entre comillas simples "'.-'"
				eventRegExp = eventRegExpBase .. "'.-'"
				text = string.gsub(text, eventRegExp, replacement)
				--agrego a la expresion regular para que elimine el codigo javascript que NO se encuentra entre comillas dobles ni simples y busca / o > como fin de codigo
				eventRegExp = eventRegExpBase .. ".-[%s/>]"
				text = string.gsub(text, eventRegExp, function(match)
					local lastCharacter = match:sub(match:len(), match:len())
					if lastCharacter == " " then -- si el ultimo caractar es un espacio retorna el match
						return replacement
					else         -- si el utlimo caracter NO es un espacio retorna el match contactatenado el ultimo caracter. Esto es asi para que no se pierte el / o el >
						return replacement .. " " .. lastCharacter
					end
				end)
			end
		end
		return text
	end
	--Esta funcion elimina del css existente en el html las ejecucion javascript
	local function cleanUpJavascriptCalledFromCss(text)
		local regexp = "[eE][xX][pP][rR][eE][sS][sS][iI][oO][nN]%(.-%)" -- elimina la ejecuci�n "expression" ej: background-color:expression( "yellow" ) ;
		text = text:gsub(regexp, "")
		regexp = "[bB][eE][hH][aA][vV][iI][oO][rR]%s*:.-%)" -- elimina el atributo behaviour y su contendido ej: behavior:	url("css/test.htc");
		text = text:gsub(regexp, "")
		return text
	end
	text = text:gsub("<[^%s>]+", string.lower) --paso todos los tags a minuscula
	text = cleanUpTags(text, options.tagList) -- elimino el contenido de los tags
	text = cleanUpJavascriptEventsAndProperties(text) -- elimino del contenido todos los eventos y metodos javascirpt
	text = cleanUpJavascriptCalledFromCss(text) -- elimino del contentido de los css de la pagina todo codigo que se pueda ejecutar javascript
	return text
end

--[[-----------------------------------------------------
	GOOGLE ANALYTICS
---------------------------------------------------------]]
local m_analytics = {}

--[[
--]]
m_analytics.GetSystemConfig = function(self)
	local system = require "helpers.system"
	local webAnalyticsSnippetsConfig = system:GetSystemConfig("WEB_ANALYTICS_SNIPPETS")
	-- LogDebug("[HELPERS.utils.analytics:GetSystemConfig] --> webAnalyticsSnippetsConfig: %s", webAnalyticsSnippetsConfig)
	-- m_table:Log(webAnalyticsSnippetsConfig)
	return webAnalyticsSnippetsConfig
end

--[[
--]]
m_analytics.IsEnabled = function(self)
	local isEnabled = false
	local webAnalyticsSnippetsConfig = self:GetSystemConfig()
	if webAnalyticsSnippetsConfig then
		isEnabled = tonumber(webAnalyticsSnippetsConfig.IsEnabled) == 1
	end
	--LogDebug("[HELPERS.utils.analytics:IsEnabled] -> status: %s", isEnabled == true and "ENABLED" or "DISABLED")
	return isEnabled, webAnalyticsSnippetsConfig
end

--[[
--]]
m_analytics.IncludeHeadLibResources = function(self, include)
	local includeByDefault = {
		analytics = true,
		hotjar    = true
	}
	--LogDebug("[HELPERS.utils.analytics:IncludeHeadLibResources]----> params.include: %s", json.encode(include or {}))
	include = include or includeByDefault
	-- 1. valida si los recursos de snippets se encuentran habilitados.
	local isEnabled, webAnalyticsSnippetsConfig = self:IsEnabled()
	if not isEnabled then
		return ""
	end
	local resource = ""
	------------------------
	-- 2. GoogleAnalytics
	------------------------
	local googleAnalytics = webAnalyticsSnippetsConfig.GoogleAnalytics or {}
	if include.analytics and googleAnalytics.IsEnabled then
		--LogDebug("[HELPERS.utils.analytics:IncludeHeadLibResources] 1--> (YES include) googleAnalytics.Head: %s", googleAnalytics.Head)
		resource = resource .. googleAnalytics.Head or "\n"
	--else
		--LogDebug("[HELPERS.utils.analytics:IncludeHeadLibResources] 1--> (NOT include) googleAnalytics.Head")
	end
	------------------------
	-- 3. GoogleTagManager
	------------------------
	local googleTagManager = webAnalyticsSnippetsConfig.GoogleTagManager or {}
	if include.analytics and googleTagManager.IsEnabled then
		--LogDebug("[HELPERS.utils.analytics:IncludeHeadLibResources] 2--> (YES include) googleTagManager.Head: %s", googleTagManager.Head)
		resource = resource .. googleTagManager.Head or "\n"
	--else
		--LogDebug("[HELPERS.utils.analytics:IncludeHeadLibResources] 2--> (NOT include) googleTagManager.Head")
	end
	------------------------
	-- 4. Hotjar
	------------------------
	local hotjar = webAnalyticsSnippetsConfig.Hotjar or {}
	if include.hotjar and hotjar.IsEnabled then
		--LogDebug("[HELPERS.utils.analytics:IncludeHeadLibResources] 3--> (YES include) hotjar.Head: %s", hotjar.Head)
		resource = resource .. hotjar.Head or "\n"
	--else
		--LogDebug("[HELPERS.utils.analytics:IncludeHeadLibResources] 3--> (NOT include) hotjar.Head")
	end
	-- 4 . retorna todos los recursos
	--LogDebug("[HELPERS.utils:IncludeHeadLibResources] HEAD resource: %s", resource)
	return #resource > 0 and ("\n" .. resource) or ""
end

--[[
--]]
m_analytics.IncludeBodyLibResources = function(self, include)
	local includeByDefault = {
		analytics = true
	}
	--LogDebug("[HELPERS.utils.analytics:IncludeBodyLibResources]----> params.include: %s", json.encode(include or {}))
	include = include or includeByDefault
	local isEnabled, webAnalyticsSnippetsConfig = self:IsEnabled()
	if not isEnabled then
		return ""
	end
	local resource = ""
	local googleTagManager = webAnalyticsSnippetsConfig.GoogleTagManager or {}
	if include.analytics and googleTagManager.IsEnabled then
		--LogDebug("[HELPERS.utils.analytics:IncludeBodyLibResources] 1--> (YES include) googleTagManager.Body: %s", googleTagManager.Body)
		resource = resource .. googleTagManager.Body or "\n"
	--else
		--LogDebug("[HELPERS.utils.analytics:IncludeBodyLibResources] 1--> (NOT include) googleTagManager.Body")
	end
	--LogDebug("[HELPERS.utils:IncludeBodyLibResources] BODY resource: %s", resource)
	return #resource > 0 and ("\n" .. resource) or ""
end

--[[
	Funcion utilizada desde "initialdata4cassandra.lua" (LoadGoogleAnalyticsInSystemConfiguration) y en los migrations (si es que el snippet cambia)

	- POR DEFECTO LOS SNIPPETS DE ANALYTICS SE ENCUENTRAN APAGADOS -> SIEMPRE EN INSTALACIONES NUEVAS

	Queda versionado por si las moscas y en algun momento los snippets cambian o se agregan mas.

	NOTA:
	-----
	En tiempo de ejecucion (login de los agentes - carga de los htmls) los snippets se obtienen de cassandra SystemConfiguration.
	Esto solo se usa en los migrations y en el "initialdata4cassandra.lua"
--]]
m_analytics.MakeSystemConfig = function(self, versionNumber)
	versionNumber = tostring(versionNumber or 1)
	---------------------------------------------
	local versions = {
	-- VERSION 1-----------------------------------------------------------------
		["1"] = {
			Id          = "WEB_ANALYTICS_SNIPPETS",
			Description = "Include in the HTML the SNIPPETS for GOOGLE ANALYTICS, GOOGLE TAG MANAGER, HOTJAR",
			IsEnabled   = "0", -- POR DEFECTO LOS SNIPPETS DE ANALYTICS SE ENCUENTRAN APAGADOS -> SIEMPRE EN INSTALACIONES NUEVAS
			Version     = 1,
		-- 1. GoogleAnalytics -----------------------------------------------------------------------
			GoogleAnalytics = {
				IsEnabled = true,
				Head = [[
					<!-- - - - - -  - - - - -  - - - - - - - - - - - - - - - - - - - - -->
					<!--1) SNIPPETS DE GOOGLE ANALYTICS EMMA Vallejo -->
					<!-- - - - - -  - - - - -  - - - - - - - - - - - - - - - - - - - - -->
					<script async src="https://www.googletagmanager.com/gtag/js?id=G-8HSHQK5EWJ"></script>
					<script>
						window.dataLayer = window.dataLayer || [];
						function gtag(){dataLayer.push(arguments);}
						gtag('js', new Date());
						gtag('config', 'G-8HSHQK5EWJ');
					</script>
				]],
				Body = [[]]
			},
		-- 2. GoogleTagManager -----------------------------------------------------------------------
			GoogleTagManager = {
				IsEnabled = true,
				Head = [[
						<!-- - - - - -  - - - - -  - - - - - - - - - - - - - - - - - - - - -->
						<!--2) SNIPPETS DE GOOGLE TAG MANAGER -->
						<!-- - - - - -  - - - - -  - - - - - - - - - - - - - - - - - - - - -->
						<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
							new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
							j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
							'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
							})(window,document,'script','dataLayer','GTM-KSWRFV4J');
						</script>
					]],
					Body = [[
						<!-- - - - - -  - - - - -  - - - - - - - - - - - - - - - - - - - - -->
						<!--4) SNIPPETS DE GOOGLE TAG MANAGER ROCIO -->
						<!-- - - - - -  - - - - -  - - - - - - - - - - - - - - - - - - - - -->
						<noscript>
							<iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KSWRFV4J&quot; height="0" width="0" style="display:none;visibility:hidden"></iframe>
						</noscript>
					]]
				},
		-- 3. Hotjar -----------------------------------------------------------------------
			Hotjar = {
				IsEnabled = true,
				Head = [[
					<!-- - - - - -  - - - - -  - - - - - - - - - - - - - - - - - - - - -->
					<!--3) SNIPPETS DE HOTJAR -->
					<!-- - - - - -  - - - - -  - - - - - - - - - - - - - - - - - - - - -->
					<script>
						(function(h,o,t,j,a,r){
						h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
						h._hjSettings={hjid:3813439,hjsv:6};
						a=o.getElementsByTagName('head')[0];
						r=o.createElement('script');r.async=1;
						r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
						a.appendChild(r);
						})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');
					</script>
				]],
				Body = [[]]
			}
		}
	}
	------------------------------------
	local version = versions[versionNumber]
	if not version then
		local messageError = string.format("[HELPERS.utils:MakeSystemConfig] Unable to make System Analytics Info. Version '%s' does not exist.", versionNumber)
		LogWarning(messageError)
		return nil, messageError
	end
	return version
end

--[[
--]]
function GetAnalyticsInstance ()
	--LogDebug("[HELPERS.utils:GetAnalyticsInstance] ...")
	return m_analytics
end
