// Matrix In Region Libs >
var SAT = require('sat')
var V = SAT.Vector;
var P = SAT.Polygon;
// Matrix In Region Libs />
var P2P = require('pipe2pam')
// pamDiff is based on https://www.npmjs.com/package/pam-diff
var PamDiff = require('pam-diff')
module.exports = function(s,config){
    s.createPamDiffEngine = function(e){
        var width,
            height,
            globalSensitivity,
            globalColorThreshold,
            fullFrame = false
        if(s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_x===''||s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_y===''){
            width = s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_x;
            height = s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_y;
        }else{
            width = e.width
            height = e.height
        }
        if(e.details.detector_sensitivity===''){
            globalSensitivity = 10
        }else{
            globalSensitivity = parseInt(e.details.detector_sensitivity)
        }
        if(e.details.detector_color_threshold===''){
            globalColorThreshold = 9
        }else{
            globalColorThreshold = parseInt(e.details.detector_color_threshold)
        }

        globalThreshold = parseInt(e.details.detector_threshold) || 0

        var regionJson
        try{
            regionJson = JSON.parse(s.group[e.ke].rawMonitorConfigurations[e.id].details.cords)
        }catch(err){
            regionJson = s.group[e.ke].rawMonitorConfigurations[e.id].details.cords
        }

        if(Object.keys(regionJson).length === 0 || e.details.detector_frame === '1'){
            fullFrame = {
                name:'FULL_FRAME',
                sensitivity:globalSensitivity,
                color_threshold:globalColorThreshold,
                points:[
                    [0,0],
                    [0,height],
                    [width,height],
                    [width,0]
                ]
            }
        }

        e.triggerTimer = {}

        var regions = s.createPamDiffRegionArray(regionJson,globalColorThreshold,globalSensitivity,fullFrame)
        var pamDiffOptions = {
            grayscale: 'luminosity',
            regions : regions.forPam
        }
        if(e.details.detector_show_matrix==='1'){
            pamDiffOptions.response = 'bounds'
        }
        s.group[e.ke].activeMonitors[e.id].pamDiff = new PamDiff(pamDiffOptions);
        s.group[e.ke].activeMonitors[e.id].p2p = new P2P()
        var regionArray = Object.values(regionJson)
        if(config.detectorMergePamRegionTriggers === true){
            // merge pam triggers for performance boost
            var buildTriggerEvent = function(trigger){
                var detectorObject = {
                    f:'trigger',
                    id:e.id,
                    ke:e.ke,
                    name:trigger.name,
                    details:{
                        plug:'built-in',
                        name:trigger.name,
                        reason:'motion',
                        confidence:trigger.percent
                    },
                    plates:[],
                    imgHeight:e.details.detector_scale_y,
                    imgWidth:e.details.detector_scale_x
                }
                if(trigger.merged){
                    if(trigger.matrices)detectorObject.details.matrices = trigger.matrices
                    var filteredCount = 0
                    var filteredCountSuccess = 0
                    trigger.merged.forEach(function(triggerPiece){
                        var region = regionArray.find(x => x.name == triggerPiece.name)
                        s.checkMaximumSensitivity(e, region, detectorObject, function(err1) {
                            s.checkTriggerThreshold(e, region, detectorObject, function(err2) {
                                ++filteredCount
                                if(!err1 && !err2)++filteredCountSuccess
                                if(filteredCount === trigger.merged.length && filteredCountSuccess > 0){
                                    detectorObject.doObjectDetection = (s.isAtleatOneDetectorPluginConnected && e.details.detector_use_detect_object === '1')
                                    s.triggerEvent(detectorObject)
                                }
                            })
                        })
                    })
                }else{
                    if(trigger.matrix)detectorObject.details.matrices = [trigger.matrix]
                    var region = regionArray.find(x => x.name == detectorObject.name)
                    s.checkMaximumSensitivity(e, region, detectorObject, function(err1) {
                        s.checkTriggerThreshold(e, region, detectorObject, function(err2) {
                            if(!err1 && !err2){
                                detectorObject.doObjectDetection = (s.isAtleatOneDetectorPluginConnected && e.details.detector_use_detect_object === '1')
                                s.triggerEvent(detectorObject)
                            }
                        })
                    })
                }
            }
            if(e.details.detector_noise_filter==='1'){
                if(!s.group[e.ke].activeMonitors[e.id].noiseFilterArray)s.group[e.ke].activeMonitors[e.id].noiseFilterArray = {}
                var noiseFilterArray = s.group[e.ke].activeMonitors[e.id].noiseFilterArray
                Object.keys(regions.notForPam).forEach(function(name){
                    if(!noiseFilterArray[name])noiseFilterArray[name]=[];
                })
                s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
                    var filteredCount = 0
                    var filteredCountSuccess = 0
                    data.trigger.forEach(function(trigger){
                        s.filterTheNoise(e,noiseFilterArray,regions,trigger,function(err){
                            ++filteredCount
                            if(!err)++filteredCountSuccess
                            if(filteredCount === data.trigger.length && filteredCountSuccess > 0){
                                buildTriggerEvent(s.mergePamTriggers(data))
                            }
                        })
                    })
                })
            }else{
                s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
                    buildTriggerEvent(s.mergePamTriggers(data))
                })
            }
        }else{
            //config.detectorMergePamRegionTriggers NOT true
            //original behaviour, all regions have their own event.
            var buildTriggerEvent = function(trigger){
                var detectorObject = {
                    f:'trigger',
                    id:e.id,
                    ke:e.ke,
                    name:trigger.name,
                    details:{
                        plug:'built-in',
                        name:trigger.name,
                        reason:'motion',
                        confidence:trigger.percent
                    },
                    plates:[],
                    imgHeight:e.details.detector_scale_y,
                    imgWidth:e.details.detector_scale_x
                }
                if(trigger.matrix)detectorObject.details.matrices = [trigger.matrix]
                var region = Object.values(regionJson).find(x => x.name == detectorObject.name)
                s.checkMaximumSensitivity(e, region, detectorObject, function(err1) {
                    s.checkTriggerThreshold(e, region, detectorObject, function(err2) {
                        if(!err1 && ! err2){
                            detectorObject.doObjectDetection = (s.isAtleatOneDetectorPluginConnected && e.details.detector_use_detect_object === '1')
                            s.triggerEvent(detectorObject)
                        }
                    })
                })
            }
            if(e.details.detector_noise_filter==='1'){
                if(!s.group[e.ke].activeMonitors[e.id].noiseFilterArray)s.group[e.ke].activeMonitors[e.id].noiseFilterArray = {}
                var noiseFilterArray = s.group[e.ke].activeMonitors[e.id].noiseFilterArray
                Object.keys(regions.notForPam).forEach(function(name){
                    if(!noiseFilterArray[name])noiseFilterArray[name]=[];
                })
                s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
                    data.trigger.forEach(function(trigger){
                        s.filterTheNoise(e,noiseFilterArray,regions,trigger,function(){
                            s.createMatrixFromPamTrigger(trigger)
                            buildTriggerEvent(trigger)
                        })
                    })
                })
            }else{
                s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
                    data.trigger.forEach(function(trigger){
                        s.createMatrixFromPamTrigger(trigger)
                        buildTriggerEvent(trigger)
                    })
                })
            }
        }
    }

    s.createPamDiffRegionArray = function(regions,globalColorThreshold,globalSensitivity,fullFrame){
        var pamDiffCompliantArray = [],
            arrayForOtherStuff = [],
            json
        try{
            json = JSON.parse(regions)
        }catch(err){
            json = regions
        }
        if(fullFrame){
            json[fullFrame.name]=fullFrame;
        }
        Object.values(json).forEach(function(region){
            if(!region)return false;
            region.polygon = [];
            region.points.forEach(function(points){
                var x = parseFloat(points[0]);
                var y = parseFloat(points[1]);
                if(x < 0)x = 0;
                if(y < 0)y = 0;
                region.polygon.push({
                    x: x,
                    y: y
                })
            })
            if(region.sensitivity===''){
                region.sensitivity = globalSensitivity
            }else{
                region.sensitivity = parseInt(region.sensitivity)
            }
            if(region.color_threshold===''){
                region.color_threshold = globalColorThreshold
            }else{
                region.color_threshold = parseInt(region.color_threshold)
            }
            pamDiffCompliantArray.push({name: region.name, difference: region.color_threshold, percent: region.sensitivity, polygon:region.polygon})
            arrayForOtherStuff[region.name] = region;
        })
        if(pamDiffCompliantArray.length===0){pamDiffCompliantArray = null}
        return {forPam:pamDiffCompliantArray,notForPam:arrayForOtherStuff};
    }

    s.filterTheNoise = function(e,noiseFilterArray,regions,trigger,callback){
        if(noiseFilterArray[trigger.name].length > 2){
            var thePreviousTriggerPercent = noiseFilterArray[trigger.name][noiseFilterArray[trigger.name].length - 1];
            var triggerDifference = trigger.percent - thePreviousTriggerPercent;
            var noiseRange = e.details.detector_noise_filter_range
            if(!noiseRange || noiseRange === ''){
                noiseRange = 6
            }
            noiseRange = parseFloat(noiseRange)
            if(((trigger.percent - thePreviousTriggerPercent) < noiseRange)||(thePreviousTriggerPercent - trigger.percent) > -noiseRange){
                noiseFilterArray[trigger.name].push(trigger.percent);
            }
        }else{
            noiseFilterArray[trigger.name].push(trigger.percent);
        }
        if(noiseFilterArray[trigger.name].length > 10){
            noiseFilterArray[trigger.name] = noiseFilterArray[trigger.name].splice(1,10)
        }
        var theNoise = 0;
        noiseFilterArray[trigger.name].forEach(function(v,n){
            theNoise += v;
        })
        theNoise = theNoise / noiseFilterArray[trigger.name].length;
        var triggerPercentWithoutNoise = trigger.percent - theNoise;
        if(triggerPercentWithoutNoise > regions.notForPam[trigger.name].sensitivity){
            callback(null,trigger)
        }else{
            callback(true)
        }
    }

    s.checkMaximumSensitivity = function(monitor, region, detectorObject, callback) {
        var logName = detectorObject.id + ':' + detectorObject.name
        var globalMaxSensitivity = parseInt(monitor.details.detector_max_sensitivity) || undefined
        var maxSensitivity = parseInt(region.max_sensitivity) || globalMaxSensitivity
        if (maxSensitivity === undefined || detectorObject.details.confidence <= maxSensitivity) {
            callback(null)
        } else {
            callback(true)
            if (monitor.triggerTimer[detectorObject.name] !== undefined) {
                clearTimeout(monitor.triggerTimer[detectorObject.name].timeout)
                monitor.triggerTimer[detectorObject.name] = undefined
            }
        }
    }

    s.checkTriggerThreshold = function(monitor, region, detectorObject, callback){
        var threshold = parseInt(region.threshold) || globalThreshold
        if (threshold <= 1) {
            callback(null)
        } else {
            if (monitor.triggerTimer[detectorObject.name] === undefined) {
                monitor.triggerTimer[detectorObject.name] = {
                    count : threshold,
                    timeout : null
                }
            }
            if (--monitor.triggerTimer[detectorObject.name].count == 0) {
                callback(null)
                clearTimeout(monitor.triggerTimer[detectorObject.name].timeout)
                monitor.triggerTimer[detectorObject.name] = undefined
            } else {
                callback(true)
                var fps = parseFloat(monitor.details.detector_fps) || 2
                if (monitor.triggerTimer[detectorObject.name].timeout !== null)
                    clearTimeout(monitor.triggerTimer[detectorObject.name].timeout)
                monitor.triggerTimer[detectorObject.name].timeout = setTimeout(function() {
                    monitor.triggerTimer[detectorObject.name] = undefined
                }, ((threshold+0.5) * 1000) / fps)
            }
        }
    }
    s.mergePamTriggers = function(data){
        if(data.trigger.length > 1){
            var n = 0
            var sum = 0
            var name = []
            var matrices = []
            data.trigger.forEach(function(trigger){
                name.push(trigger.name + ' ('+trigger.percent+'%)')
                ++n
                sum += trigger.percent
                s.createMatrixFromPamTrigger(trigger)
                if(trigger.matrix)matrices.push(trigger.matrix)
            })
            var average = sum / n
            name = name.join(', ')
            if(matrices.length === 0)matrices = null
            var trigger = {
                name: name,
                percent: parseInt(average),
                matrices: matrices,
                merged: data.trigger
            }
        }else{
            var trigger = data.trigger[0]
            s.createMatrixFromPamTrigger(trigger)
            trigger.matrices = [trigger.matrix]
        }
        return trigger
    }
    s.isAtleastOneMatrixInRegion = function(regions,matrices,callback){
        var regionPolys = []
        var matrixPoints = []
        regions.forEach(function(region,n){
            var polyPoints = []
            region.points.forEach(function(point){
                polyPoints.push(new V(parseInt(point[0]),parseInt(point[1])))
            })
            regionPolys[n] = new P(new V(0,0), polyPoints)
        })
        var collisions = []
        var foundInRegion = false
        matrices.forEach(function(matrix){
            var matrixPoints = [
                new V(matrix.x,matrix.y),
                new V(matrix.width,matrix.y),
                new V(matrix.width,matrix.height),
                new V(matrix.x,matrix.height)
            ]
            var matrixPoly = new P(new V(0,0), matrixPoints)
            regionPolys.forEach(function(region,n){
                var response = new SAT.Response()
                var collided = SAT.testPolygonPolygon(matrixPoly, region, response)
                if(collided === true){
                    collisions.push({
                        matrix: matrix,
                        region: regions[n]
                    })
                    foundInRegion = true
                }
            })
        })
        if(callback)callback(foundInRegion,collisions)
        return foundInRegion
    }
    s.createMatrixFromPamTrigger = function(trigger){
        if(
            trigger.minX &&
            trigger.maxX &&
            trigger.minY &&
            trigger.maxY
        ){
            var coordinates = [
                {"x" : trigger.minX, "y" : trigger.minY},
                {"x" : trigger.maxX, "y" : trigger.minY},
                {"x" : trigger.maxX, "y" : trigger.maxY}
            ]
            var width = Math.sqrt( Math.pow(coordinates[1].x - coordinates[0].x, 2) + Math.pow(coordinates[1].y - coordinates[0].y, 2));
            var height = Math.sqrt( Math.pow(coordinates[2].x - coordinates[1].x, 2) + Math.pow(coordinates[2].y - coordinates[1].y, 2))
            trigger.matrix = {
                x: coordinates[0].x,
                y: coordinates[0].y,
                width: width,
                height: height,
                tag: trigger.name
            }
        }
        return trigger
    }
}
