local Log, DEBUG_ON = ...

local DATA_PATH = "/MODELS/DSMDATA" -- Path to store model settings files
local TX_CHANNELS = 12

-- MODEL information from ETX/OTX
local ModelLib = {}

local MODEL = {
    modelName = "",            -- The name of the model comming from OTX/ETX
    modelOutputChannel = {},   -- Output information from OTX/ETX
    AirWingTailDesc = "",
    
    TX_CH_TEXT = {},
    PORT_TEXT = {},
    DSM_ChannelInfo = {}       -- Data Created by DSM Configuration Script
}

--Channel Types --
local CH_TYPE = {
    NONE     = 0x00,
    AIL      = 0x01,
    ELE      = 0x02,
    RUD      = 0x04,

    REVERSE  = 0x20,
    THR      = 0x40,
    SLAVE    = 0x80,
}

-- Seems like Reverse Mix is complement of the 3 bits
local CH_MIX_TYPE = {
    MIX_NORM     = 0x00,   -- 0000
    MIX_AIL      = 0x10,   -- 0001 Taileron
    MIX_ELE      = 0x20,   -- 0010 For VTIAL and Delta-ELEVON
    MIX_RUD      = 0x30,   -- 0011 For VTIAL

    MIX_RUD_REV  = 0x40,   -- 0100 For VTIAL
    MIX_ELE_REV  = 0x50,   -- 0101 For VTIAL and Delta-ELEVON A
    MIX_AIL_REV  = 0x60,   -- 0110 Taileron 
    MIX_NORM_REV = 0x70    -- 0111
}

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
    TRAILERON_A_R2  = 9,  -- 3
    TRAILERON_B_R2  = 10  -- 3
}
local tail_type_text = {[0]="Rud Only","Normal","Rud + Dual Ele","Dual Rud + Elv","Dual Rud/Ele",
                        "VTail A","VTail B","Taileron A","Taileron B","Taileron A + 2x Rud","Taileron B + 2x Rud"}

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,
    PORT11 = 10,
    PORT12 = 11
}

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,
        PORT11_MODE     = 1030,
        PORT12_MODE     = 1031,

        DATA_END        = 1040
}


-- MENU DATA Management
local MENU_DATA = {}            -- Store the variables used in the Menus.


---- DSM_ChannelInfo ---------------------------------
-- First byte describe Special  Mixing (Vtail/Elevon = 0x20)
--VTAIL
--(0x00 0x06) CH_TYPE.ELE+CH_TYPE.RUD  (0x02+0x04 = 0x06)
--(0x20 0x86) CH_TYPE.ELE+CH_TYPE.RUD+CH_TYPE.SLAVE  (0x02+0x04+0x80 = 0x86)

-- The 2nd byte describes the functionality of the port 
-- 
-- Single   Example: CH_TYPE.AIL (0x01) Aileron
-- Reverse  Example: CH_TYPE.AIL+CH_TYPE.REVERSE (0x01+0x20=0x21) Reverse Aileron
-- Slave    Example: CH_TYPE.AIL+CH_TYPE.SLAVE (0x01+0x80) -- 2nd servo Aileron

-- Elevon   Example: CH_TYPE.AIL+CH_TYPE.ELE  (0x01+0x02 = 0x03) -- Elevon
-- Elevon   Example: CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE  (0x01+0x02+0x80 = 0x83) -- Slave Elevon

-- RudElv (VTail) Example: CH_TYPE.ELE+CH_TYPE.RUD  (0x02+0x04 = 0x06) -- Rudevator
-- RudElv (VTail) Example: CH_TYPE.ELE+CH_TYPE.RUD+CH_TYPE.SLAVE  (0x02+0x04+0x80 = 0x86) -- Rudevator Slave

-- DEFAULT Simple Plane Port configuration (The Configuration tool will overrride this)
MODEL.DSM_ChannelInfo= {[0]= -- Start array at position 0
                        {[0]=  CH_MIX_TYPE.MIX_NORM, CH_TYPE.THR},    -- Ch1 Thr  (0x40)
                        {[0]=  CH_MIX_TYPE.MIX_NORM, CH_TYPE.AIL},    -- Ch2 Ail  (0x01)
                        {[0]=  CH_MIX_TYPE.MIX_NORM, CH_TYPE.ELE},    -- Ch2 ElE  (0x02)
                        {[0]=  CH_MIX_TYPE.MIX_NORM, CH_TYPE.RUD},    -- Ch4 Rud  (0x04)
                        {[0]=  CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE},   -- Ch5 Gear (0x00)
                        {[0]=  CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE},   -- Ch6 Aux1 (0x00)
                        {[0]=  CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE},   -- Ch7 Aux2 (0x00)
                        {[0]=  CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE},   -- Ch8 Aux3 (0x00)
                        {[0]=  CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE},   -- Ch9 Aux4 (0x00)
                        {[0]=  CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE},   -- Ch10 Aux5 (0x00)
                        {[0]=  CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE},   -- Ch11 Aux6 (0x00)
                        {[0]=  CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE}    -- Ch12 Aux7 (0x00)
                    }   

function ModelLib.printChannelSummary(a,w,t)
    -- Summary
    print("CHANNEL INFORMATION")
    print("Aircraft:".. (aircraft_type_text[MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE]] or "--"))
    print("Wing Type:".. (wing_type_text[MENU_DATA[MEMU_VAR.WING_TYPE]] or "--"))
    print("Tail Type:".. (tail_type_text[MENU_DATA[MEMU_VAR.TAIL_TYPE]] 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

function ModelLib.printServoReverseInfo()
    print("SERVO Normal/Reverse INFORMATION")
    for i=0, TX_CHANNELS-1 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

function ModelLib.channelType2String(byte1, byte2) 
    local s = ""

    if (byte2==0) then return s end;
    
    if (bit32.band(byte2,CH_TYPE.AIL)>0) then s=s.."Ail" end
    if (bit32.band(byte2,CH_TYPE.ELE)>0) then s=s.."Ele" end
    if (bit32.band(byte2,CH_TYPE.RUD)>0) then s=s.."Rud" end
    if (bit32.band(byte2,CH_TYPE.THR)>0) then s=s.."Thr" end

    if (bit32.band(byte2,CH_TYPE.REVERSE)>0) then s=s.."-" end

    if (bit32.band(byte2,CH_TYPE.SLAVE)>0) then s=s.." Slv" end

    if (byte1==CH_MIX_TYPE.MIX_NORM) then s=s.." " 
    elseif (byte1==CH_MIX_TYPE.MIX_AIL) then s=s.." M_Ail" 
    elseif (byte1==CH_MIX_TYPE.MIX_ELE) then s=s.." M_Ele" 
    elseif (byte1==CH_MIX_TYPE.MIX_RUD) then s=s.." M_Rud" 
    elseif (byte1==CH_MIX_TYPE.MIX_RUD_REV) then s=s.." M_Rud-" 
    elseif (byte1==CH_MIX_TYPE.MIX_ELE_REV) then s=s.." M_Ele-" 
    elseif (byte1==CH_MIX_TYPE.MIX_AIL_REV) then s=s.." M_Ail-" 
    elseif (byte1==CH_MIX_TYPE.MIX_NORM_REV) then s=s.." M-" 
    end

    return s;
end


-------------------------------------------------------------------------------------------------
-- Read the model information from OTX/ETX

local function getModuleChannelOrder(num) 
        --Determine fist 4 channels order
    local channel_names={}
    local stick_names = {[0]= "R", "E", "T", "A" }
    local ch_order=num
    if (ch_order == -1) then
        channel_names[0] = stick_names[3]
        channel_names[1] = stick_names[1]
        channel_names[2] = stick_names[2]
        channel_names[3] = stick_names[0]
    else
        channel_names[bit32.band(ch_order,3)] = stick_names[3]
        ch_order = math.floor(ch_order/4)
        channel_names[bit32.band(ch_order,3)] = stick_names[1]
        ch_order = math.floor(ch_order/4)
        channel_names[bit32.band(ch_order,3)] = stick_names[2]
        ch_order = math.floor(ch_order/4)
        channel_names[bit32.band(ch_order,3)] = stick_names[0]
    end

    local s = ""
    for i=0,3 do
        s=s..channel_names[i]
    end
    return s
end

function ModelLib.ReadTxModelData()
  local TRANSLATE_AETR_TO_TAER=false
  local table = model.getInfo()   -- Get the model name 
  MODEL.modelName = table.name

  local module = model.getModule(0) -- Internal
  if (module==nil or module.Type~=6) then module = model.getModule(1) end -- External
  if (module~=nil) then
      if (module.Type==6 ) then -- MULTI-MODULE
          local chOrder = module.channelsOrder
          local s = getModuleChannelOrder(chOrder)
          Log.LOG_write("MultiChannel Ch Order: [%s]  %s\n",chOrder,s) 

          if (s=="AETR") then TRANSLATE_AETR_TO_TAER=true 
          else TRANSLATE_AETR_TO_TAER=false 
          end
      end
  end

  Log.LOG_write("MODEL NAME = %s\n",MODEL.modelName) 

  -- Read Ch1 to Ch10
  local i= 0
  for i = 0, TX_CHANNELS-1 do 
      local ch = model.getOutput(i) -- Zero base 
      if (ch~=nil) then
          MODEL.modelOutputChannel[i] = ch
          if (string.len(ch.name)==0) then 
              ch.formatCh = string.format("TX:Ch%i",i+1)
          else
              ch.formatCh = string.format("TX:Ch%i/%s",i+1,ch.name or "--")
          end
      end
  end

  -- Translate AETR to TAER
  -- TODO: Check if there is a way to know how to the TX is configured, since if it is 
  -- already TAER, is not needed 

  if (TRANSLATE_AETR_TO_TAER) then 
      Log.LOG_write("Applying  AETR -> TAER translation\n") 
      local ail = MODEL.modelOutputChannel[0]
      local elv = MODEL.modelOutputChannel[1]
      local thr = MODEL.modelOutputChannel[2]

      MODEL.modelOutputChannel[0] = thr
      MODEL.modelOutputChannel[1] = ail
      MODEL.modelOutputChannel[2] = elv
  end

  -- Create the Port Text to be used 
  Log.LOG_write("Ports/Channels:\n") 
  for i = 0, TX_CHANNELS-1 do 
      local ch =  MODEL.modelOutputChannel[i]
      if (ch~=nil) then
          MODEL.TX_CH_TEXT[i] = ch.formatCh
          MODEL.PORT_TEXT[i] = string.format("P%i (%s) ",i+1,MODEL.TX_CH_TEXT[i])
          
          Log.LOG_write("Port%d %s [%d,%d] Rev=%d, Off=%d, ppmC=%d, syn=%d\n",i+1,MODEL.TX_CH_TEXT[i],math.floor(ch.min/10),math.floor(ch.max/10), ch.revert, ch.offset, ch.ppmCenter, ch.symetrical)
      end
  end
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??

function ModelLib.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 ModelLib.ST_LoadFileData() 
    local fname = ModelLib.hashName(MODEL.modelName)..".txt"

    -- Clear Menu Data
    for i = 0, MEMU_VAR.DATA_END do
        MENU_DATA[i]=nil
    end

    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
    
    local currAircraftType = MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE]
    local currWingType = MENU_DATA[MEMU_VAR.WING_TYPE]
    local 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]))
   
    ModelLib.printChannelSummary()
    ModelLib.printServoReverseInfo()

    -- 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 ModelLib.ST_SaveFileData() 
    local fname = ModelLib.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)
end

-- This Creates the Servo Settings that will be used to pass to 
-- Forward programming
function ModelLib.CreateDSMPortChannelInfo()
    local function ApplyWingMixA(b2)
        -- ELEVON
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_ELE end; -- 0x03
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x83
    end

    local function ApplyWingMixB(b2)
        -- ELEVON 
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x03
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_ELE end; -- 0x83
   end

    local function ApplyTailMixA(b2)
        -- VTAIL
        -- Default normal/reverse behaviour 
        if (b2==CH_TYPE.RUD+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x06
        if (b2==CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_ELE end; -- 0x86

        --TAILERON
        -- Default normal/reverse behaviour 
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x03
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_AIL end; -- 0x83
    end

    local function ApplyTailMixB(b2)
        -- VTAIL 
        -- Default normal/reverse behaviour 
        if (b2==CH_TYPE.RUD+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x06
        if (b2==CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_RUD end; -- 0x86

        --TAILERON
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE) then return CH_MIX_TYPE.MIX_AIL end; -- 0x03
        if (b2==CH_TYPE.AIL+CH_TYPE.ELE+CH_TYPE.SLAVE) then return CH_MIX_TYPE.MIX_NORM end; -- 0x83
    end

    local function reverseMix(b)
        if (b==CH_MIX_TYPE.MIX_NORM) then return CH_MIX_TYPE.MIX_NORM_REV end;
        if (b==CH_MIX_TYPE.MIX_AIL) then return CH_MIX_TYPE.MIX_AIL_REV end;
        if (b==CH_MIX_TYPE.MIX_ELE) then return CH_MIX_TYPE.MIX_ELE_REV end;
        if (b==CH_MIX_TYPE.MIX_RUD) then return CH_MIX_TYPE.MIX_RUD_REV end;
        return b
    end



    local DSM_ChannelInfo = MODEL.DSM_ChannelInfo 

    for i=0, TX_CHANNELS-1 do
        DSM_ChannelInfo[i] = {[0]= CH_MIX_TYPE.MIX_NORM, CH_TYPE.NONE} -- Initialize with no special function 
    end

    local aircraftType = MENU_DATA[MEMU_VAR.AIRCRAFT_TYPE]
    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 and thrCh < 10) 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 or tailType==TAIL_TYPE.VTAIL_B) then 
        DSM_ChannelInfo[lElevCh][1] = CH_TYPE.RUD+CH_TYPE.ELE
        DSM_ChannelInfo[rElevCh][1] = CH_TYPE.RUD+CH_TYPE.ELE+CH_TYPE.SLAVE
    end

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

    ---- ELEVON :  AIL + ELE 
    if (wingType==WING_TYPE.ELEVON_A or 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

    ------MIXES ---------

    -- TAIL Mixes (Elevator and VTail)
    if (tailType==TAIL_TYPE.VTAIL_A or tailType==TAIL_TYPE.TRAILERON_A or tailType==TAIL_TYPE.TRAILERON_A_R2) 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 or tailType==TAIL_TYPE.TRAILERON_B or tailType==TAIL_TYPE.TRAILERON_B_R2) then 
        DSM_ChannelInfo[lElevCh][0] = ApplyTailMixA(DSM_ChannelInfo[lElevCh][1])
        DSM_ChannelInfo[rElevCh][0] = ApplyTailMixA(DSM_ChannelInfo[rElevCh][1])
    end

     ---- Wing Mixes 
     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

    -- Apply Gyro Reverse as needed for each channel as long as it is used 
    for i=0, TX_CHANNELS-1 do
        if (MENU_DATA[MEMU_VAR.PORT_BASE+i]==CH_MODE_TYPE.REVERSE and DSM_ChannelInfo[i][1]>0) then
            DSM_ChannelInfo[i][0]=reverseMix(DSM_ChannelInfo[i][0])
            DSM_ChannelInfo[i][1]=DSM_ChannelInfo[i][1]+CH_TYPE.REVERSE
        end
    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, ModelLib.channelType2String(b1,b2)))
    end

    MODEL.AirWingTailDesc = string.format("Aircraft(%s) Wing(%s) Tail(%s)",aircraft_type_text[aircraftType],wing_type_text[wingType],tail_type_text[tailType])
end

function ModelLib.ST_PlaneWingInit(wingType) 
    print("Change Plane WingType:"..wing_type_text[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 


    ModelLib.printChannelSummary()
end

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

    print("Change Plane Tail Type:"..tail_type_text[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.PORT4
        MENU_DATA[MEMU_VAR.CH_R_RUD] = PORT.PORT5
    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.PORT4
        MENU_DATA[MEMU_VAR.CH_R_RUD] = PORT.PORT6
    elseif (tailType == TAIL_TYPE.VTAIL_A or tailType == TAIL_TYPE.VTAIL_B) then
        MENU_DATA[MEMU_VAR.CH_L_ELE] = PORT.PORT4
        MENU_DATA[MEMU_VAR.CH_R_ELE] = PORT.PORT3
    elseif (tailType == TAIL_TYPE.TRAILERON_A or tailType==TAIL_TYPE.TRAILERON_A_R2 or
            tailType == TAIL_TYPE.TRAILERON_B or tailType==TAIL_TYPE.TRAILERON_B_R2) then
        MENU_DATA[MEMU_VAR.CH_L_RUD] = PORT.PORT4
        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

    if (tailType == TAIL_TYPE.TRAILERON_A_R2 or tailType==TAIL_TYPE.TRAILERON_B_R2) then
        MENU_DATA[MEMU_VAR.CH_R_RUD] = PORT.PORT7
    end

    ModelLib.printChannelSummary()
end

function ModelLib.ST_GliderWingInit(wingType) 
    print("Change Glider WingType:"..wing_type_text[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 

    ModelLib.printChannelSummary()
end

function ModelLib.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])

    -- 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

    ModelLib.printChannelSummary()
end


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

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

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

   
end


-- Setup Initial Default Data for the Menus
function ModelLib.ST_Default_Data()
    print("Initializing Menu DATA")
    ModelLib.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

    ModelLib.printServoReverseInfo()

end


ModelLib.TX_CHANNELS = TX_CHANNELS
ModelLib.MODEL = MODEL
ModelLib.CH_TYPE = CH_TYPE
ModelLib.CH_MODE_TYPE = CH_MODE_TYPE
ModelLib.AIRCRAFT_TYPE = AIRCRAFT_TYPE
ModelLib.WING_TYPE = WING_TYPE
ModelLib.TAIL_TYPE = TAIL_TYPE
ModelLib.MENU_DATA = MENU_DATA
ModelLib.MEMU_VAR  = MEMU_VAR
ModelLib.PORT      = PORT
ModelLib.DATA_PATH = DATA_PATH

ModelLib.aircraft_type_text = aircraft_type_text
ModelLib.wing_type_text = wing_type_text
ModelLib.tail_type_text = tail_type_text


return ModelLib