Logger:Info("-- download_handler_7.lua: init optional modules")

-- load modules
local sdamanager = require("sdamanager_module")
local monitoring = require("monitoring_module")
local datalogger = require("datalogger_module")
local timeserver = require("timemanager_module")
local recording = require("recording_module")
local usc = require("usc_module")

-- defines
local productInterfaceObjectId = 60001
local notificationInterfaceObjectId = 50001
local timeServerInterfaceObjectId = 50002
local dataLoggerInterfaceObjectId = 50003
local recordingInterfaceObjectId = 50004

-- set the configuration as KnxDevice channel parameter set for the web UI
local configuration = {}

-- usage statistics client config
local uscConfig = {}

-- sdamanager module
sdamanager.AddCosToMap()
local productInterfaceObject = PropertyService:GetInterfaceObjectByTypeAndInstance(productInterfaceObjectId, 1)
local sdaManagerAppConfig = {}
uscConfig["sdamanager"] = sdaManagerAppConfig
sdaManagerAppConfig.GeneralRemoteAccessAfterRestart = productInterfaceObject:GetPropertyById(51):GetValue():GetAsUInt8()
sdaManagerAppConfig.ResidentRemoteAccessAfterRestart = productInterfaceObject:GetPropertyById(52):GetValue():GetAsUInt8()
sdaManagerAppConfig.InstallerRemoteAccessAfterRestart = productInterfaceObject:GetPropertyById(53):GetValue():GetAsUInt8()
sdamanager.Configure(sdaManagerAppConfig)
table.insert(configuration, "SdaService")

-- timeserver module
local timeServerInterfaceObject = PropertyService:GetInterfaceObjectByTypeAndInstance(timeServerInterfaceObjectId, 1)
local timeServerAppConfig = {}
uscConfig["timeserver"] = timeServerAppConfig
timeServerAppConfig.EnabledBool = timeServerInterfaceObject:GetPropertyById(51):GetValue():GetAsUInt8() == 1
timeServerAppConfig.SendTimeInterval = timeServerInterfaceObject:GetPropertyById(52):GetValue():GetAsUInt8()
timeServerAppConfig.SendDateInterval = timeServerInterfaceObject:GetPropertyById(53):GetValue():GetAsUInt8()
local timeZoneProperty = productInterfaceObject:GetPropertyById(57)
if timeZoneProperty:GetNumberOfElements() == 1 then
  timeServerAppConfig.TimezonePDBId = timeZoneProperty:GetValue():GetAsUInt16()
end
timeserver.Configure(timeServerAppConfig)
if timeServerAppConfig.EnabledBool then
    timeserver.AddCosToMap()
    table.insert(configuration, "TimeServer")
end

-- datalogger module
local dataLoggerInterfaceObject = PropertyService:GetInterfaceObjectByTypeAndInstance(dataLoggerInterfaceObjectId, 1)
local dataLoggerAppConfig = {}
uscConfig["datalogger"] = dataLoggerAppConfig
dataLoggerAppConfig.EnabledBool = dataLoggerInterfaceObject:GetPropertyById(53):GetValue():GetAsUInt8() == 1
dataLoggerAppConfig.LoggingFormat = dataLoggerInterfaceObject:GetPropertyById(51):GetValue():GetAsUInt8()
dataLoggerAppConfig.BufferType = dataLoggerInterfaceObject:GetPropertyById(52):GetValue():GetAsUInt8()
datalogger.Configure(dataLoggerAppConfig)
if dataLoggerAppConfig.EnabledBool then
    datalogger.AddCosToMap()
    table.insert(configuration, "DataLogger")
end

-- monitoring module (notification)
local floatingPointSeparator = PropertyService:GetInterfaceObjectByTypeAndInstance(productInterfaceObjectId, 1):GetPropertyById(55):GetValue():GetAsUInt8()
local numberOfNotifications = PropertyService:GetInterfaceObjectByTypeAndInstance(productInterfaceObjectId, 1):GetPropertyById(54):GetValue():GetAsUInt8()
local monitoringAppConfig = {}
uscConfig["monitoring"] = monitoringAppConfig
monitoringAppConfig.EnabledBool = false
monitoringAppConfig.NumberOfNotifications = 0
if numberOfNotifications > 0 then
    Logger:Info(string.format("-- found %d notification COs", numberOfNotifications))
    local notificationStartCo = 101

    local notificationDptIdToDptString = { [0] = "1.001", [1] = "5.001", [2] = "5.010", [3] = "9.*", [4] = "16.001", [5] = "14.*", [6] = "7.*", [7] = "13.*" }
    for channelNumber = 1, numberOfNotifications do
        local co = notificationStartCo + channelNumber - 1
        local groupObjectDescriptor = GroupValueService:GetGroupObjectDescriptor(co)
        if not groupObjectDescriptor:IsCommunicationEnabled() then
            Logger:Info("Skip notification " .. channelNumber .. " because communication not enabled")
        else
            monitoringAppConfig.EnabledBool = true
            monitoringAppConfig.NumberOfNotifications = monitoringAppConfig.NumberOfNotifications + 1
            local notificationInterfaceObject = PropertyService:GetInterfaceObjectByTypeAndInstance(notificationInterfaceObjectId, channelNumber)
            local channelDptId = notificationInterfaceObject:GetPropertyById(60):GetValue():GetAsUInt8()
            local config = {}
            config.DptId = channelDptId
            config.FloatingPointSeparator = floatingPointSeparator
            config.Filter = notificationInterfaceObject:GetPropertyById(51):GetValue():GetAsString()
            config.NotifyOnlyOnChange = notificationInterfaceObject:GetPropertyById(52):GetValue():GetAsUInt8() == 1
            if config.NotifyOnlyOnChange == true and channelDptId ~= 0 then
                config.Threshold = notificationInterfaceObject:GetPropertyById(53):GetValue():GetAsFloat()
            end
            
            config.Priority = notificationInterfaceObject:GetPropertyById(54):GetValue():GetAsUInt8()
            config.Category = notificationInterfaceObject:GetPropertyById(55):GetValue():GetAsString()
            config.Subject = notificationInterfaceObject:GetPropertyById(56):GetValue():GetAsString()
            config.Text = notificationInterfaceObject:GetPropertyById(57):GetValue():GetAsString()
            config.AttachmentURL = notificationInterfaceObject:GetPropertyById(59):GetValue():GetAsString()	
            
            helper.AddAssociatedCoToMap(co, "CW", notificationDptIdToDptString[channelDptId], string.format("%s:Monitoring-%d:InputValue", deviceUrn_base, channelNumber))
            monitoring.CreateNotificationDefiniton(channelNumber, config);
        end
    end
end
if monitoringAppConfig.EnabledBool then
    table.insert(configuration, "Notification")
end
monitoring.Configure(monitoringAppConfig)

-- recording module
local numberOfRecordings = PropertyService:GetInterfaceObjectByTypeAndInstance(productInterfaceObjectId, 1):GetPropertyById(56):GetValue():GetAsUInt8()
local recordingAppConfig = {}
uscConfig["recording"] = recordingAppConfig
recordingAppConfig.EnabledBool = false
recordingAppConfig.NumberOfRecordings = 0
if numberOfRecordings > 0 then
    recording.CheckDatabase(200)
    Logger:Info(string.format("-- found %d recording COs", numberOfRecordings))
    local recordingStartCo = 201
    local recordingDptMainTyppeToTypeUrns = {
        [1] = "de.gira.schema.channels.Recording.Binary",	-- ivtBinary	de.gira.schema.datapoints.Binary
        [5] = "de.gira.schema.channels.Recording.Byte",		-- ivtByte		de.gira.schema.datapoints.Byte
        [7] = "de.gira.schema.channels.Recording.Word",		-- ivtWord		de.gira.schema.datapoints.Word
        [9] = "de.gira.schema.channels.Recording.Float",	-- ivtFloat		de.gira.schema.datapoints.Float
        [12] = "de.gira.schema.channels.Recording.DWord",	-- ivtDWord		de.gira.schema.datapoints.DWord
        [13] = "de.gira.schema.channels.Recording.Integer",	-- ivtInteger	de.gira.schema.datapoints.Integer
        [14] = "de.gira.schema.channels.Recording.Float",	-- ivtFloat		de.gira.schema.datapoints.Float
        [20] = "de.gira.schema.channels.Recording.Byte"	-- ivtByte		de.gira.schema.datapoints.Byte 
    }
    local aggregationFunctionMapping = {
        [0] = "none",
        [1] = "min",
        [2] = "max",
        [3] = "avg"	
    }
    for channelNumber = 1, numberOfRecordings do
    
        local co = recordingStartCo + channelNumber - 1
        local groupObjectDescriptor = GroupValueService:GetGroupObjectDescriptor(co)
        
        local recordingInterfaceObject = PropertyService:GetInterfaceObjectByTypeAndInstance(recordingInterfaceObjectId, channelNumber)
        local dptMainType = recordingInterfaceObject:GetPropertyById(52):GetValue():GetAsUInt8()
        local dptSubType = recordingInterfaceObject:GetPropertyById(53):GetValue():GetAsUInt16()
	    local config = {}        
        config.Name = recordingInterfaceObject:GetPropertyById(51):GetValue():GetAsString()
        config.DPT = string.format(dptSubType < 100 and "%d.%03d" or "%d.%d", dptMainType, dptSubType)
        config.TypeURN = recordingDptMainTyppeToTypeUrns[dptMainType]
        -- look for special cases for main type 5
        if dptMainType == 5 then
            if dptSubType == 1 or dptSubType == 4 or dptSubType == 3 then
                config.TypeURN = recordingDptMainTyppeToTypeUrns[9]	-- float
            end          
        end
        config.AggregationFunctionNum = recordingInterfaceObject:GetPropertyById(54):GetValue():GetAsUInt8()
        config.AggregationFunctionString = aggregationFunctionMapping[config.AggregationFunctionNum]
        if not groupObjectDescriptor:IsCommunicationEnabled() then
            Logger:Info("Skip recording " .. channelNumber .. " because communication not enabled")
            recording.ConfigureDatabaseChannel(channelNumber, config, 0)
        else
            recordingAppConfig.EnabledBool = true
            recordingAppConfig.NumberOfRecordings = recordingAppConfig.NumberOfRecordings + 1
            helper.AddAssociatedCoToMap(co, "CW", config.DPT, string.format("%s:Recording-%d:Value", deviceUrn_base, channelNumber))
            recording.CreateRecordingDefiniton(channelNumber, config)
            recording.ConfigureDatabaseChannel(channelNumber, config, 1)
        end
    end
    -- need to unconfigure remaining channels; max of 200 channels
    recording.UnconfigureDatabaseChannels(numberOfRecordings+1, 200)
    recording.CloseDatabase()
else
    Logger:Info(string.format("-- found no recording COs!"))
    recording.DeleteDatabase()
end
if recordingAppConfig.EnabledBool then
    table.insert(configuration, "Recording")
end
recording.ConfigureAppChannel(recordingAppConfig)

-- fill the KNX-Device-1 with the loaded configuration
local channel = device:AddChannel("KNX-Device-1")
channel:AddParameterSet("Application", "Configuration", table.concat(configuration,","))

-- collect and send usage statistics data
usc.CollectData(uscConfig) 
usc.TriggerSendingData()

Logger:Info("-- download_handler_7.lua: done")
