#! /usr/bin/lua

local hardware = require("aredn.hardware")
local iwinfo = require("iwinfo")

function execute(args)
    local result = { changed = false, failed = false }

    local config = {}
    for line in io.lines("/etc/config.mesh/_setup")
    do
        local k, v = line:match("^(%S+)%s=%s(.*)$")
        config[k] = v
    end

    if args.enable == false then
        if config.wifi_enable ~= 0 then
            config.wifi_enable = '0'
            result.changed = true
        end
    else
        if config.wifi_enable == 0 then
            config.wifi_enable = '1'
            result.changed = true
        end
    end
    if args.address then
        if not args.address:match("^10%.%d+%.$d+%.%d+$") then
            result.failed = true
            result.msg = "bad address"
            return result
        end
        if args.address ~= config.wifi_ip then
            config.wifi_ip = args.address
            result.changed = true
        end
    end
    if args.ssid and args.ssid ~= config.wifi_ssid then
        config.wifi_ssid = args.ssid
        result.changed = true
    end
    if args.channel then
        local channel = tostring(args.channel)
        local freqs = iwinfo.nl80211.freqlist(hardware.get_iface_name("wifi"))
        if freqs then
            local found = false
            for _, f in ipairs(freqs)
            do
                local chan = f.channel
                if chan > 184 then
                    chan = chan - 256
                end
                if tostring(chan) == channel then
                    found = true
                    break
                end
            end
            if not found then
                result.failed = true
                result.msg = "unsupported channel: " .. args.channel
                return result
            end
        end
        if config.wifi_channel ~= channel then
            config.wifi_channel = channel
            result.changed = true
        end
    end
    if args.channel_width then
        local channel_width = tonumber(args.channel_width)
        if not (channel_width == 5 or channel_width == 10 or channel_width == 20) then
            result.failed = true
            result.msg = "bad channel width: " .. args.channel_width
            return result
        end
        if tostring(channel_width) ~= config.wifi_chanbw then
            config.wifi_chanbw = channel_width
            result.changed = true
        end
    end
    if args.tx_power then
        local tx_power = tonumber(args.tx_power)
        if args.tx_power == "max" then
            tx_power = hardware.wifi_maxpower(tonumber(config.wifi_channel))
        elseif not tx_power then
            result.failed = true
            result.msg = "bad tx_power: " .. args.tx_power
            return
        elseif tx_power < 1 then
            tx_power = 1
        else
            local max = hardware.wifi_maxpower(tonumber(config.wifi_channel))
            if tx_power > max then
                tx_power = max
            end
        end
        if tostring(tx_power) ~= config.wifi_txpower then
            config.wifi_txpower = tx_power
            result.changed = true
        end
    end
    if args.distance then
        local distance = args.distance == "auto" and 0 or tonumber(args.distance)
        if not distance then
            result.failed = true;
            result.msg = "bad distance: " .. args.distance
            return result
        end
        if tostring(distance) ~= config.wifi_distance then
            config.wifi_distance = distance
            result.changed = true
        end
    end

    if result.changed then
        local f = io.open("/etc/config.mesh/_setup", "w")
        if f then
            for k, v in pairs(config)
            do
                f:write(k .. " = " .. v .. "\n")
            end
            f:close()
        end
    end

    return result
end

-- Boilerplate below --

local json = require("luci.jsonc")

local success, result = pcall(execute, json.parse([[<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>]]))
if not success then
    result = {
        failed = true,
        changed = false,
        msg = result
    }
else
    if result.failed ~= false then
        result.failed = true
    end
    if result.changed ~= false then
        result.changed = true
    end
end
io.write(json.stringify(result))
