
local class = require('pl.class')
local tablex = require('pl.tablex')

local ModbusClient = require('luamods.modbus_scan.ModbusClient')
local TemplateCollection = require('luamods.modbus_scan.TemplateCollection')

local Scanner = class()

function Scanner:_init(template_dir, endpoint)
    self.template_dir = template_dir
    self.endpoint = endpoint
    self.templates = TemplateCollection()
    self.modbus_client = ModbusClient(endpoint)
end

function Scanner:prepare()
    -- Read all templates into a template collection, which provides comparison facilities for
    -- fetched data.
    local errors = self.templates:load_template_file_list(self.template_dir)
    if errors then
        -- Join errors and return.
        local err = ''
        for _, e in ipairs(errors) do
            err = err .. e .. '\n'
        end
        return err
    end

    -- Setup the modbus client.
    -- Try connect to the endpoint.
    if not self.modbus_client:try_connect() then
        return 'could not connect to ' .. self.endpoint
    end

    return nil
end

function Scanner:get_host()
    return self.modbus_client.host
end

function Scanner:get_port()
    return self.modbus_client.service
end

function Scanner:scan_slaves(slave_count)
    slave_count = slave_count or 15

    local all_slave_templates = {}

    for slave_num = 1, slave_count do
        self.modbus_client:with_slave(slave_num, function ()
            local function test_magic_on_slave(magic)
                local read_magic = self.modbus_client:read_magic(magic)
                if not read_magic then
                    return false, 'could not read'
                end

                return magic == read_magic, nil
            end

            -- Find all possible templates for the slave.
            local templates = self.templates:test_with(test_magic_on_slave)

            -- Store the slave and its possible templates if there were any.
            if #templates > 0 then
                -- Sort the templates in alphabetical order (by file path).
                local sorted_templates = {}
                local iter = tablex.sortv(templates, function (a, b) return a.file_path < b.file_path end)
                while true do
                    local _, elem = iter()
                    if not elem then
                        break
                    end
                    table.insert(sorted_templates, elem)
                end

                -- Store found templates for the slave.
                table.insert(all_slave_templates, {
                    number = slave_num,
                    host = self.modbus_client.host,
                    service = self.modbus_client.service,
                    templates = sorted_templates
                })
            end
        end)
    end

    return all_slave_templates
end

function Scanner:close()
    self.modbus_client:close()
end

return Scanner
