---- #########################################################################
---- #                                                                       #
---- # Copyright (C) OpenTX                                                  #
-----#                                                                       #
---- # 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.                          #
---- #                                                                       #
---- #########################################################################

------------------------------------------------------------------------------
-- This scrip does the airplane Setup similar to how a a Spektrum radio does
-- it. You can select the plane type, the Wing type, etc.
-- This settings are needed for ForwardProgramming to send the TX aircraft 
-- configuration to the RX when in Initial Setup
-- Author: Francisco Arzu 
------------------------------------------------------------------------------

local DEBUG_ON, SIMULATION_ON = ... -- Get DebugON from parameters
local SETUP_LIB_VERSION = "0.54"

local DATA_PATH = "/MODELS/DSMDATA" -- Path to store model settings files
local dsmLib = assert(loadScript("/SCRIPTS/TOOLS/DSMLIB/DsmFwPrgLib.lua"))(DEBUG_ON)

local PHASE = dsmLib.PHASE
local LINE_TYPE = dsmLib.LINE_TYPE
local CH_TYPE   = dsmLib.CH_TYPE
local MODEL     = dsmLib.MODEL

local AIRCRAFT_TYPE = {
    PLANE   = 0,
    HELI    = 1,
    GLIDER  = 2,
    DRONE   = 3
}
local aircraft_type_text = {[0]="Plane","Heli","Glider","Drone"}

local WING_TYPE = {
    AIL_1       = 0, --1
    AIL_2       = 1, --2
    FLAPERON    = 2, --2
    AIL_1_FLP_1 = 3, --2
    AIL_2_FLP_1 = 4, --3
    AIL_2_FLP_2 = 5, --4
    ELEVON_A    = 6, --2
    ELEVON_B    = 7  --2
}
local wing_type_text = {[0]="Normal","Dual Ail","Flapperon", "Ail + Flp","Dual Ail + Flp","Dual Ail/Flp","Elevon A","Elevon B"}

local TAIL_TYPE = {
    RUD_1        = 0,  -- 1
    RUD_1_ELEV_1 = 1,  -- 2
    RUD_1_ELEV_2 = 2,  -- 3
    RUD_2_ELEV_1 = 3,  -- 3
    RUD_2_ELEV_2 = 4,  -- 4
    VTAIL_A      = 5,  -- 2
    VTAIL_B      = 6,  -- 2
    TRAILERON_A  = 7,  -- 3
    TRAILERON_B  = 8,  -- 3
}
local tail_type_text = {[0]="Rud Only","Normal","Rud + Dual Ele","Dual Rud + Elv","Dual Rud/Ele","VTail A","VTail B","Traileron A","Traileron B"}

local CH_MODE_TYPE = {
    NORMAL      = 0,
    REVERSE     = 1,
    USE_TX      = 3
}

local PORT = {
    PORT1 = 0,
    PORT2 = 1,
    PORT3 = 2,
    PORT4 = 3,
    PORT5 = 4,
    PORT6 = 5,
    PORT7 = 6,
    PORT8 = 7,
    PORT9 = 8,
    PORT10 = 9,
}

local MEMU_VAR = {
        AIRCRAFT_TYPE = 1001,
        WING_TYPE     = 1002,
        TAIL_TYPE     = 1003,
        
        CH_BASE       = 1010,
        CH_THR        = 1010,

        CH_L_AIL      = 1011,
        CH_R_AIL      = 1012,
        CH_L_FLP      = 1013,
        CH_R_FLP      = 1014,

        CH_L_RUD      = 1015,
        CH_R_RUD      = 1016,
        CH_L_ELE      = 1017,
        CH_R_ELE      = 1018,

        PORT_BASE       = 1020,
        PORT1_MODE      = 1020,
        PORT2_MODE      = 1021,
        PORT3_MODE      = 1022,
        PORT4_MODE      = 1023,
        PORT5_MODE      = 1024,
        PORT6_MODE      = 1025,
        PORT7_MODE      = 1026,
        PORT8_MODE      = 1027,
        PORT9_MODE      = 1028,
        PORT10_MODE     = 1029,

        DATA_END        = 1040
}

local SetupLib = {}

-- MENU DATA Management
local MENU_DATA = {}            -- Store the variables used in the Menus.
local menuDataChanged = false   -- Flag to notify if any data has changed
local currAircraftType = -1     -- Current AircraftType selected, and to detect change
local currTailType = -1         -- Current WingType selected, and to detect change
local currWingType = -1         -- Current TailType selected, and to detect change

local lastGoodMenu=0            

-------------------  Model Setup Helper functions ----------------------

local function printChannelSummary()
    -- Summary
    print("CHANNEL INFORMATION")
    print("Aircraft:".. (aircraft_type_text[currAircraftType] or "--"))
    print("Wing Type:".. (wing_type_text[currWingType] or "--"))
    print("Tail Type:".. (tail_type_text[currTailType] or "--"))
    print("Thr:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_THR]  or 30)] or "--"))   -- use fake ch30 for non existing channels 
    print("LAil:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_L_AIL] or 30)] or "--"))
    print("RAil:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_R_AIL] or 30)] or "--"))
    print("LFlp:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_L_FLP] or 30)] or "--"))
    print("RFlp:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_R_FLP] or 30)] or "--"))
    print("LEle:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_L_ELE] or 30)] or "--"))
    print("REle:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_R_ELE] or 30)] or "--"))
    print("LRud:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_L_RUD] or 30)] or "--"))
    print("RRud:".. (MODEL.PORT_TEXT[(MENU_DATA[MEMU_VAR.CH_R_RUD] or 30)] or "--"))   
end

local function printServoReverseInfo()
    print("SERVO Normal/Reverse INFORMATION")
    for i=0,10 do
        local s="--"
        if (MENU_DATA[MEMU_VAR.PORT1_MODE+i] or 0) == 0 then s="NORMAL" else s="REVERSE" end
        print(string.format("Port%d:  %s", i+1, s))
    end
end

local function ST_PlaneWingInit(wingType) 
    print("Change Plane WingType:"..wing_type_text[wingType])

    currWingType = wingType

    MENU_DATA[MEMU_VAR.WING_TYPE] = wingType

    -- Clear all Wing Data 
    MENU_DATA[MEMU_VAR.CH_L_AIL] = nil
    MENU_DATA[MEMU_VAR.CH_R_AIL] = nil
    MENU_DATA[MEMU_VAR.CH_L_FLP] = nil
    MENU_DATA[MEMU_VAR.CH_R_FLP] = nil

    MENU_DATA[MEMU_VAR.CH_THR] = PORT.PORT1
    
    -- Default Channel Assisgments for each Wing type

    if (wingType==WING_TYPE.AIL_1) then
        MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT2
    elseif (wingType==WING_TYPE.AIL_2) then
        MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT6
        MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
    elseif (wingType==WING_TYPE.FLAPERON) then
        MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT6
        MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
    elseif (wingType==WING_TYPE.AIL_1_FLP_1) then
        MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT2
        MENU_DATA[MEMU_VAR.CH_L_FLP] = PORT.PORT6
    elseif (wingType==WING_TYPE.AIL_2_FLP_1) then
        MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT6
        MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
        MENU_DATA[MEMU_VAR.CH_L_FLP] = PORT.PORT5
    elseif (wingType==WING_TYPE.AIL_2_FLP_2) then
        MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT6
        MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
        MENU_DATA[MEMU_VAR.CH_R_FLP] = PORT.PORT5
        MENU_DATA[MEMU_VAR.CH_L_FLP] = PORT.PORT7
    elseif (wingType==WING_TYPE.ELEVON_A) then
        MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT2
        MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT3
    elseif (wingType==WING_TYPE.ELEVON_B) then
        MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT3
        MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
    else -- Assume normal 
       print("ERROR: Invalid Wing Type")
    end 


    printChannelSummary()
end

local function ST_PlaneTailInit(tailType) 
    if (MENU_DATA[MEMU_VAR.WING_TYPE]==WING_TYPE.ELEVON_A) then
        tailType = TAIL_TYPE.RUD_1 -- Delta only have ruder  
    end

    print("Change Plane Tail Type:"..tail_type_text[tailType])

    currTailType = tailType

    -- Clear all data for Tail 
    MENU_DATA[MEMU_VAR.TAIL_TYPE] = tailType
    MENU_DATA[MEMU_VAR.CH_L_ELE] = nil
    MENU_DATA[MEMU_VAR.CH_R_ELE] = nil
    MENU_DATA[MEMU_VAR.CH_L_RUD]  = nil
    MENU_DATA[MEMU_VAR.CH_R_RUD] = nil

    -- Setup Channels for different Tail types 
    if (tailType == TAIL_TYPE.RUD_1) then
        MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4
    elseif (tailType == TAIL_TYPE.RUD_1_ELEV_1) then
        MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT3
        MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4
    elseif (tailType == TAIL_TYPE.RUD_1_ELEV_2) then
        MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT5
        MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3
        MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4
    elseif (tailType == TAIL_TYPE.RUD_2_ELEV_1) then
        MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT3
        MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT5
        MENU_DATA[MEMU_VAR.CH_R_RUD] = PORT.PORT4
    elseif (tailType == TAIL_TYPE.RUD_2_ELEV_2) then
        MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT5
        MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3
        MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT6
        MENU_DATA[MEMU_VAR.CH_R_RUD] = PORT.PORT4
    elseif (tailType == TAIL_TYPE.VTAIL_A) then
        MENU_DATA[MEMU_VAR.CH_L_ELE]  = PORT.PORT4
        MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3
    elseif (tailType == TAIL_TYPE.VTAIL_B) then
        MENU_DATA[MEMU_VAR.CH_L_ELE]   = PORT.PORT3
        MENU_DATA[MEMU_VAR.CH_R_ELE]  = PORT.PORT4
    elseif (tailType == TAIL_TYPE.TRAILERON_A) then
        MENU_DATA[MEMU_VAR.CH_L_ELE]  = PORT.PORT5
        MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3
    elseif (tailType == TAIL_TYPE.TRAILERON_B) then
        MENU_DATA[MEMU_VAR.CH_L_ELE]   = PORT.PORT5
        MENU_DATA[MEMU_VAR.CH_R_ELE]  = PORT.PORT3
    else -- Assume Normal 
        print("ERROR:invalid Tail Type")
    end

    printChannelSummary()
end

local function ST_GliderWingInit(wingType) 
    print("Change Glider WingType:"..wing_type_text[wingType])

    currWingType = wingType

    MENU_DATA[MEMU_VAR.WING_TYPE] = wingType

    -- Clear all Wing Data 
    MENU_DATA[MEMU_VAR.CH_L_AIL] = nil
    MENU_DATA[MEMU_VAR.CH_R_AIL] = nil
    MENU_DATA[MEMU_VAR.CH_L_FLP] = nil
    MENU_DATA[MEMU_VAR.CH_R_FLP] = nil
    MENU_DATA[MEMU_VAR.CH_THR] = PORT.PORT6
    
    -- Default Channel Assisgments for each Wing type

    if (wingType==WING_TYPE.AIL_1) then
        MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT1
    elseif (wingType==WING_TYPE.AIL_2) then
        MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT1
        MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
    elseif (wingType==WING_TYPE.AIL_2_FLP_1) then
        MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT1
        MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
        MENU_DATA[MEMU_VAR.CH_L_FLP] = PORT.PORT5
    elseif (wingType==WING_TYPE.AIL_2_FLP_2) then
        MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT1
        MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
        MENU_DATA[MEMU_VAR.CH_L_FLP] = PORT.PORT5
        MENU_DATA[MEMU_VAR.CH_R_FLP] = PORT.PORT6
        MENU_DATA[MEMU_VAR.CH_THR] = PORT.PORT7
    elseif (wingType==WING_TYPE.ELEVON_A) then
        MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT1
        MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT2
    elseif (wingType==WING_TYPE.ELEVON_B) then
        MENU_DATA[MEMU_VAR.CH_L_AIL] = PORT.PORT2
        MENU_DATA[MEMU_VAR.CH_R_AIL] = PORT.PORT1
    else -- Assume normal 
        print("ERROR: Invalid Wing Type")
    end 

    printChannelSummary()
end

local function ST_GliderTailInit(tailType) 
    if (MENU_DATA[MEMU_VAR.WING_TYPE]==WING_TYPE.ELEVON_A) then
        tailType = TAIL_TYPE.RUD_1 -- Delta only have ruder  
    end

    print("Change Glider Tail Type:"..tail_type_text[tailType])

    currTailType = tailType

    -- Clear all data for Tail 
    MENU_DATA[MEMU_VAR.TAIL_TYPE] = tailType
    MENU_DATA[MEMU_VAR.CH_L_ELE] = nil
    MENU_DATA[MEMU_VAR.CH_R_ELE] = nil
    MENU_DATA[MEMU_VAR.CH_L_RUD]  = nil
    MENU_DATA[MEMU_VAR.CH_R_RUD] = nil

    -- Setup Channels for different Tail types 
    if (tailType == TAIL_TYPE.RUD_1) then
        MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4
    elseif (tailType == TAIL_TYPE.RUD_1_ELEV_1) then
        MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT3
        MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4
    elseif (tailType == TAIL_TYPE.VTAIL_A) then
        MENU_DATA[MEMU_VAR.CH_L_ELE]  = PORT.PORT4
        MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3
    elseif (tailType == TAIL_TYPE.VTAIL_B) then
        MENU_DATA[MEMU_VAR.CH_L_ELE]   = PORT.PORT3
        MENU_DATA[MEMU_VAR.CH_R_ELE]  = PORT.PORT4
    else -- Assume Normal 
        print("ERROR: Invalid Tail Type")
    end

    printChannelSummary()
end


local function ST_AircraftInit(aircraftType)
    MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE] = aircraftType
    currAircraftType = aircraftType

    print("Change Aircraft:".. aircraft_type_text[aircraftType])

    -- Setup Default Aircraft Wing/Tail 
    if (aircraftType==AIRCRAFT_TYPE.PLANE) then
        ST_PlaneWingInit(WING_TYPE.AIL_1)
        ST_PlaneTailInit(TAIL_TYPE.RUD_1_ELEV_1)
    elseif (aircraftType==AIRCRAFT_TYPE.GLIDER) then
        ST_GliderWingInit(WING_TYPE.AIL_1)
        ST_GliderTailInit(TAIL_TYPE.RUD_1_ELEV_1)
    else 
        ST_PlaneWingInit(WING_TYPE.AIL_1)
        ST_PlaneTailInit(TAIL_TYPE.RUD_1_ELEV_1)
    end

   
end


-- Setup Initial Default Data for the Menus
local function ST_Default_Data()
    print("Initializing Menu DATA")
    ST_AircraftInit(AIRCRAFT_TYPE.PLANE)

    print("Initializing Servo Reverse from TX output settings")

    MENU_DATA[MEMU_VAR.PORT1_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT1].revert
    MENU_DATA[MEMU_VAR.PORT2_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT2].revert
    MENU_DATA[MEMU_VAR.PORT3_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT3].revert
    MENU_DATA[MEMU_VAR.PORT4_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT4].revert
    MENU_DATA[MEMU_VAR.PORT5_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT5].revert
    MENU_DATA[MEMU_VAR.PORT6_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT6].revert
    MENU_DATA[MEMU_VAR.PORT7_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT7].revert
    MENU_DATA[MEMU_VAR.PORT8_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT8].revert
    MENU_DATA[MEMU_VAR.PORT9_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT9].revert
    MENU_DATA[MEMU_VAR.PORT10_MODE] = CH_MODE_TYPE.NORMAL + MODEL.modelOutputChannel[PORT.PORT10].revert

    printServoReverseInfo()

end

-----------------------  FILE MANAGEMENT ---------------------------------------------
-- Create a fairly unique name for a model..combination of name and a hash 
-- TODO: Check with ETX why we can't get the filename used to store the model info
-- Improvement request??

local function hashName(mName)
    local c=10000;

    local prefix = string.gsub(mName,"%.","_") -- Change any "." to "_"
    prefix = string.gsub(prefix,"% ","_") -- Change any space to "_"
    prefix = string.sub(prefix,1,5) -- Take the first 5 characters

    -- Simple Hash of the Model Name adding each character 
    for i = 1, #mName do
        local ch = string.byte(mName,i,i)
        c=c+ch
    end

    return (prefix .. c) -- Return Prefix + Hash
end

-- Load Menu Data from a file 
function ST_LoadFileData() 
    local fname = hashName(MODEL.modelName)..".txt"

    print("Loading File:"..fname)

    local dataFile = io.open(DATA_PATH .. "/".. fname, "r")  -- read File 
    -- cannot read file???
    if (dataFile==nil) then return 0 end

    local line = io.read(dataFile, 5000)
    io.close(dataFile)

    if #line == 0 then return 0 end -- No data??

    -- Process the input, each line is "Var_Id : Value" format 
    -- Store it into MANU_DATA
    local i=0
    for k, v in string.gmatch(line, "(%d+):(%d+)") do
        --print(string.format("Read  MENU_DATA[%d]:[%d]",k, v))
        MENU_DATA[k+0]=v+0 -- do aritmentic to convert string to number
        i=i+1
    end

    -- Get the basic info
    currAircraftType = MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE]
    currWingType = MENU_DATA[MEMU_VAR.WING_TYPE]
    currTailType = MENU_DATA[MEMU_VAR.TAIL_TYPE]

    print("Validation")
    print(string.format("AIRCRAFT_TYPE(%d)=%s", MEMU_VAR.AIRCRAFT_TYPE,aircraft_type_text[currAircraftType]))
    print(string.format("WING_TYPE(%d)=%s", MEMU_VAR.WING_TYPE, wing_type_text[currWingType]))
    print(string.format("TAIL_TYPE(%d)=%s", MEMU_VAR.TAIL_TYPE, tail_type_text[currTailType]))
   
    printChannelSummary()
    printServoReverseInfo()


    -- No need to save right now
    menuDataChanged = false

    -- Return 0 if no lines processed, 1 otherwise
    if (i > 0) then return 1 else return 0 end
end

-- Saves MENU_DATA to a file
function ST_SaveFileData() 
    local fname = hashName(MODEL.modelName)..".txt"

    print("Saving File:"..fname)
    local dataFile = assert(io.open(DATA_PATH .. "/" .. fname, "w"),"Please create "..DATA_PATH.." folder")  -- write File 
    
    -- Foreach MENU_DATA with a value write Var_Id:Value into file
    for i = 0, MEMU_VAR.DATA_END do
        if (MENU_DATA[i]~=nil) then
            --print(string.format("Write MENU_DATA[%s] : %s",i,MENU_DATA[i]))
            io.write(dataFile,string.format("%s:%s\n",i,MENU_DATA[i]))
        end
    end
    io.close(dataFile)
    menuDataChanged = false
end

-- This Creates the Servo Settings that will be used to pass to 
-- Forward programming
local function CreateDSMPortChannelInfo()
    local function ApplyWingMixA(b2)
        -- ELEVON
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return 0x20 end; -- 0x03
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.REVERSE) then return 0x50 end; -- 0x23
       
       -- Default normal/reverse behaviour 
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return 0x00 end; -- 0x83
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE+CH_TYPE.REVERSE) then return 0x70 end; -- 0xA3
    end

    local function ApplyWingMixB(b2)
        -- ELEVON 
        -- Default normal/reverse behaviour 
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return 0x00 end; -- 0x03
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.REVERSE) then return 0x70 end; -- 0x23

        -- Difference with B 
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return 0x20 end; -- 0x83
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE+CH_TYPE.REVERSE) then return 0x50 end; -- 0xA3
   end

    local function ApplyTailMixA(b2)
        -- VTAIL
        -- Default normal/reverse behaviour 
        if (b2==CH_TYPE.RUD+CH_TYPE.ELE) then return 0x00 end; -- 0x06
        if (b2==CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.REVERSE) then return 0x70 end; -- 0x26

        if (b2==CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.SLAVE) then return 0x20 end; -- 0x86
        if (b2==CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.SLAVE+CH_TYPE.REVERSE) then return 0x50 end; -- 0xA6

        --TRAILERON
         -- Default normal/reverse behaviour 
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return 0x00 end; -- 0x03
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.REVERSE) then return 0x70 end; -- 0x23

        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return 0x10 end; -- 0x83
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE+CH_TYPE.REVERSE) then return 0x60 end; -- 0xA3

    end

    local function ApplyTailMixB(b2)
        -- VTAIL 
        -- Default normal/reverse behaviour 
        if (b2==CH_TYPE.RUD+CH_TYPE.ELE) then return 0x00 end; -- 0x06
        if (b2==CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.REVERSE) then return 0x70 end; -- 0x26

        if (b2==CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.SLAVE) then return 0x40 end; -- 0x86
        if (b2==CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.SLAVE+CH_TYPE.REVERSE) then return 0x30 end; -- 0xA6

        --TAILERON
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return 0x10 end; -- 0x03
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.REVERSE) then return 0x60 end; -- 0x23

        -- Default normal/reverse behaviour 
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return 0x00 end; -- 0x83
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE+CH_TYPE.REVERSE) then return 0x70 end; -- 0xA3
    end



    local DSM_ChannelInfo = {} 

    for i=0, 9 do
        DSM_ChannelInfo[i] = {[0]= 0x00, CH_TYPE.NONE} -- Initialize with no special function 
    end

    local wingType = MENU_DATA[MEMU_VAR.WING_TYPE]
    local tailType = MENU_DATA[MEMU_VAR.TAIL_TYPE]

    local thrCh  =  MENU_DATA[MEMU_VAR.CH_THR]
    local lAilCh =  MENU_DATA[MEMU_VAR.CH_L_AIL]
    local rAilCh =  MENU_DATA[MEMU_VAR.CH_R_AIL]
    local lflapCh = MENU_DATA[MEMU_VAR.CH_L_FLP]
    local rflapCh = MENU_DATA[MEMU_VAR.CH_R_FLP]

    local lElevCh = MENU_DATA[MEMU_VAR.CH_L_ELE]
    local rElevCh = MENU_DATA[MEMU_VAR.CH_R_ELE]

    local lRudCh = MENU_DATA[MEMU_VAR.CH_L_RUD]
    local rRudCh = MENU_DATA[MEMU_VAR.CH_R_RUD]

    -- Channels in menu vars are Zero base, Channel info is 1 based 
    
    -- THR 
    if (thrCh~=nil) then DSM_ChannelInfo[thrCh][1]= CH_TYPE.THR end

    -- AIL (Left and Right)
    if (lAilCh~=nil) then DSM_ChannelInfo[lAilCh][1] = CH_TYPE.AIL  end
    if (rAilCh~=nil) then DSM_ChannelInfo[rAilCh][1] = CH_TYPE.AIL+CH_TYPE.SLAVE end
    -- ELE (Left and Right)
    if (lElevCh~=nil) then DSM_ChannelInfo[lElevCh][1] = CH_TYPE.ELE end
    if (rElevCh~=nil) then DSM_ChannelInfo[rElevCh][1] = CH_TYPE.ELE+CH_TYPE.SLAVE end
    -- RUD (Left and Right)
    if (lRudCh~=nil) then DSM_ChannelInfo[lRudCh][1] = CH_TYPE.RUD end
    if (rRudCh~=nil) then DSM_ChannelInfo[rRudCh][1] = CH_TYPE.RUD+CH_TYPE.SLAVE end

    -- VTAIL: RUD + ELE
    if (tailType==TAIL_TYPE.VTAIL_A) then 
        DSM_ChannelInfo[lElevCh][1] = CH_TYPE.RUD+CH_TYPE.ELE
        DSM_ChannelInfo[rElevCh][1] = CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.SLAVE
    elseif (tailType==TAIL_TYPE.VTAIL_B) then
        DSM_ChannelInfo[lElevCh][1] = CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.SLAVE
        DSM_ChannelInfo[rElevCh][1] = CH_TYPE.RUD+CH_TYPE.ELE
    end

    -- TRAILERRON: 2-ELE + AIL
    if (tailType==TAIL_TYPE.TRAILERON_A) then 
        DSM_ChannelInfo[lElevCh][1] = CH_TYPE.AIL+CH_TYPE.ELE
        DSM_ChannelInfo[rElevCh][1] = CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE
    elseif (tailType==TAIL_TYPE.TRAILERON_B) then
        DSM_ChannelInfo[lElevCh][1] = CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE
        DSM_ChannelInfo[rElevCh][1] = CH_TYPE.AIL+CH_TYPE.ELE
    end

    ---- ELEVON :  AIL + ELE 
    if (wingType==WING_TYPE.ELEVON_A) then 
        DSM_ChannelInfo[lAilCh][1] = CH_TYPE.AIL+CH_TYPE.ELE
        DSM_ChannelInfo[rAilCh][1] = CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE
    elseif (wingType==WING_TYPE.ELEVON_B) then
        DSM_ChannelInfo[lAilCh][1] = CH_TYPE.AIL+CH_TYPE.ELE
        DSM_ChannelInfo[rAilCh][1] = CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE
    end

    -- Apply Gyro Reverse as needed for each channel as long as it is used 
    for i=0, 9 do
        if (MENU_DATA[MEMU_VAR.PORT_BASE+i]==CH_MODE_TYPE.REVERSE and DSM_ChannelInfo[i][1]>0) then
            DSM_ChannelInfo[i][0]=DSM_ChannelInfo[i][1]+0x70 -- ALL REVERSE is 0x70 for normal
            DSM_ChannelInfo[i][1]=DSM_ChannelInfo[i][1]+CH_TYPE.REVERSE
        end
    end

    -- VTAIL: RUD + ELE
    if (tailType==TAIL_TYPE.VTAIL_A) then 
        DSM_ChannelInfo[lElevCh][0] = ApplyTailMixA(DSM_ChannelInfo[lElevCh][1])
        DSM_ChannelInfo[rElevCh][0] = ApplyTailMixA(DSM_ChannelInfo[rElevCh][1])
    elseif (tailType==TAIL_TYPE.VTAIL_B) then
        DSM_ChannelInfo[lElevCh][0] = ApplyTailMixB(DSM_ChannelInfo[lElevCh][1])
        DSM_ChannelInfo[rElevCh][0] = ApplyTailMixB(DSM_ChannelInfo[rElevCh][1])
    end

    -- TRAILERRON: ELE + AIL
    if (tailType==TAIL_TYPE.TRAILERON_A) then 
        DSM_ChannelInfo[lElevCh][1] = ApplyTailMixA(DSM_ChannelInfo[lElevCh][1])
        DSM_ChannelInfo[rElevCh][1] = ApplyTailMixA(DSM_ChannelInfo[rElevCh][1])
    elseif (tailType==TAIL_TYPE.TRAILERON_B) then
        DSM_ChannelInfo[lElevCh][1] = ApplyTailMixB(DSM_ChannelInfo[lElevCh][1])
        DSM_ChannelInfo[rElevCh][1] = ApplyTailMixB(DSM_ChannelInfo[rElevCh][1])
    end

     ---- ELEVON :  AIL + ELE 
     if (wingType==WING_TYPE.ELEVON_A) then 
        DSM_ChannelInfo[lAilCh][0] = ApplyWingMixA(DSM_ChannelInfo[lAilCh][1])
        DSM_ChannelInfo[rAilCh][0] = ApplyWingMixA(DSM_ChannelInfo[rAilCh][1])
    elseif (wingType==WING_TYPE.ELEVON_B) then
        DSM_ChannelInfo[lAilCh][0] = ApplyWingMixB(DSM_ChannelInfo[lAilCh][1])
        DSM_ChannelInfo[rAilCh][0] = ApplyWingMixB(DSM_ChannelInfo[rAilCh][1])
    end

    -- Show how it looks
    for i=0, 9 do
        local b1,b2 =  DSM_ChannelInfo[i][0], DSM_ChannelInfo[i][1]
        print(string.format("%s (%02X %02X)  %s", MODEL.PORT_TEXT[i],
             b1, b2, dsmLib.channelType2String(b1,b2)))
    end

    return DSM_ChannelInfo, string.format("Aircraft(%s) Wing(%s) Tail(%s)",aircraft_type_text[currAircraftType],wing_type_text[wingType],tail_type_text[tailType])
end

---- Memu Processing Helpers to Mimic the DSM RX menu behaviour

local function ST_StartConnection()
    return 0
end

local function ST_ReleaseConnection()
end

-- Clear each line of the menu 
local function clearMenuLines() 
    local ctx = dsmLib.DSM_Context
    for i = 0, dsmLib.MAX_MENU_LINES do -- clear menu
        ctx.MenuLines[i] = { MenuId = 0, lineNum = 0, Type = 0, Text = "", TextId = 0, ValId = 0, Min=0, Max=0, Def=0, TextStart=0, Val=nil }
    end
end

-- Post processing needed for each menu 
local function PostProcessMenu()
    local ctx = dsmLib.DSM_Context

    if (ctx.Menu.Text==nil) then
        ctx.Menu.Text = dsmLib.Get_Text(ctx.Menu.TextId)
        dsmLib.MenuPostProcessing (ctx.Menu)
    end

    if (DEBUG_ON) then dsmLib.LOG_write("SIM RESPONSE Menu: %s\n", dsmLib.menu2String(ctx.Menu)) end

    for i = 0, dsmLib.MAX_MENU_LINES do -- clear menu
        local line = ctx.MenuLines[i]
        if (line.Type~=0) then
            line.MenuId = ctx.Menu.MenuId
            line.lineNum = i
            dsmLib.MenuLinePostProcessing(line) -- Do the same post processing as if they come from the RX
            if (DEBUG_ON) then dsmLib.LOG_write("SIM RESPONSE MenuLine: %s\n", dsmLib.menuLine2String(line))  end
        end

    end
end

-- Creates the menus to Render with the GUI
local function ST_LoadMenu(menuId)
    local ctx = dsmLib.DSM_Context

    local function formatTXRevert(port)
        return ((MODEL.modelOutputChannel[port].revert==0 and "  (Tx:Normal)") or "  (Tx:Reverse)")
    end

    clearMenuLines()

    if (menuId==0x1000) then -- MAIN MENU
        ctx.Menu = { MenuId = 0x1000, Text = "Main Menu ("..MODEL.modelName..")", PrevId = 0, NextId = 0, BackId = 0, TextId=0 }
        ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, Text = "Model Setup", ValId = 0x1001,TextId=0 }
       
        if (menuDataChanged) then
            ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, Text="Save Changes", TextId = 0, ValId = 0x1005 }
            ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, Text="Discard Changes", TextId = 0, ValId = 0x1006 }
            ctx.SelLine = 4 
        else
            if (SIMULATION_ON) then
                ctx.MenuLines[4] = { Type = LINE_TYPE.MENU, Text = "RX Simulator (GUI dev only)", ValId = 0xFFF1, TextId=0 } -- Menu 0xFFF2 to SIMULATOR  
            end
            ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text = "Forward Programming RX", ValId = 0xFFF2, TextId=0 }  -- Menu 0xFFF2 to Real RX 
            ctx.SelLine = 6 
        end
        lastGoodMenu = menuId
    elseif (menuId==0x1001) then -- MODEL SETUP
        ctx.Menu = { MenuId = 0x1001, Text = "Model Setup  ("..MODEL.modelName..")", PrevId = 0, NextId = 0, BackId = 0x1000, TextId=0 }
        ctx.MenuLines[0] = { Type = LINE_TYPE.MENU, Text = "Aircraft Type Setup", ValId = 0x1010,TextId=0 }
        ctx.MenuLines[1] = { Type = LINE_TYPE.MENU, Text = "Wing & Tail Channels ", ValId = 0x1020, TextId=0 } 
        ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, Text = "Gyro Channel Reverse", ValId = 0x1030, TextId=0 } 
        ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, Text = "WARNING: Changing of Aircraft or Wing will", ValId = 0x1001, TextId=0 } 
        ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text = "delete previous Channel/Port assigments.", ValId = 0x1001, TextId=0 } 

        ctx.SelLine = 0
        lastGoodMenu = menuId
    elseif (menuId==0x1005) then
        printChannelSummary()
        ST_SaveFileData()


        local msg1 = "Data saved to: " 
        local msg2 = "  "..DATA_PATH.."/"..hashName(MODEL.modelName)..".txt" 

        ctx.Menu = { MenuId = 0x1005, Text = "Config Saved", PrevId = 0, NextId = 0, BackId = 0, TextId=0 }
        ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, Text=msg1, TextId = 0, ValId = 0x1005 }
        ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, Text=msg2, TextId = 0, ValId = 0x1005 }
        ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text="Complete", TextId = 0, ValId = 0x1000 }
        ctx.SelLine = 6
        lastGoodMenu = menuId
    elseif (menuId==0x1006) then
        ST_LoadFileData()
        local msg1 = "Data restored from: " 
        local msg2 = "  "..DATA_PATH.."/"..hashName(MODEL.modelName)..".txt" 
        
        ctx.Menu = { MenuId = 0x1006, Text = "Discart Changes", PrevId = 0, NextId = 0, BackId = 0, TextId=0 }
        ctx.MenuLines[2] = { Type = LINE_TYPE.MENU, Text=msg1, TextId = 0, ValId = 0x1006 }
        ctx.MenuLines[3] = { Type = LINE_TYPE.MENU, Text=msg2, TextId = 0, ValId = 0x1006 }
        ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text="Complete", TextId = 0, ValId = 0x1000 }
        ctx.SelLine = 6
        lastGoodMenu = menuId
    elseif (menuId==0x1010) then
        ctx.Menu = { MenuId = 0x1010, Text = "Aircraft Type", PrevId = 0x1001, NextId = 0x1011, BackId = 0, TextId=0 }
        ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, Text="Aircraft Type", TextId = 0, ValId = MEMU_VAR.AIRCRAFT_TYPE, Min=50, Max=53, Def=50, Val=MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE] }
        ctx.SelLine = 5
        lastGoodMenu = menuId
    elseif (menuId==0x1011) then
        ctx.Menu = { MenuId = 0x1011, Text = "Model Type:"..aircraft_type_text[currAircraftType], PrevId = 0x1010, NextId = 0x1020, BackId = 0, TextId=0 }
        ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, Text="Wing Type", TextId = 0, ValId = MEMU_VAR.WING_TYPE, Min=100, Max=107, Def=100, Val=MENU_DATA[MEMU_VAR.WING_TYPE] }
        ctx.MenuLines[6] = { Type = LINE_TYPE.LIST_MENU_NC, Text="Tail Type", TextId = 0, ValId = MEMU_VAR.TAIL_TYPE, Min=200, Max=208, Def=200, Val=MENU_DATA[MEMU_VAR.TAIL_TYPE] }
        ctx.SelLine = 5
        lastGoodMenu = menuId
    elseif (menuId==0x1020) then
        ------ WING  SETUP -------
        local thr = MENU_DATA[MEMU_VAR.CH_THR]
        local leftAil = MENU_DATA[MEMU_VAR.CH_L_AIL]
        local rightAil = MENU_DATA[MEMU_VAR.CH_R_AIL]
        local leftFlap = MENU_DATA[MEMU_VAR.CH_L_FLP]
        local rightFlap = MENU_DATA[MEMU_VAR.CH_R_FLP]

        local thrText     = "Thr"
        local leftAilText = "Left Aileron"
        local rightAilText = "Right Aileron"
        local leftFlapText = "Left Flap"
        local rightFlapText = "Right Flap"

        if (rightAil==nil) then leftAilText = "Aileron" end
        if (rightFlap==nil) then leftFlapText = "Flap" end

        local title = aircraft_type_text[currAircraftType].."   Wing:"..wing_type_text[currWingType]

        ctx.Menu = { MenuId = 0x1020, Text = title, PrevId = 0x1011, NextId = 0x1021, BackId = 0, TextId=0 }

        ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU_NC, Text=thrText, TextId = 0, ValId = MEMU_VAR.CH_THR, Min=0, Max=9, Def=0, Val= thr }

        ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU_NC, Text=leftAilText, TextId = 0, ValId = MEMU_VAR.CH_L_AIL, Min=0, Max=9, Def=0, Val= leftAil }

        if (rightAil~=nil) then
            ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU_NC, Text=rightAilText, TextId = 0, ValId = MEMU_VAR.CH_R_AIL, Min=0, Max=9, Def=0, Val= rightAil }
        end

        if (leftFlap~=nil) then
            ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, Text=leftFlapText, TextId = 0, ValId = MEMU_VAR.CH_L_FLP, Min=0, Max=9, Def=0, Val= leftFlap }
        end
        if (rightFlap~=nil) then
            ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, Text=rightFlapText, TextId = 0, ValId = MEMU_VAR.CH_R_FLP, Min=0, Max=9, Def=0, Val= rightFlap }
        end

        ctx.SelLine = 1
        lastGoodMenu = menuId

    elseif (menuId==0x1021) then
        ------ TAIL SETUP -------
        local leftRud = MENU_DATA[MEMU_VAR.CH_L_RUD] 
        local rightRud = MENU_DATA[MEMU_VAR.CH_R_RUD] 
        local leftEle = MENU_DATA[MEMU_VAR.CH_L_ELE]
        local rightEle = MENU_DATA[MEMU_VAR.CH_R_ELE]

        local leftRudText = "Left Rudder"
        local rightRudText = "Right Rudder"

        local leftElvText = "Left Elevator"
        local rightElvText = "Right Elevator"

        if (rightRud==nil) then leftRudText = "Rudder"  end
        if (rightEle==nil) then leftElvText = "Elevator" end

        local title = aircraft_type_text[currAircraftType].."  Tail:"..tail_type_text[currTailType]

        ctx.Menu = { MenuId = 0x1021, Text = title, PrevId = 0x1020, NextId = 0x1001, BackId = 0, TextId=0 }
        if (leftRud~=nil) then
            ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU_NC, Text=leftRudText, TextId = 0, ValId = MEMU_VAR.CH_L_RUD, Min=0, Max=9, Def=0, Val= leftRud}
        end

        if (rightRud~=nil) then
            ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU_NC, Text=rightRudText, TextId = 0, ValId = MEMU_VAR.CH_R_RUD, Min=0, Max=9, Def=0, Val=rightRud }
        end

        if (leftEle~=nil) then
            ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, Text=leftElvText, TextId = 0, ValId = MEMU_VAR.CH_L_ELE, Min=0, Max=9, Def=0, Val=leftEle }
        end

        if (rightEle~=nil) then
            ctx.MenuLines[5] = { Type = LINE_TYPE.LIST_MENU_NC, Text=rightElvText, TextId = 0, ValId = MEMU_VAR.CH_R_ELE, Min=0, Max=9, Def=0, Val=rightEle }
        end

        ctx.SelLine = 1
        lastGoodMenu = menuId
   
    elseif (menuId==0x1030) then
        printChannelSummary()
        
        ctx.Menu = { MenuId = 0x1030, Text = "Gyro Channel Reverse (Port 1-5)", PrevId = 0, NextId = 0x1031, BackId = 0x1001, TextId=0 }
        ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU_NC, Text=MODEL.PORT_TEXT[PORT.PORT1], TextId = 0, ValId = MEMU_VAR.PORT1_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT1_MODE], Format = formatTXRevert(PORT.PORT1) }
        ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU_NC, Text=MODEL.PORT_TEXT[PORT.PORT2], TextId = 0, ValId = MEMU_VAR.PORT2_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT2_MODE], Format = formatTXRevert(PORT.PORT2) }
        ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU_NC, Text=MODEL.PORT_TEXT[PORT.PORT3], TextId = 0, ValId = MEMU_VAR.PORT3_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT3_MODE], Format = formatTXRevert(PORT.PORT3) }
        ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU_NC, Text=MODEL.PORT_TEXT[PORT.PORT4], TextId = 0, ValId = MEMU_VAR.PORT4_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT4_MODE], Format = formatTXRevert(PORT.PORT4) }
        ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, Text=MODEL.PORT_TEXT[PORT.PORT5], TextId = 0, ValId = MEMU_VAR.PORT5_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT5_MODE], Format = formatTXRevert(PORT.PORT5) }

        ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, Text="Only Thr/Ail/Rud/Ele. This affects AS3X/SAFE reaction dir./b", TextId = 0, ValId = 0x1030 }
        ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text="Any changes, use RX 'Relearn Servo Settings'/b", TextId = 0, ValId = 0x1030 }

        ctx.SelLine = 0
        lastGoodMenu = menuId
    elseif (menuId==0x1031) then
        printChannelSummary()
        ctx.Menu = { MenuId = 0x1031, Text = "Gyro Channel Reverse (Port 6-10)", PrevId = 0x1030, NextId = 0, BackId = 0x1001, TextId=0 }
        ctx.MenuLines[0] = { Type = LINE_TYPE.LIST_MENU_NC, Text=MODEL.PORT_TEXT[PORT.PORT6], TextId = 0, ValId = MEMU_VAR.PORT6_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT6_MODE], Format = formatTXRevert(PORT.PORT6) }
        ctx.MenuLines[1] = { Type = LINE_TYPE.LIST_MENU_NC, Text=MODEL.PORT_TEXT[PORT.PORT7], TextId = 0, ValId = MEMU_VAR.PORT7_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT7_MODE], Format = formatTXRevert(PORT.PORT7) }
        ctx.MenuLines[2] = { Type = LINE_TYPE.LIST_MENU_NC, Text=MODEL.PORT_TEXT[PORT.PORT8], TextId = 0, ValId = MEMU_VAR.PORT8_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT8_MODE], Format = formatTXRevert(PORT.PORT8) }
        ctx.MenuLines[3] = { Type = LINE_TYPE.LIST_MENU_NC, Text=MODEL.PORT_TEXT[PORT.PORT9], TextId = 0, ValId = MEMU_VAR.PORT9_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT9_MODE], Format = formatTXRevert(PORT.PORT9) }
        ctx.MenuLines[4] = { Type = LINE_TYPE.LIST_MENU_NC, Text=MODEL.PORT_TEXT[PORT.PORT10], TextId = 0, ValId = MEMU_VAR.PORT10_MODE, Min=300, Max=301, Def=300, Val=MENU_DATA[MEMU_VAR.PORT10_MODE], Format = formatTXRevert(PORT.PORT10) }
     
        ctx.MenuLines[5] = { Type = LINE_TYPE.MENU, Text="Only Thr/Ail/Rud/Ele. This affects AS3X/SAFE reaction dir./b", TextId = 0, ValId = 0x1031 }
        ctx.MenuLines[6] = { Type = LINE_TYPE.MENU, Text="Any changes, use RX 'Relearn Servo Settings'/b", TextId = 0, ValId = 0x1031 }

        ctx.SelLine = 0
        lastGoodMenu = menuId
    else
        print("NOT IMPLEMENTED")
        ctx.Menu = { MenuId = 0x0002, Text = "NOT IMPLEMENTED", TextId = 0, PrevId = 0, NextId = 0, BackId = lastGoodMenu }
        ctx.SelLine = dsmLib.BACK_BUTTON
    end

    PostProcessMenu()
end

-- ST_SendReceive
-- Main state machine for the Setup menu

local function ST_SendReceive()
    local ctx = dsmLib.DSM_Context
    --if (DEBUG_ON) then dsmLib.LOG_write("%3.3f %s: ", dsmLib.getElapsedTime(), dsmLib.phase2String(ctx.Phase)) end

    if ctx.Phase == PHASE.RX_VERSION then -- request RX version
        ctx.RX.Name = "MODEL SETUP"
        ctx.RX.Version = SETUP_LIB_VERSION
        ctx.Phase = PHASE.MENU_TITLE

        ctx.Refresh_Display = true

        
    elseif ctx.Phase == PHASE.WAIT_CMD then 
        
    elseif ctx.Phase == PHASE.MENU_TITLE then -- request menu title
        if ctx.Menu.MenuId == 0 then  -- First time loading a menu ?
            ST_LoadMenu(0x01000)
        else
            ST_LoadMenu(ctx.Menu.MenuId)
        end
        ctx.Phase = PHASE.WAIT_CMD
        ctx.Refresh_Display = true

    elseif ctx.Phase == PHASE.VALUE_CHANGING then -- send value
        local line = ctx.MenuLines[ctx.SelLine] -- Updated Value of SELECTED line
       
        if (MENU_DATA[line.ValId] ~= line.Val ) then
            MENU_DATA[line.ValId] = line.Val 
            print(string.format("MENU_DATA[%d/%s]=%d",line.ValId,line.Text, line.Val))
            menuDataChanged=true
        end

        ctx.Phase = PHASE.VALUE_CHANGING_WAIT

    elseif ctx.Phase == PHASE.VALUE_CHANGING_WAIT then
        local line = ctx.MenuLines[ctx.SelLine]

    elseif ctx.Phase == PHASE.VALUE_CHANGE_END then -- send value
        local line = ctx.MenuLines[ctx.SelLine] -- Updated Value of SELECTED line
        
        -- Update the menu data from the line
        if (MENU_DATA[line.ValId] ~= line.Val ) then
            MENU_DATA[line.ValId] = line.Val 
            print(string.format("MENU_DATA[%d/%s]=%d",line.ValId,line.Text, line.Val))
            menuDataChanged=true
        end

        -- Did the aircraft type change?
        if (currAircraftType ~= MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE]) then
            ST_AircraftInit(MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE])
        end

        -- Did the Wing type change?
        if (currWingType ~= MENU_DATA[MEMU_VAR.WING_TYPE]) then
            if (currAircraftType==AIRCRAFT_TYPE.GLIDER) then
                ST_GliderWingInit(MENU_DATA[MEMU_VAR.WING_TYPE])
            else
                ST_PlaneWingInit(MENU_DATA[MEMU_VAR.WING_TYPE])
            end

            -- DELTA has only RUDER 
            if ((currWingType==WING_TYPE.ELEVON_A or currWingType==WING_TYPE.ELEVON_B) and TAIL_TYPE~=TAIL_TYPE.RUD_1) then
                currTailType = TAIL_TYPE.RUD_1
            end
        end

        --- Did the tail changed?
        if (currTailType ~= MENU_DATA[MEMU_VAR.TAIL_TYPE]) then
            if (currAircraftType==AIRCRAFT_TYPE.GLIDER) then
                ST_GliderTailInit(MENU_DATA[MEMU_VAR.TAIL_TYPE])
            else
                ST_PlaneTailInit(MENU_DATA[MEMU_VAR.TAIL_TYPE])
            end
        end

        ctx.Phase = PHASE.WAIT_CMD
    elseif ctx.Phase == PHASE.EXIT then
       ctx.Phase=PHASE.EXIT_DONE
    end
end

------------------------------------------------------------------------------------------------------------
-- TEXT Management

local List_Text = {}
local Text = {}
local List_Text_Img = {}
local List_Values = {}

-- Get the text for this menus 
local function ST_Get_Text(index)
    local out = Text[index]   -- Find in regular header first
    if out== nil then
        out = List_Text[index]  -- Try list values, don't think is necesary, but just playing Safe
    end
    if out == nil then -- unknown...
        out = "Unknown_" .. string.format("%X", index)
    end
    return out
end

-- Get the List text for this menus 
local function ST_Get_List_Text(index)
    local out = List_Text[index]   -- Try to find the message in List_Text
    if out == nil then
        out = Text[index]  -- Try list headers, don't think is necesary, but just playing Safe
    end
    if out == nil then -- unknown...
        out = "UnknownLT_" .. string.format("%X", index)
    end
    return out
end

-- Get the List_Text Images
local function ST_Get_List_Text_Img(index) 
    local out = List_Text_Img[index]
    return out
end

local function ST_Get_List_Values(index)
    local out = List_Values[index]
    return out
end

-- Inital List and Image Text for this menus
local function ST_Init_Text(rxId) 
    dsmLib.Init_Text(rxId)

    -- Channel Names use the Port Text Retrived from OTX/ETX
    for i = 0, 9 do List_Text[i] = MODEL.PORT_TEXT[i]  end

    -- Aircraft Type
    List_Text[50+AIRCRAFT_TYPE.PLANE]  = "Airplane";    --List_Text_Img[50+AIRCRAFT_TYPE.PLANE]   = "at_plane.png|Airplane" 
    List_Text[50+AIRCRAFT_TYPE.GLIDER] = "Glider (Partial work)";      --List_Text_Img[50+AIRCRAFT_TYPE.GLIDER]  = "at_glider.png|Glider" 
    List_Text[50+AIRCRAFT_TYPE.HELI]   = "Helicopter (Not done)";  --List_Text_Img[50+AIRCRAFT_TYPE.HELI]    = "at_heli.png|Helicopter" 
    List_Text[50+AIRCRAFT_TYPE.DRONE]  = "Drone (not done)";       --List_Text_Img[50+AIRCRAFT_TYPE.DRONE]   = "at_drone.png|Drone" 

    -- Wing Types
    List_Text[100+WING_TYPE.AIL_1] = "Single Ail";  List_Text_Img[100+WING_TYPE.AIL_1]  = "wt_1ail.png|Single Aileron" 
    List_Text[100+WING_TYPE.AIL_2] = "Dual Ail";  List_Text_Img[100+WING_TYPE.AIL_2]  = "wt_2ail.png|Dual Aileron" 
    List_Text[100+WING_TYPE.FLAPERON] = "Flaperon";  List_Text_Img[100+WING_TYPE.FLAPERON]  = "wt_flaperon.png|Flaperon" 
    List_Text[100+WING_TYPE.AIL_1_FLP_1] = "Ail + Flap";  List_Text_Img[100+WING_TYPE.AIL_1_FLP_1]  = "wt_1ail_1flp.png|Aileron + Flap" 
    List_Text[100+WING_TYPE.AIL_2_FLP_1] = "Dual Ail + Flap";  List_Text_Img[100+WING_TYPE.AIL_2_FLP_1]  = "wt_2ail_1flp.png|Dual Aileron + Flap" 
    List_Text[100+WING_TYPE.AIL_2_FLP_2] = "Dual Ail + Dual Flap";  List_Text_Img[100+WING_TYPE.AIL_2_FLP_2]  = "wt_2ail_2flp.png|Dual Aileron + Dual Flap" 
    List_Text[100+WING_TYPE.ELEVON_A] = "Delta/Elevon A";  List_Text_Img[100+WING_TYPE.ELEVON_A]  = "wt_elevon.png|Delta/Elevon A" 
    List_Text[100+WING_TYPE.ELEVON_B] = "Delta/Elevon B";  List_Text_Img[100+WING_TYPE.ELEVON_B]  = "wt_elevon.png|Delta/Elevon B" 

    -- Tail Types
    List_Text[200+TAIL_TYPE.RUD_1] = "Rudder Only";  List_Text_Img[200+TAIL_TYPE.RUD_1]  = "tt_1rud.png|Rudder Only" 
    List_Text[200+TAIL_TYPE.RUD_1_ELEV_1] = "Rud + Ele";  List_Text_Img[200+TAIL_TYPE.RUD_1_ELEV_1]  = "tt_1rud_1ele.png|Tail Normal" 
    List_Text[200+TAIL_TYPE.RUD_1_ELEV_2] = "Rud + Dual Ele";  List_Text_Img[200+TAIL_TYPE.RUD_1_ELEV_2]  = "tt_1rud_2ele.png|Rud + Dual Elev" 
    List_Text[200+TAIL_TYPE.RUD_2_ELEV_1] = "Dual Rud + Ele";  List_Text_Img[200+TAIL_TYPE.RUD_2_ELEV_1]  = "tt_2rud_1ele.png|Dual Rud + Elev" 
    List_Text[200+TAIL_TYPE.RUD_2_ELEV_2] = "Dual Rud + Dual Ele";  List_Text_Img[200+TAIL_TYPE.RUD_2_ELEV_2]  = "tt_2rud_2ele.png|Dual Rud + Dual Elev" 
    List_Text[200+TAIL_TYPE.VTAIL_A] = "V-Tail A";  List_Text_Img[200+TAIL_TYPE.VTAIL_A]  = "tt_vtail.png|V-Tail A" 
    List_Text[200+TAIL_TYPE.VTAIL_B] = "V-Tail B";  List_Text_Img[200+TAIL_TYPE.VTAIL_B]  = "tt_vtail.png|V-Tail B" 
    List_Text[200+TAIL_TYPE.TRAILERON_A] = "Traileron A";  List_Text_Img[200+TAIL_TYPE.TRAILERON_A]  = "tt_traileron.png|Traileron A" 
    List_Text[200+TAIL_TYPE.TRAILERON_B] = "Traileron B";  List_Text_Img[200+TAIL_TYPE.TRAILERON_B]  = "tt_traileron.png|Traileron B" 

    -- Servo Reverse
    List_Text[300+CH_MODE_TYPE.NORMAL]  = "Normal "
    List_Text[300+CH_MODE_TYPE.REVERSE] = "Reverse"

end

-- Initial Setup
local function ST_Init()
    -- Initialize dsmLib background
    dsmLib.Init()
    -- Initialize text (use RX_ID 0)
    ST_Init_Text(0)

    -- Setup default Data, and load a file if exist
    ST_Default_Data()
    if (ST_LoadFileData()==0) then -- Did not load a file
        ST_SaveFileData() -- Save Defaults
    end
end


------------------------------------------------------------------------------------------------------------
-- Lib EXPORTS

-- Export Constants
SetupLib.PHASE       = dsmLib.PHASE
SetupLib.LINE_TYPE   = dsmLib.LINE_TYPE
SetupLib.RX          = dsmLib.RX 
SetupLib.DISP_ATTR   = dsmLib.DISP_ATTR

SetupLib.BACK_BUTTON = dsmLib.BACK_BUTTON
SetupLib.NEXT_BUTTON = dsmLib.NEXT_BUTTON
SetupLib.PREV_BUTTON = dsmLib.PREV_BUTTON
SetupLib.MAX_MENU_LINES = dsmLib.MAX_MENU_LINES

-- Export Shared Context Variables
SetupLib.DSM_Context = dsmLib.DSM_Context

-- Export Functions
SetupLib.LOG_write = dsmLib.LOG_write
SetupLib.LOG_close = dsmLib.LOG_close
SetupLib.getElapsedTime = dsmLib.getElapsedTime

SetupLib.Get_Text = ST_Get_Text
SetupLib.Get_List_Text = ST_Get_List_Text
SetupLib.Get_List_Text_Img = ST_Get_List_Text_Img

SetupLib.phase2String = dsmLib.phase2String
SetupLib.menu2String = dsmLib.menu2String
SetupLib.menuLine2String = dsmLib.menuLine2String

SetupLib.isSelectableLine = dsmLib.isSelectableLine
SetupLib.isEditableLine = dsmLib.isEditableLine
SetupLib.isListLine = dsmLib.isListLine
SetupLib.isPercentValueLine = dsmLib.isPercentValueLine
SetupLib.isNumberValueLine = dsmLib.isNumberValueLine
SetupLib.isDisplayAttr = dsmLib.isDisplayAttr
SetupLib.isFlightModeLine = dsmLib.isFlightModeLine
SetupLib.GetFlightModeValue = dsmLib.GetFlightModeValue

SetupLib.StartConnection = ST_StartConnection   -- Override Function 
SetupLib.ReleaseConnection = ST_ReleaseConnection  -- Override Function
SetupLib.ChangePhase = dsmLib.ChangePhase
SetupLib.Value_Add = dsmLib.Value_Add
SetupLib.Value_Default = dsmLib.Value_Default
SetupLib.Value_Write_Validate = dsmLib.Value_Write_Validate
SetupLib.GotoMenu = dsmLib.GotoMenu
SetupLib.MoveSelectionLine = dsmLib.MoveSelectionLine
SetupLib.Send_Receive = ST_SendReceive -- Override Function 
SetupLib.Init = ST_Init
SetupLib.Init_Text = ST_Init_Text

SetupLib.CreateDSMPortChannelInfo = CreateDSMPortChannelInfo

return SetupLib
