-- Somfy module example (beside somfy module requires also SJSON module)
-- The rolling code number is stored in the file somfy.cfg.
-- A cached write of the somfy.cfg file is implemented in order to reduce
-- the number of write to the EEPROM memory. Together with the logic of the
-- file module it should allow long lasting operation.

local config_file = "somfy."
local config, config_saved
-- somfy.cfg looks like
-- {"window1":{"rc":1,"address":123},"window2":{"rc":1,"address":124}}

local tmr_cache = tmr.create()
local tmr_delay = tmr.create()

local pin = 4

local function deepcopy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in next, orig, nil do
            copy[deepcopy(orig_key)] = deepcopy(orig_value)
        end
        setmetatable(copy, deepcopy(getmetatable(orig)))
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end

local function readconfig()
    local ln
    if file.exists(config_file.."cfg") then
        print("Reading config from "..config_file.."cfg")
        file.open(config_file.."cfg", "r+")
        ln = file.readline()
        file.close()
    else
        if file.exists(config_file.."bak") then
            print("Reading config from "..config_file.."bak")
            file.open(config_file.."bak", "r+")
            ln = file.readline()
            file.close()
        end
    end
    if not ln then ln = "{}" end
    print("Configuration: "..ln)
    config = sjson.decode(ln)
    config_saved = deepcopy(config)
end

local function writeconfighard()
    print("Saving config")
    file.remove(config_file.."bak")
    file.rename(config_file.."cfg", config_file.."bak")
    file.open(config_file.."cfg", "w+")
    local ok, cfg = pcall(sjson.encode, config)
    if ok then
        file.writeline(cfg)
    else
        print("Config not saved!")
    end
    file.close()

    config_saved = deepcopy(config)
end

local function writeconfig()
    tmr_cache:stop()
    local savenow = false
    local savelater = false

--print("Config: "..sjson.encode(config))
--print("Config saved: "..sjson.encode(config))

    local count = 0
    for _ in pairs(config_saved) do count = count + 1 end
    if count == 0 then
        config_saved = readconfig()
    end
    for remote,cfg in pairs(config_saved) do
        savelater = savelater or not config[remote] or config[remote].rc > cfg.rc
        savenow = savenow or not config[remote] or config[remote].rc > cfg.rc + 10
    end
    savelater = savelater and not savenow
    if savenow then
        print("Saving config now!")
        writeconfighard()
    end
    if savelater then
        print("Saving config later")
        tmr_cache:alarm(65000, tmr.ALARM_SINGLE, writeconfighard)
    end
end

--======================================================================================================--
local function wait(ms, cb, par)
    par = par or {}
    print("wait: ".. ms)
    if cb then tmr_delay:alarm(ms, tmr.ALARM_SINGLE, function () cb(unpack(par)) end) end
end

local function down(remote, cb, par)
    par = par or {}
    print("down: ".. remote)
    config[remote].rc=config[remote].rc+1
    somfy.sendcommand(pin, config[remote].address, somfy.DOWN, config[remote].rc, 16, function() wait(100, cb, par) end)
    writeconfig()
end

local function up(remote, cb, par)
    par = par or {}
    print("up: ".. remote)
    config[remote].rc=config[remote].rc+1
    somfy.sendcommand(pin, config[remote].address, somfy.UP, config[remote].rc, 16, function() wait(100, cb, par) end)
    writeconfig()
end

local function downStep(remote, cb, par)
    par = par or {}
    print("downStep: ".. remote)
    config[remote].rc=config[remote].rc+1
    somfy.sendcommand(pin, config[remote].address, somfy.DOWN, config[remote].rc, 2,  function() wait(300, cb, par) end)
    writeconfig()
end

local function upStep(remote, cb, par) -- luacheck: ignore
    par = par or {}
    print("upStep: ".. remote)
    config[remote].rc=config[remote].rc+1
    somfy.sendcommand(pin, config[remote].address, somfy.UP, config[remote].rc, 2, function() wait(300, cb, par) end)
    writeconfig()
end

--======================================================================================================--
gpio.mode(pin, gpio.OUTPUT, gpio.PULLUP)

if not config then readconfig() end
if #config == 0 then -- somfy.cfg does not exist
    config = sjson.decode([[{"window1":{"rc":1,"address":123},"window2":{"rc":1,"address":124}}]])
    config_saved = deepcopy(config)
end
down('window1',
    wait, {60000,
    up, {'window1',
    wait, {9000,
    downStep, {'window1',
      downStep, {'window1',
        downStep, {'window1',
          downStep, {'window1',
            downStep, {'window1',
              downStep, {'window1',
                downStep, {'window1'
}}}}}}}}}})
