local toolName = "TNS|DSM Smart RX Telemetry|TNE"
---- #########################################################################                                                                  #
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html               #
---- #                                                                       #
---- # This program is free software; you can redistribute it and/or modify  #
---- # it under the terms of the GNU General Public License version 2 as     #
---- # published by the Free Software Foundation.                            #
---- #                                                                       #
---- # This program is distributed in the hope that it will be useful        #
---- # but WITHOUT ANY WARRANTY; without even the implied warranty of        #
---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
---- # GNU General Public License for more details.                          #
---- #                                                                       #
---- #########################################################################

------------------------------------------------------------------------------ 
-- Developer: Francisco Arzu
 

local DEBUG_ON = false
--

local TEXT_SIZE             = 0 -- NORMAL
local X_COL1_HEADER         = 6
local X_COL1_DATA           = 60
local X_COL2_HEADER         = 170
local X_COL2_DATA           = 220
local Y_LINE_HEIGHT         = 20
local Y_HEADER              = 0
local Y_DATA                = Y_HEADER + Y_LINE_HEIGHT*2 
local X_DATA_LEN            = 80 
local X_DATA_SPACE          = 5




local function getPage(iParam)
  -- get page from 0-based index
  -- {0,1,2,3}: cyclic (1), {4,5,6,7}: tail (2)
  local res = (math.floor(iParam/4)==0) and 0 or 1
  return res
end

local function round(v)
  -- round float
  local factor = 100
  return math.floor(v * factor + 0.5) / factor
end


local function readValue(sensor)
  -- read from sensor, round and return
  local v = getValue(sensor)
  --v = round(v)
  return v
end

local function readValueById(sensor)
  local i = getFieldInfo(sensor)
  if (i==nil) then return nil end 

  local v = getValue(i.id)
  return v
end



local function readBatValue(sensor)
  -- read from sensor, round and return
  local v = getValue(sensor)
  if (v==nil) then return v end

  return string.format("%2.2f",v)
end
 
local function readActiveParamValue(sensor)
  --  read and return a validated active parameter value
  local v = getValue(sensor)
  if (v<1 or v>8) then
    return -1
  end
  return v
end
   


local function drawFlightLogScreen(event)
  -- draw labels and params on screen
  local h = getValue("Hold")
  local activeParam = h-1 -- H 
  
  lcd.clear()
  lcd.drawText (X_COL1_HEADER, Y_HEADER, "Flight Log", TEXT_SIZE + INVERS)

   -- read and return parameters
   local a = getValue("FdeA")
   local b = getValue("FdeB")
   local l = getValue("FdeL")
   local r = getValue("FdeR")
   local f = getValue("FLss")
 
   local titles  = {[0]="A:", "B:", "L:", "R:", "F:", "H:"}
   local values  = {[0]=a,b,l,r,f,h}

   local y = Y_LINE_HEIGHT+Y_DATA
   
   for iParam=0,3 do   -- A,B,L,R 
    -- highlight selected parameter  (rund)
    local attr = ((activeParam%4)==iParam) and INVERS or 0
    -- labels
    local x = X_COL1_HEADER
    local val = titles[iParam]
    lcd.drawText (x, y, val, TEXT_SIZE)
    
    -- Values
    val = values[iParam] 
    x = X_COL1_DATA + X_DATA_LEN
    if (val~=16384) then  -- Active value
        lcd.drawText (x, y, val, attr + TEXT_SIZE + RIGHT)
    end

    y = y + Y_LINE_HEIGHT
  end

  y = Y_LINE_HEIGHT+Y_DATA
  for iParam=4,5 do  -- F, H
    -- labels
    local x = X_COL2_HEADER
    local val = titles[iParam]
    lcd.drawText (x, y, val, TEXT_SIZE + BOLD)
    
    -- Values
    val = values[iParam] 
    x = X_COL2_DATA + X_DATA_LEN
    lcd.drawText (x, y, val, TEXT_SIZE + RIGHT + BOLD)

    y = y + Y_LINE_HEIGHT
  end

  -- Bat 
  y = y + Y_LINE_HEIGHT
  local bat = readBatValue("A2") or "--"
  lcd.drawText (X_COL2_HEADER, y, "Bat:", TEXT_SIZE)
  lcd.drawText (X_COL2_DATA + X_DATA_LEN, y, bat, TEXT_SIZE + RIGHT)
  lcd.drawText (X_COL2_DATA + X_DATA_LEN + X_DATA_SPACE, y, "v", TEXT_SIZE)
  

end






local function Unsigned_to_SInt16(value) 
  if value >= 0x8000 then  -- Negative value??
      return value - 0x10000
  end
  return value
end

local function getDegreesValue(sensor)
  local i = getFieldInfo(sensor)
  if (i==nil) then return "-unk-" end 

  local v = getValue(i.id)
  if v==nil then return "---" end
  local vs = Unsigned_to_SInt16(v)

  return string.format("%0.1f o",vs/10)
end


local function getDecHexValue(sensor)
  local i = getFieldInfo(sensor)
  if (i==nil) then return "-unk-" end 

  local v = getValue(i.id)
  if v==nil then return "---" end
  local vs = Unsigned_to_SInt16(v)

  return string.format("%d (0x%04X)",vs,v)
end

local as3xData = {[0]=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
local function drawAS3XSettings(event, page)
  local s0500 = getDecHexValue("0500")
  local s0502 = getDecHexValue("0502")
  local s0504 = getDecHexValue("0504")
  local s0506 = getDecHexValue("0506")
  local s0508 = getDecHexValue("0508")
  local s050B = getDecHexValue("050B")
  local s050D = getDecHexValue("050D")

  local d0500 = readValueById("0500") or 0
  local flags = bit32.rshift(d0500,8)
  local state = bit32.band(d0500,0xFF)

  local flagsMsg=""
  -- flags bits:  Safe Envelop, ?, Angle Demand, Stab 
  if (bit32.band(flags,0x1)~=0) then flagsMsg=flagsMsg.."AS3X Stab" end
  -- This one, only one should show
  if (bit32.band(flags,0x2)~=0) then flagsMsg=flagsMsg..", Angle Demand" 
  elseif (bit32.band(flags,0x8)~=0) then flagsMsg=flagsMsg..", Safe Envelope" 
  elseif (bit32.band(flags,0x4)~=0) then flagsMsg=flagsMsg..", AS3X Heading" end

  local d0502 = readValueById("0502") or 0  -- 0x?F?S 
  local fm =  bit32.band(bit32.rshift(d0502,8),0xF)  -- 0,1,2

  local axis =  bit32.band(d0502,0xF)  -- 0=Gains,1=Headings,2=Angle Limits (cointinus iterating to provide all values)

  local d0504 = readValueById("0504") or 0  
  local d0506 = readValueById("0506") or 0  
  local d0508 = readValueById("0508") or 0  

  local d0 = bit32.rshift(d0504,8)
  local d1 = bit32.band(d0504,0xFF)
  local d2 = bit32.rshift(d0506,8)
  local d3 = bit32.band(d0506,0xFF)
  local d4 = bit32.rshift(d0508,8)
  local d5 = bit32.band(d0508,0xFF)

  --axis: 0=Gains+Headings (RG,PG,YG,RH,PH,YH), 1=Safe Gains (R,P,Y),2=Angle Limits(L,R,U,D) 
  --Constantly changing from 0..2 to represent different data, thats why we have to store the values
  --in a script/global variable, and not local to the function
  local s = axis*6   
  as3xData[s+0] = d0
  as3xData[s+1] = d1
  as3xData[s+2] = d2
  as3xData[s+3] = d3
  as3xData[s+4] = d4
  as3xData[s+5] = d5


  lcd.clear()
  lcd.drawText (0,0, "AS3X/SAFE Settings", TEXT_SIZE + INVERS)

  local y = Y_DATA
  -- Flight Mode
  lcd.drawText (X_COL1_HEADER,y, "FM: "..(fm+1), TEXT_SIZE)
  lcd.drawText (X_COL1_DATA+X_DATA_LEN*0.3,y, "Flags: "..flags, TEXT_SIZE)
  lcd.drawText (X_COL2_HEADER+X_DATA_LEN*0.3,y, "State: "..state, TEXT_SIZE)

  y = y + Y_LINE_HEIGHT
  lcd.drawText (X_COL1_HEADER,y, flagsMsg, TEXT_SIZE)

  y = y + Y_LINE_HEIGHT

  if (page==1) then
    lcd.drawText (X_COL1_HEADER+X_DATA_LEN*0.3,y, "AS3X Gains", TEXT_SIZE+BOLD)
    lcd.drawText (X_COL2_HEADER+X_DATA_LEN*0.3,y, "AS3X Headings", TEXT_SIZE+BOLD)
    
    y = y + Y_LINE_HEIGHT
    lcd.drawText (X_COL1_HEADER,y, "Roll:", TEXT_SIZE)
    lcd.drawText (X_COL1_DATA+X_DATA_LEN,y, as3xData[0], TEXT_SIZE + RIGHT) -- Roll G 
    lcd.drawText (X_COL2_DATA+X_DATA_LEN,y, as3xData[3], TEXT_SIZE + RIGHT) -- Roll H 

    y = y + Y_LINE_HEIGHT
    lcd.drawText (X_COL1_HEADER,y, "Pitch:", TEXT_SIZE)
    lcd.drawText (X_COL1_DATA+X_DATA_LEN,y,as3xData[1], TEXT_SIZE + RIGHT)  -- Pitch G
    lcd.drawText (X_COL2_DATA+X_DATA_LEN,y, as3xData[4], TEXT_SIZE + RIGHT) -- Pitch H 

    y = y + Y_LINE_HEIGHT
    lcd.drawText (X_COL1_HEADER,y, "Yaw:", TEXT_SIZE)
    lcd.drawText (X_COL1_DATA+X_DATA_LEN,y, as3xData[2], TEXT_SIZE + RIGHT) -- Yaw G
    lcd.drawText (X_COL2_DATA+X_DATA_LEN,y, as3xData[5], TEXT_SIZE + RIGHT) -- Yaw H
  end


  if (page==2) then
    local x_data1 = X_COL1_DATA+X_DATA_LEN
    local x_data2 = X_COL2_HEADER+X_DATA_LEN*1.6

    lcd.drawText (X_COL1_HEADER+X_DATA_LEN*0.3,y, "SAFE Gains", TEXT_SIZE+BOLD)
    lcd.drawText (X_COL2_HEADER+X_DATA_LEN*0.1,y, "Angle Limits", TEXT_SIZE+BOLD)
  
    y = y + Y_LINE_HEIGHT
    lcd.drawText (X_COL1_HEADER,y, "Roll:", TEXT_SIZE)
    lcd.drawText (x_data1,y, as3xData[6], TEXT_SIZE + RIGHT)

    lcd.drawText (X_COL2_HEADER,y, "Roll R:", TEXT_SIZE)
    lcd.drawText (x_data2,y, as3xData[12], TEXT_SIZE + RIGHT)


    y = y + Y_LINE_HEIGHT
    lcd.drawText (X_COL1_HEADER,y, "Pitch:", TEXT_SIZE)
    lcd.drawText (x_data1,y,as3xData[7], TEXT_SIZE + RIGHT)

    lcd.drawText (X_COL2_HEADER,y, "Roll L:", TEXT_SIZE)
    lcd.drawText (x_data2,y,as3xData[13], TEXT_SIZE + RIGHT)


    y = y + Y_LINE_HEIGHT
    lcd.drawText (X_COL1_HEADER,y, "Yaw:", TEXT_SIZE)
    lcd.drawText (x_data1,y, as3xData[8], TEXT_SIZE + RIGHT)

    lcd.drawText (X_COL2_HEADER,y, "Pitch U:", TEXT_SIZE)
    lcd.drawText (x_data2,y, as3xData[14], TEXT_SIZE + RIGHT)

    y = y + Y_LINE_HEIGHT
    lcd.drawText (X_COL2_HEADER,y, "Pitch D:", TEXT_SIZE)
    lcd.drawText (x_data2,y, as3xData[15], TEXT_SIZE + RIGHT)
  end

  -- Debug Values 
  if (DEBUG_ON) then
      local titles = {[0]=
      "0500","0502","0504","0506","0508","050B","050D"}

      local values = {[0]=
      s0500,s0502,s0504,s0506,s0508,s050B,s050D }

      y = Y_LINE_HEIGHT*2 + Y_HEADER
      for iParam=0,#titles do   -- ??
        -- labels
        local x = X_COL1_HEADER+250
        local val = titles[iParam]
        lcd.drawText (x, y, val, TEXT_SIZE)
        
        val = values[iParam] or "--" 
        x = X_COL1_DATA+250
        lcd.drawText (x, y, val, TEXT_SIZE)

        y = y + Y_LINE_HEIGHT
      end
  end 
end

local function drawAS3XSettingsP1(event)
  drawAS3XSettings(event, 1)
end

local function drawAS3XSettingsP2(event)
  drawAS3XSettings(event, 2)
end


local function doFloat(v)
  if v==nil then return 0.0 end

  local vs = string.format("%1.2f",v)
  
  return vs + 0.0
end


local ESC_Title={[0]="","RPM:","Volts:","Motor:","Mot Out:","Throttle:","FET Temp:", "BEC V:", "BEC T:", "BEC A:"}
local ESC_uom={[0]="","","V","A","%","%","C", "V","C","A"}
local ESC_Status={[0]=0,0,0,0,0,0,0,0,0,0,0}
local ESC_Min={[0]=0,0,0,0,0,0,0,0,0,0,0}
local ESC_Max={[0]=0,0,0,0,0,0,0,0,0,0,0}

local function drawESCStatus(event)
  lcd.clear()
  ESC_Status[1] = getValue("Erpm") -- RPM
  ESC_Status[2] = doFloat(getValue("EVIN")) -- Volts
  ESC_Status[3] = doFloat(getValue("ECUR")) -- Current
  ESC_Status[4] = doFloat(getValue("EOUT"))*10  -- % Output
  ESC_Status[5] = doFloat(getValue("ETHR"))*10  -- Throttle % (EOUT)
  ESC_Status[6] = getValue("TFET")  -- Temp FET
  
  ESC_Status[7] = doFloat(getValue("VBEC")) -- Volts BEC
  ESC_Status[8] = getValue("TBEC")  -- Temp BEC
  ESC_Status[9] = doFloat(getValue("CBEC")) -- Current BEC

  for i=1,9 do
    if (ESC_Status~=nil) then
      if (ESC_Min[i]==0) then 
          ESC_Min[i]=ESC_Status[i]
      else 
          ESC_Min[i] = math.min(ESC_Min[i],ESC_Status[i])
      end

      ESC_Max[i] = math.max(ESC_Max[i],ESC_Status[i])
    end
  end
 
  lcd.drawText (0,0, "ESC", TEXT_SIZE+INVERS)

  local y = 0
  local x_data = X_COL1_DATA+X_DATA_LEN*1.5
  local x_data2 = X_COL2_DATA+X_DATA_LEN*0.5
  local x_data3 = x_data2 + X_DATA_LEN*0.8


  lcd.drawText (x_data,y , "Status", TEXT_SIZE+BOLD+RIGHT)
  lcd.drawText (x_data2,y, "Min", TEXT_SIZE+BOLD+RIGHT)
  lcd.drawText (x_data3,y, "Max", TEXT_SIZE+BOLD+RIGHT)
  
  y = Y_DATA
  for i=1,9 do
      lcd.drawText (X_COL1_HEADER,y, ESC_Title[i], TEXT_SIZE + BOLD)

      lcd.drawText (x_data,y, ESC_Status[i] or "--", TEXT_SIZE + RIGHT)
      lcd.drawText (x_data + X_DATA_SPACE,y, ESC_uom[i], TEXT_SIZE)

      lcd.drawText (x_data2,y, ESC_Min[i] or "--", TEXT_SIZE + RIGHT)
      lcd.drawText (x_data3,y, ESC_Max[i] or "--", TEXT_SIZE + RIGHT)
      y = y + Y_LINE_HEIGHT
  end
end


local function drawBATStatus(event)
  local Title={[0]="","Bat:","Temp:","Rem :","Curr:","Used:","Imbal:","Cycles:", "RX:", "BCpT?:"}
  local uom={[0]="","V","C","%","mAh","mAh","mV","", "V",""}
  local Values={[0]=0,0,0,0,0,0,0,0,0,0,0}
  local CellValues={[0]=0,0,0,0,0,0,0,0,0,0,0}

  lcd.clear()

  local ESC_Volts = getValue("EVIN") or 0 -- Volts
  local ESC_Current = getValue("ECUR") or 0 -- Current
  
  Values[1] = 0 -- compute later
  Values[2] = getValue("BTmp") -- Current  (C)
  Values[3] = nil -- Remaining???
  Values[4] = getValue("BCur") -- Current  (mAh)
  Values[5] = getValue("BUse") -- Current Used  (mAh)
  Values[6] = getValue("CLMa") -- 0.0  (mV)  Imbalance
  Values[7] = getValue("Cycl") -- Cycles
  Values[8] = readBatValue("A2") -- v
  Values[9] = getValue("BCpT") -- Current  (mAh) ????


  --- Total Voltange Calculation
  local VTotal=0
  for i=1,10 do
      CellValues[i] = getValue("Cel"..i)
      VTotal = VTotal + CellValues[i]
  end

  if (VTotal==0) then -- No Inteligent Battery,use intelligent ESC if any
    VTotal = ESC_Volts
    Values[4] = string.format("%d",ESC_Current * 1000)
  end

  Values[1] = string.format("%2.2f",VTotal)

  --- SCREEN
  
  lcd.drawText (X_COL1_HEADER,0, "Battery Stats", TEXT_SIZE+INVERS)
  


  local y = Y_DATA
  local x_data = X_COL1_DATA+X_DATA_LEN+X_DATA_SPACE*3
  for i=2,9 do
      lcd.drawText (X_COL1_HEADER, y, Title[i], TEXT_SIZE + BOLD)
      lcd.drawText (x_data, y, Values[i] or "--", TEXT_SIZE + RIGHT)
      lcd.drawText (x_data+X_DATA_SPACE, y, uom[i], TEXT_SIZE)
      y = y + Y_LINE_HEIGHT
  end

  y = Y_DATA
  x_data = X_COL2_DATA+X_DATA_LEN+X_DATA_SPACE*5
  for i=1,8 do
      if ((CellValues[i] or 0) > 0) then
        lcd.drawText (X_COL2_HEADER+X_DATA_LEN/2,y, "Cel "..i..":", TEXT_SIZE + BOLD)
        lcd.drawText (x_data,y, string.format("%2.2f",CellValues[i] or 0), TEXT_SIZE + RIGHT)
        lcd.drawText (x_data+X_DATA_SPACE,y, "v", TEXT_SIZE)
      end
      y = y + Y_LINE_HEIGHT
  end

  lcd.drawText (X_COL2_HEADER+X_DATA_LEN/2,0, Title[1], TEXT_SIZE + INVERS + BOLD)
  lcd.drawText (x_data,0, string.format("%2.2f",Values[1] or 0), TEXT_SIZE + INVERS+ RIGHT)
  lcd.drawText (x_data+X_DATA_SPACE, 0, uom[1], TEXT_SIZE + INVERS)
end




local function openTelemetryRaw(i2cId)
    --Init telemetry  (Spectrun Telemetry Raw STR)
    multiBuffer( 0, string.byte('S') )
    multiBuffer( 1, string.byte('T') )
    multiBuffer( 2, string.byte('R') ) 
    multiBuffer( 3, i2cId ) -- Monitor this teemetry data
    multiBuffer( 4, 0 ) -- Allow to get Data
end

local function closeTelemetryRaw()
    multiBuffer(0, 0) -- Destroy the STR header 
    multiBuffer(3, 0) -- Not requesting any Telementry ID
end

local lineText = {nil}
local I2C_TEXT_GEN = 0x0C
local function drawTextGen(event)
  if (multiBuffer(0)~=string.byte('S')) then -- First time run???
    openTelemetryRaw(I2C_TEXT_GEN) -- I2C_ID for TEXT_GEN
    lineText = {nil}
  end

  -- Proces TEXT GEN Telementry message
  if multiBuffer( 4 ) == I2C_TEXT_GEN then -- Specktrum Telemetry ID of data received
    local instanceNo = multiBuffer( 5 )
    local lineNo = multiBuffer( 6 )
    local ch_array = {}
    for i=0,13 do
      ch_array[i] = string.char(multiBuffer( 7 + i ))
    end

    multiBuffer( 4, 0 ) -- Clear Semaphore, to notify that we fully process the current message
    lineText[lineNo]=table.concat(ch_array,nil,0) -- Concatenate all characters to create message
  end

  lcd.clear()
  -- Header
  if (lineText[0]) then
    lcd.drawText (X_COL1_HEADER,0,  "   "..lineText[0].."   ", TEXT_SIZE + BOLD + INVERS)
  else
    lcd.drawText (X_COL1_HEADER,0, "TextGen", TEXT_SIZE+INVERS)
  end

  -- Menu lines
  local y = Y_DATA
  for i=1,8 do
    if (lineText[i]) then
      lcd.drawText (X_COL1_HEADER,y,  lineText[i], TEXT_SIZE)
    end
    y = y + Y_LINE_HEIGHT
  end

  if event == EVT_VIRTUAL_EXIT then -- Exit?? Clear menu data
    closeTelemetryRaw()
  end
end


local telPage = 1
local telPageSelected = 0
local pageTitle = {[0]="Main", "AS3X Settings", "SAFE Settings", "ESC Status", "Battery Status","TextGen","Flight Log"}

local function drawMainScreen(event) 
  lcd.clear()
  lcd.drawText (X_COL1_HEADER, Y_HEADER, "Main Telemetry (Smart RXs)", TEXT_SIZE + INVERS)

  for iParam=1,#pageTitle do    
    -- highlight selected parameter
    local attr = (telPage==iParam) and INVERS or 0

    -- set y draw coord
    local y = (iParam)*Y_LINE_HEIGHT+Y_DATA 
    
    -- labels
    local x = X_COL1_HEADER
    local val = pageTitle[iParam]
    lcd.drawText (x, y, val, attr + TEXT_SIZE)
  end

  if event == EVT_VIRTUAL_PREV then
    if (telPage>1) then telPage = telPage - 1 end
  elseif event == EVT_VIRTUAL_NEXT then
    if (telPage<#pageTitle) then telPage = telPage + 1 end
  elseif event == EVT_VIRTUAL_ENTER then
    telPageSelected = telPage
  end
end


local pageDraw  = {[0]=drawMainScreen, drawAS3XSettingsP1, drawAS3XSettingsP2, drawESCStatus, drawBATStatus, drawTextGen, drawFlightLogScreen}

local function run_func(event)
  if event == nil then
    error("Cannot be run as a model script!")
    return 2
  end

  -- draw specific page 
  pageDraw[telPageSelected](event)

  if event == EVT_VIRTUAL_EXIT then
    if (telPageSelected==0) then return 1 end  -- on Main?? Exit Script 
    telPageSelected = 0  -- any page, return to Main 
  end

  return 0
end

local function init_func()

  if (LCD_W <= 128 or LCD_H <=64) then -- Smaller Screens 
    TEXT_SIZE = SMLSIZE 
    X_COL1_HEADER         = 0
    X_COL1_DATA           = 20

    X_COL2_HEADER         = 60
    X_COL2_DATA           = 90

    X_DATA_LEN            = 28 
    X_DATA_SPACE          = 1


    Y_LINE_HEIGHT         = 8
    Y_DATA                = Y_HEADER + Y_LINE_HEIGHT

  end
end

return { run=run_func,  init=init_func  }
