sub init()
    mBind(["EpgGrid", "title", "poster", "info", "blending", "categoryList", "LoginTimer", "Video", "VideoLoading","InfoBar","TimerHint","Hint",
		   "HideBar", "ShowBar", "showInfo", "TitleInfo", "posterInfo", "TimerInfo", "TitleProgram", "progressBar", 
		   "progressBarBack", "infoTimer", "TimerBar", "pinWidget", "showAdult", "progressBarBackInfo", "progressBarInfo", 
		   "TimerProgram", "stbSignal", "description", "descProgram", "rectLoading", "InfoFuntion", "LabelGuia2", "LabelGuia3", "LabelGuia4", "LabelGuia5", "GroupGuia", "avisoFav", "avisoGroup", "tituloFav", "tituloCat", "TimerFav", "ShowAdFav", "optionDialog"])
		   
    ' Inicializar campos globales si no existen
    if m.global.hasField("live") = false then
        m.global.addFields({live: []})
    end if
    
    ' Cargar favoritos guardados
    loadSavedFavorites()
    
    ' Variables para manejo de reconexión y buffer
    m.retryCount = 0
    m.maxRetries = 5
    m.retryDelay = 3 ' segundos
    m.bufferingStuckCount = 0
    
    ' Configuración de monitoreo de reproducción
    m.playbackMonitor = createObject("roSGNode", "Timer")
    m.playbackMonitor.repeat = true
    m.playbackMonitor.duration = 5 ' Verifica cada 5 segundos
    m.playbackMonitor.observeField("fire", "checkPlaybackStatus")
    m.playbackMonitor.control = "start"
    
    getScene().FindNode("overhang").visible = false
    getScene().backgroundUri = m.global.fondo

    print "entro aca"
	m.poster.failedBitmapUri = m.global.logomenu
	
    currentTime =  CreateObject("roDateTime") ' roDateTime is initialized
    ' to the current time
    t = currentTime.AsSeconds()
    t = t - (t mod 1800) 
    m.EpgGrid.contentStartTime = t
    m.EpgGrid.leftEdgeTargetTime = currentTime.AsSeconds()
	'm.EpgGrid.channelInfoColumnLabel = tr("All Channels")
	m.EpgGrid.channelNoDataText = tr("No Data Available")

    m.top.observeField("content", "onTimeGridViewContentChange")
	
    m.EpgGrid.observeField("channelFocused", "channelFocused")
    m.EpgGrid.observeField("channelInfoFocused", "channelInfoFocused")
	m.EpgGrid.observeField("channelSelected", "OnChannelSelected")
	
    m.EpgGrid.observeField("programFocused", "programFocused")
    m.EpgGrid.observeField("programSelected", "OnProgramSelected")

    m.blending.width = getScreenSize().width
	m.blending.height = getScreenSize().height
	
    ' InfoBar Epg
	m.InfoBar.width = getScreenSize().width
	m.InfoBar.translation = [0,getScreenSize().height]
	
    m.Video.observeField("state","controlvideoplay")
    
    ' Mejora el buffer para conexiones inestables
    m.Video.enableBuffering = true
    m.Video.bufferingParams = {
        minimumTime: 5.0,      ' Comienza reproducción después de 5 segundos de buffer
        initialBufferTime: 8.0  ' Buffer de 8 segundos antes de comenzar
    }
	
	m.TimerHint.observeField("fire", "hideHint")
	m.TimerBar.observeField("fire", "hideShowBar")
	m.TimerFav.ObserveField("fire", "onTimerFav")
	
    m.showInfo = true
	m.Hint.text = tr("Press Up/Down to Change Channels")
	m.Hint.visible = false
	
	m.videoLoading.text = tr("Loading...")

	m.LabelGuia2.text = tr("5 Channels Previous")
	m.LabelGuia3.text = tr("5 Channels After")
	m.LabelGuia4.text = tr("Show & Hide the Guide")
	m.LabelGuia5.text = tr("Play & Select")
	m.InfoFuntion.translation = [getScreenSize().width - m.GroupGuia.boundingRect().width + ( m.GroupGuia.boundingRect().width/3) - 20,400]
	
	m.GroupGuia.width = getScreenSize().width
	m.GroupGuia.translation = [0,(getScreenSize().height / 2) + 40 - 150]
	
	m.tituloFav.text = tr("to see the options")
	m.tituloCat.text = tr("Category:")
	m.avisoFav.width = m.avisoGroup.boundingRect().width + 40
	m.tituloCat.translation = [getComponentXParentCenter(300,m.tituloCat.boundingRect().width),30]
	expandMenu()
end sub

' Función para cargar favoritos guardados
sub loadSavedFavorites()
    ' Leer favoritos guardados del registro
    savedFavorites = regRead("live", ReadManifest().title)
    print "Leyendo favoritos guardados: "; savedFavorites
    
    if savedFavorites <> invalid and savedFavorites <> ""
        ' Convertir JSON a objeto
        favoritesList = ParseJSON(savedFavorites)
        if favoritesList <> invalid and type(favoritesList) = "roArray"
            m.global.live = favoritesList
            print "Favoritos cargados correctamente: "; favoritesList.Count(); " canales"
        else
            print "Error al parsear favoritos guardados"
            m.global.live = []
        end if
    else
        print "No hay favoritos guardados"
        m.global.live = []
    end if
end sub
' Función para verificar si un elemento está en la lista de favoritos
function IsMyListItem(listType as String, itemId as String) as Boolean
    if m.global.hasField(listType) = false or m.global[listType] = invalid then 
        return false
    end if
    
    favList = m.global[listType]
    if type(favList) <> "roArray" then return false
    
    for each item in favList
        if item <> invalid and item.id = itemId then 
            return true
        end if
    end for
    
    return false
end function

' Función para convertir un nodo de contenido a JSON
function ContentNodeToJson(node as Object) as Object
    if node = invalid then return invalid
    
    json = {}
    ' Propiedades básicas
    json.id = node.id
    json.title = node.title
    
    ' Propiedades adicionales importantes
    if node.hdposterurl <> invalid then json.hdposterurl = node.hdposterurl
    if node.url <> invalid then json.url = node.url
    if node.streamUrl <> invalid then json.streamUrl = node.streamUrl
    if node.description <> invalid then json.description = node.description
    
    ' Propiedades específicas para reproducción
    if node.stream <> invalid then json.stream = node.stream
    if node.streamFormat <> invalid then json.streamFormat = node.streamFormat
    
    return json
end function

sub controlvideoplay()
    if (m.Video.state = "error")
        if m.retryCount < m.maxRetries
            m.retryCount = m.retryCount + 1
            m.videoLoading.text = tr("Reconectando... Intento ") + m.retryCount.toStr() + "/" + m.maxRetries.toStr()
            m.videoLoading.visible = true
            
            ' Crear temporizador de reintento con retraso progresivo
            m.reconnectTimer = createObject("roSGNode", "Timer")
            m.reconnectTimer.duration = m.retryDelay * m.retryCount ' Aumenta el tiempo entre intentos
            m.reconnectTimer.control = "start"
            m.reconnectTimer.observeField("fire", "attemptReconnect")
        else
            m.retryCount = 0
            m.Video.control = "stop"
            m.stbSignal.visible = true
            m.rectLoading.visible = true
            m.videoLoading.text = tr("Señal perdida - intente más tarde")
            m.videoLoading.visible = true
            m.Video.visible = false
        end if
    else if m.Video.state = "playing"
        m.retryCount = 0  ' Reinicia contador de intentos cuando la reproducción es exitosa
        m.bufferingStuckCount = 0
        m.videoLoading.visible = false
    else if m.Video.state = "buffering"
        m.stbSignal.visible = false
        m.rectLoading.visible = false
        m.videoLoading.text = tr("Cargando...")
        m.videoLoading.visible = true
    else if m.Video.state = "finished" 
        m.Video.control = "stop"
        m.Video.visible = false
    else if m.video.state = "stopped"
        m.rectLoading.visible = false
        m.videoLoading.visible = false
        m.stbSignal.visible = false
    end if
end sub

sub attemptReconnect()
    m.videoLoading.text = tr("Reconectando...")
    m.Video.control = "play"
    m.Video.visible = true
end sub

sub checkPlaybackStatus()
    if m.Video.state = "buffering"
        m.bufferingStuckCount = m.bufferingStuckCount + 1
        
        ' Si está atascado en buffering demasiado tiempo, intenta recuperar
        if m.bufferingStuckCount > 6 ' 30 segundos
            m.bufferingStuckCount = 0
            m.videoLoading.text = tr("Optimizando conexión...")
            m.Video.control = "stop"
            m.Video.control = "play"
        end if
    else
        m.bufferingStuckCount = 0
    end if
end sub

sub expandMenu()
	m.EpgGrid.SetFocus(false)
    m.categoryList.setFocus(true)
end sub

sub collapseMenu()
    m.categoryList.setFocus(false)
	m.EpgGrid.SetFocus(true)
end sub

sub OnChannelSelected(event as Object)
    timeGrid = event.GetRoSGNode()
    selectedItem = timeGrid.channelSelected
	m.OnChannelContent = timeGrid.content.GetChild(selectedItem)
    m.Video.content = m.OnChannelContent
    
    ' Reinicia contadores de error al cambiar de canal
    m.retryCount = 0
    m.bufferingStuckCount = 0
    
    ' Añade encabezados personalizados si es necesario
    ' m.Video.AddHeader("User-Agent", "TuUserAgent")
    
    m.Video.control = "play"
    m.Video.visible = true
    'm.Video.setFocus(false)
	m.top.rowItemSelected = [timeGrid.channelSelected, timeGrid.programSelected]
end sub

' OnProgramSelected triggered when timeGrid.programSelected updated on user
sub OnProgramSelected(event as Object)
    timeGrid = event.GetRoSGNode()
    m.top.rowItemSelected = [timeGrid.channelSelected, timeGrid.programSelected]
end sub

function channelInfoFocused(event as Object)
    m.avisoFav.opacity = 0
    m.TimerFav.control = "stop"
    if m.EpgGrid <> invalid and m.EpgGrid.content <> invalid 
        m.TimerFav.control = "start"
	endif
end function

function channelFocused(event as Object)
    if m.EpgGrid <> invalid and m.EpgGrid.content <> invalid
        ChannelProgramFocused(m.EpgGrid.channelFocused, m.EpgGrid.programFocused)	
    end if
end function

function programFocused()
    m.avisoFav.opacity = 0
    m.TimerFav.control = "stop"
    if m.EpgGrid <> invalid
        ChannelProgramFocused(m.EpgGrid.channelFocused, m.EpgGrid.programFocused)
    end if
end function

Sub onTimeGridViewContentChange()
	m.EpgGrid.visible= true
    if m._isContentFocusResetDone = true then return
    content = m.EpgGrid.content
    if content = invalid then return
    channel = content.getChild(0)
    if channel = invalid OR channel.getChildCount() = 0 then return

    isNowProgramAvailable = false
    currentTime = m.EpgGrid.leftEdgeTargetTime
    for each program in channel.GetChildren(-1, 0)
        if program.PlayStart <= currentTime AND program.PlayStart + program.PlayDuration >= currentTime then
            isNowProgramAvailable = true
            exit for
        end if
    end for

    if not isNowProgramAvailable then
        ' focus to begin on content
        m.EpgGrid.jumpToProgram = 0
        m.EpgGrid.leftEdgeTargetTime = channel.GetChild(0).PlayStart
    end if
	m._isContentFocusResetDone = true
End Sub

' ChannelProgramFocused is invoked when either channelFocused or programFocused was changed
' Used for loading content when user navigates vertically
' And updating the item details panel
sub ChannelProgramFocused(currentRowIndex as Integer, currentItemIndex as Integer)
    row = invalid
    if m.EpgGrid.content <> invalid then
        row = m.EpgGrid.content.GetChild(currentRowIndex)
    end if
    if row <> invalid
        if currentItemIndex < 0 then currentItemIndex = 0
        UpdateItemDetails(currentRowIndex, currentItemIndex)
    end if
   ' m.previousFocusedRow = currentRowIndex
    'm.previousFocusedItemIndex = currentItemIndex
end sub

Sub onTimerFav()
	m.ShowAdFav.control = "start"
End Sub

Sub hideHint()
    m.Hint.visible = false
	m.TimerHint.control = "stop"
End Sub

Sub hideShowBar()
    m.Hint.visible = true
	m.showInfo = true
	m.HideBar.control = "start"
    m.TimerHint.control = "start"
	m.TimerBar.control = "stop"
End Sub

Sub hideBar()
    m.Hint.visible = false
	m.showInfo = false
	m.TimerHint.control = "stop"
	m.TimerBar.control = "start"
End Sub

Sub showBar()
    m.Hint.visible = true
	m.showInfo = true
    m.TimerHint.control = "start"
	m.TimerBar.control = "stop"
End Sub

Sub InfoShow()
    if m.showInfo
	    viewPrograma()
        m.ShowBar.control = "start"
        hideBar()
    else
        m.HideBar.control = "start"
        showBar()
    End if
End Sub
sub UpdateItemDetails(channelIndex, programIndex)
    content = m.EpgGrid.content
    if content = invalid then return
    channel = content.GetChild(channelIndex)
    if channel = invalid then return
    program = channel.GetChild(programIndex)

    m.title.text = channel.title
    '** aca es tv en vivo debe ser logomenu
	m.poster.uri =  m.global.logomenu
	m.progressBar.width = 0
	m.progressBarBack.visible = false
	m.info.text  = tr("No Data Available")
	m.infoTimer.text = ""
	m.description.text = ""
    if program <> invalid  then 
        m.progressBarBack.visible = true
        m.info.text  = program.title
        m.infoTimer.text = secondsToTime(evalInteger(program.playStart),true,true) + " - " + secondsToTime(evalInteger(program.playStop),true,true)
        m.description.text  = program.description
        'percent = calculatePercent(program.playStart, program.playStop, m.global.timeServer)
        'm.progressBar.width = percentToNumber(percent.toInt(),m.progressBarBack.width)
	end if
end sub

sub viewPrograma()
    content = m.EpgGrid.content
    if content = invalid then return
    channel = content.GetChild(m.EpgGrid.channelFocused)
    if channel = invalid then return
    program = channel.GetChild(m.EpgGrid.programFocused)
    m.TitleInfo.text = channel.title.ToStr()
    m.posterInfo.uri = channel.hdposterurl
	m.progressBarInfo.width = 0
	m.progressBarBackInfo.visible = false
	m.TitleProgram.text  = tr("No Data Available")
	m.TimerProgram.text = ""
	m.descProgram.text = ""
    if program <> invalid  then 
	     m.progressBarBackInfo.visible = true
	    m.TitleProgram.text  = program.title
		m.TimerProgram.text = secondsToTime(program.playStart,true,true)+ " - " +secondsToTime(program.playStop,true,true)
		m.descProgram.text  = program.description
	    'percent = calculatePercent(program.playStart, program.playStart + program.playDuration, m.global.timeServer)
	    'm.progressBarInfo.width = percentToNumber(percent.toInt(),m.progressBarBack.width)
	end if
	m.TimerInfo.text = getCurrentTime(false)
	m.TimerInfo.translation = [getScreenSize().width - 220, 25]
end sub

sub playvideo()
   	m.videocontent = m.EpgGrid.content.GetChild(m.top.rowItemSelected[0])
   if m.videocontent <> invalid
      ' Reinicia contadores de error al cambiar de canal
      m.retryCount = 0
      m.bufferingStuckCount = 0
      
	  m.Video.content = m.videocontent
	  'm.Video.AddHeader("User-Agent", "MPka4TJW4gkVJmB64Cr5N0YuuxQcZm4Jf1D")
      m.Video.control = "play"
      m.Video.visible = true
   end if
end sub

sub minimizeVideo()
    m.Video.translation = [1260,50]
    m.Video.width = "600"
    m.Video.height = "325"
    m.stbSignal.translation = [1260,50]
    m.stbSignal.width = "600"
    m.stbSignal.height = "325"
	m.Video.setfocus(false)
    m.EpgGrid.setFocus(true)
	m.rectLoading.width = 600
	m.rectLoading.translation = [1265,220]
    m.videoLoading.width = 600
    m.videoLoading.translation = [1265,220]
    m.videoLoading.font="font:SmallBoldSystemFont"
    m.Hint.visible = false
	m.showInfo = true
	m.InfoBar.visible = false
	m.HideBar.control = "start"
    if m.top.rowItemSelected <> invalid
        m.EpgGrid.jumpToChannel  = m.top.rowItemSelected[0]
    end if
end sub

sub maximizeVideo()
   	m.InfoBar.visible = true
    m.Video.translation = [0,0]
    m.Video.width = "0"
    m.Video.height = "0"
    m.stbSignal.translation = [0,0]
    m.stbSignal.width = getScreenSize().width
    m.stbSignal.height = getScreenSize().height
    m.EpgGrid.setFocus(false)
	m.Video.setfocus(true)
    centerx = (getScreenSize().width - m.videoLoading.BoundingRect().width) / 2
    centery = (getScreenSize().height - m.videoLoading.BoundingRect().height) / 2
    m.videoLoading.translation = [centerx,centery]
    m.videoLoading.font="font:LargeBoldSystemFont"
	m.rectLoading.translation = [centerx,centery]
end sub

function moveRowFocus(stepValue)
  if isNode(m.EpgGrid.content)
    if m.EpgGrid.channelFocused + stepValue >= 0 and m.EpgGrid.channelFocused + stepValue < m.EpgGrid.content.getChildCount() 
      m.EpgGrid.channelFocused += stepValue
	  m.EpgGrid.channelSelected += stepValue
      focusedCell = m.EpgGrid.channelFocused
      if m.EpgGrid.channelFocused = focusedCell
        m.EpgGrid.jumpToChannel  = focusedCell
	    playvideo()
      end if
      return true
    end if
  end if
  return false
end function

sub expandOptions()
    optionsPalette = createObject("roSGNode", "RSGPalette")
    optionsPalette.colors = { "FocusColor": "0x00CCCCFF", "PrimaryTextColor": "0xFFFF00FF", "SecondaryItemColor": "0xFE1E1E1FF", "FocusItemColor": "0xFFFF00FF", "KeyboardColor": "0x00CCCC80", "InputFieldColor":"0x00CCCC80" }
	m.optionDialog.visible = true
    m.optionDialog.title = tr("Channel options")
	m.optionDialog.optionsDialog = true
	content = m.EpgGrid.content.GetChild(m.EpgGrid.channelFocused)
	if not IsMyListItem("live", content.id)
        m.optionDialog.buttons = [ tr("Add this channel to favorites"), tr("Close") ]
	else
        m.optionDialog.buttons = [ tr("Remove this channel from favorites"), tr("Close") ]
	end if
    m.optionDialog.observeFieldScoped("buttonSelected", "OptionsSelectedButton")
   ' m.optionDialog.palette = optionsPalette
	m.optionDialog.setFocus(true)
end sub

sub OptionsSelectedButton(event as Object)
	selectedButtonText = m.optionDialog.buttons[event.getData()]
    content = m.EpgGrid.content.GetChild(m.EpgGrid.channelFocused)
    if m.optionDialog.buttonSelected = 0 then
	    if selectedButtonText = tr("Add this channel to favorites")
		    if not IsMyListItem("live", content.id)
		        itemList = []
			    if m.global.live <> invalid and type(m.global.live) = "roArray"
			    	itemList = m.global.live
			    end if	
			    
			    ' Convertir el canal a JSON y añadirlo a la lista
			    channelJson = ContentNodeToJson(content)
			    itemList.Push(channelJson)
			    
			    ' Actualizar la lista global
	    	    m.global.live = itemList
	    	    
	    	    ' Guardar en el registro
        	    regWrite("live", FormatJSON(itemList), ReadManifest().title)
        	    print "Favorito añadido. Total: "; itemList.Count(); " canales"
		    end if
	    else if selectedButtonText = tr("Remove this channel from favorites")
		    updatedItemList = []
		    
		    ' Solo incluir canales que no coincidan con el ID actual
		    for each item in m.global.live
                if item.id <> content.id
				    updatedItemList.Push(item)
                end if
		    end for
		    
		    ' Actualizar la lista global
		    m.global.live = updatedItemList
		    
		    ' Guardar en el registro
		    regWrite("live", FormatJSON(updatedItemList), ReadManifest().title)
		    print "Favorito eliminado. Quedan: "; updatedItemList.Count(); " canales"
		end if
    end if
	m.optionDialog.visible = false
    m.optionDialog.setFocus(false)
	m.EpgGrid.setFocus(true)
end sub

sub UnMarkItemList(content as Object)
	if content.mediatype = "movie"
        updatedItemList = []
        for each item in m.global.movie
            if item.id <> content.id
                updatedItemList.Push(item)
            end if
        end for
	    m.global.movie = updatedItemList
	    regWrite("movie", FormatJSON(updatedItemList), ReadManifest().title)
	    'print "Del to List: "; updatedItemList
	elseif content.mediatype = "series"
        updatedItemList = []
        for each item in m.global.series
            if item.id <> content.id
                updatedItemList.Push(item)
            end if
        end for
	    m.global.series = updatedItemList
	    regWrite("series", FormatJSON(updatedItemList), ReadManifest().title)
	    'print "Del to List: "; updatedItemList
	endif
end sub

function onKeyEvent(key as String, press as Boolean) as Boolean
    handled = false
    if press then
	    Dbg("Press ", key)
		if key = "OK"
		    if not m.EpgGrid.hasFocus() and m.video.hasFocus() then
				InfoShow()				
			end if
            handled = true
        else if key = "left" and not m.categoryList.hasFocus() then
		    expandMenu()
			handled = true
        else if key = "right" and not m.EpgGrid.hasFocus() then
		    collapseMenu()
			handled = true
        else if key = "up" and not m.EpgGrid.hasFocus() then
            handled = moveRowFocus(-1)
        else if key = "down" and not m.EpgGrid.hasFocus() then
            handled = moveRowFocus(1)
        else if key = "play"
            if m.video.state <> "stopped" and m.video.state <> "none" and m.Video.state = "playing" and m.EpgGrid.hasFocus() = true then
		       maximizeVideo()
            else
		       minimizeVideo()
            end if	
            handled = true
        else if key = "options"
            if m.EpgGrid.hasFocus() = true and m.EpgGrid.content.GetChild(m.EpgGrid.channelFocused) <> invalid
			   expandOptions()
            end if			
			handled = true
        else if key = "back" 
		  if (m.Video.state = "playing" or m.Video.state = "buffering") and m.EpgGrid.hasFocus() = false
            minimizeVideo()
            handled = true
		  else if (m.Video.state = "playing" or m.Video.state = "buffering") and m.EpgGrid.hasFocus() = true
			m.Video.control = "stop"
		  else if m.categoryList.hasFocus() and m.EpgGrid.hasFocus() = false
		    collapseMenu()
            handled = true
		  end if
        end if
    end if
    return handled
end function