var apikey = null;
var apiversion = 'vnd.ddel.v1';
var isFocused = false;
var curEdit = {};
var lastSavedBinding = {};
var lights = [];
var sensors = [];
var rules = [];
var groups = [];
var PAGE_STATE_INIT = 0;
var PAGE_STATE_UPDATE = 1;
var PAGE_STATE_SAVE = 2;
var PAGE_STATE_IDLE = 3;

var pageState = PAGE_STATE_INIT;

/**
 * Init touch and small displays.
 */
function sensorsInit() {
    pageState = PAGE_STATE_INIT;
    curEdit.light = {};
    curEdit.light.state = {};
    curEdit.light.state.bri = "-";

    // check if this is mobile or desktop
    // and load custom js and css files
    if (Modernizr.touch) {
        loadjscssfile("mobile.css", "css");
        isDesktop = false;
    }
    else {
        isDesktop = true;
        loadjscssfile("desktop.css", "css");
    }

    if (Modernizr.touch) {
        clickevent = "touchend";
        pointerStart = "touchstart";
        pointerEnd = "touchend";
        pointerMove = "touchmove";
    }
}

/**
 * Init the base branding of the interface.
 */
function initBranding() {
    var customBrand = false;

    var navName = "Wireless Light";

    if (customBrand) {
        navName = "CUSTOM <span style=\"position: relative; display: inline-block; bottom: 8px;\">&reg;</span>";
        var brand = '<span class="custom-brand">' + navName + '</span>';
        $(".navbar-inner .brand").html(brand);
    }
}

function buildPage() {
    var rowtxt="";

    rowtxt += '<div class="location span6" style="margin:0px;padding: 10px 0px;max-width:400px;">';
    rowtxt += '<div class="row-fluid" align="center">';
    rowtxt += '<table border="0" style="width:90%"><tbody style="">';

    //Light
    rowtxt += '<tr style=""><td><h4>Light</h4></td><td colspan="2" align="center"><select id="curLight" style="width:220px;">';
    rowtxt += '<option value="none" class="curLightOption">-</option>';
    for (light in lights) {
        var extraClass = " withoutSensors";
        for (sensor in sensors) {
            if (sensors[sensor].mac === lights[light].mac) {
                extraClass = " withSensors";
                break;
            }
        }

        rowtxt += '<option value="' + lights[light].id + '" class="curLightOption' +  extraClass + '">' + lights[light].name + '</option>';
    }
    rowtxt += '</select></td></tr>';

    // Mo Sensor
    rowtxt += '<tr><td colspan="3">&nbsp;</td></tr>';
    rowtxt += '<tr><td colspan="3" align="center"><h4>Sensors</h4></td></tr>';
    rowtxt += '<tr class="moSensorRow not_reachable"><td colspan="2" >Motion</td><td align="right"><input disabled id="moSensor" class="moSensor" type="checkbox"></input></td></tr>';

    rowtxt += '<tr><td colspan="3">&nbsp;</td></tr>';
    //glow duration
    rowtxt += '<tr class="glowDurationRow not_reachable"><td colspan="2">Glow duration</td>';
    rowtxt += '<td><div style="float:right;margin: 20px 0px;"><select id="minutes" style="width:60px;"><option value="0">00</option><option value="1">01</option><option value="2">02</option><option value="3">03</option><option value="4">04</option><option value="5">05</option><option value="6">06</option><option value="7">07</option><option value="8">08</option><option value="9">09</option><option value="10">10</option><option value="15">15</option><option value="20">20</option><option value="25">25</option><option value="30">30</option><option value="45">45</option><option value="60">60</option></select>&nbsp;min&nbsp;<select id="seconds" style="width:60px;"><option value="0">00</option><option value="10">10</option><option value="20">20</option><option value="30">30</option><option value="40">40</option><option value="50">50</option></select>&nbsp;s</div></td>'

    rowtxt += '</tr>';

    rowtxt += '<tr><td colspan="3">&nbsp;</td></tr>';
    // Bri Sensor
    rowtxt += '<tr class="briSensorRow not_reachable"><td colspan="2">Brightness</td><td align="right"><input disabled id="briSensor" class="moSensor" type="checkbox"></input></td></tr>';
    rowtxt += '<tr><td colspan="3">&nbsp;</td></tr>';

    //brightness
    rowtxt += '<tr class="actBriRow not_reachable"><td colspan="2">Measured lux</td><td id="actBriValue" align="right">' + curEdit.light.state.bri + ' </td></tr>';
    rowtxt += '<tr><td colspan="3">&nbsp;</td></tr>';
    rowtxt += '<tr><td colspan="3">&nbsp;</td></tr>';

    //Group
    rowtxt += '<tr><td colspan="3" align="center">Group controlled by Light</td></tr>';
    rowtxt += '<tr><td colspan="3">&nbsp;</td></tr>';
    rowtxt += '<tr><td colspan="3" align="center"><select id="curGroup" style="width:220px;" disabled>';
    rowtxt += '<option value="none">-</option>';
    for (group in groups) {
        rowtxt += '<option value="' + groups[group].id + '">' + groups[group].name + '</option>';
    }
    rowtxt += '</select></td></tr>';

    rowtxt += '<tr><td colspan="3">&nbsp;</td></tr>';
    //save/done
    rowtxt += '<tr><td colspan="3" align="center"><span class="btn btn-primary saveButton" style="margin: 15px 0px;">apply</span>&nbsp;&nbsp;&nbsp;<a href="index.html"><span class="btn actionButton doneButton" style="margin: 15px 0px;">done</span></a></td>';
    rowtxt += '</table></tbody>';
    rowtxt += '</div>';
    rowtxt += '</div>';

    pageLightList.innerHTML += rowtxt;

    // handler when user selects a light from the list
    $("#curLight").change(function() {
        if ($(".curLightOption:selected").val() === "none") {
            $('.moSensorRow').addClass('not_reachable');
            $('.briSensorRow').addClass('not_reachable');
            $('#moSensor').prop("checked", false);
            $('#moSensor').prop("disabled", true);
            $('#briSensor').prop("checked", false);
            $('#briSensor').prop("disabled", true);
            $('#curGroup option[value="none"]').prop('selected',true);
            $('#actBriValue').html("-");
            $('#curGroup').prop('disabled', true);
            checkSensorCheckedState();
        } else {
            var lightId = $(".curLightOption:selected").val();
            location.assign("sensors.html?light=" + lightId);
            //getLightforId($(".curLightOption:selected").val());
        }
    });

    // handler when user selects a group from the list
    $("#curGroup").change(function() {
        if ($("#curGroup option:selected").val() === "none") {
          curEdit.group = undefined;
        } else {
          curEdit.group = {};
          curEdit.group.id = $("#curGroup option:selected").val();
        }
    });

    $('input').change(function() {
        if (this.id === 'moSensor' || this.id === 'briSensor') {
            checkSensorCheckedState();
        }
    });
}

/**
* check if the selected light has sensors and then activates the sensor paramter edit functions on the page
* and starts a search if a group binding exists with the chosen light and sensors.
*/
function checkLightHasSensor(lightMac) {
    $('.moSensorRow').addClass('not_reachable');
    $('#moSensor').prop("disabled", true);
    $('#moSensor').prop("checked", false);
    $('.briSensorRow').addClass('not_reachable');
    $('#briSensor').prop("disabled", true);
    $('#briSensor').prop("checked", false);
    //display no group:
    $('.groupOption').removeProp("selected");
    $('.groupOption[value="none"]').prop("selected", true);
    $('#curGroup').prop('disabled', true);
    //reset current Sensors:
    curEdit.moSensor = undefined;
    curEdit.briSensor = undefined;
    curEdit.ZHASwitch = undefined;

    // measured lux value
    $('#actBriValue').html('-');

    for (sensor in sensors) {
        if (sensors[sensor].mac === lightMac) {
            if ((curEdit.moSensor === undefined) && (curEdit.briSensor === undefined)) {
                $('#curGroup').prop('disabled', false);
            }

            if (sensors[sensor].type === "CLIPPresence" || sensors[sensor].type === "ZHAPresence") { //mo
                $('.moSensorRow').removeClass('not_reachable');
                $('#moSensor').prop('disabled', false);
                //getGroupBoundByLight(sensors[sensor].id);
                if (sensors[sensor].on === true) {            // better: check curEdit.light.state.on
                    $('#moSensor').prop("checked",true);
                    curEdit.moSensorActive = true;
                } else {
                    $('#moSensor').prop("checked",false);
                    curEdit.moSensorActive = false;
                }

                //display glow duration
                if (sensors[sensor].glowDuration !== undefined) {
                    displayGlowDuration();
                }
                curEdit.moSensor = sensors[sensor];
            } else if (sensors[sensor].type.indexOf("ZHALight") !== -1) { //bri
                //display measured lux
                if (sensors[sensor].state.lux !== undefined) {
                    $('#actBriValue').html(sensors[sensor].state.lux);
                }
                $('.briSensorRow').removeClass('not_reachable');
                $('#briSensor').prop('disabled', false);
                //$('#briSelect option:contains('+sensors[sensor].targetbrightness+')').prop('selected',true);
                //getGroupBoundByLight(sensors[sensor].id);
                if (sensors[sensor].on === true) {
                    $('#briSensor').prop("checked",true);
                    curEdit.briSensorActive = true;
                } else {
                    $('#briSensor').prop("checked",false);
                    curEdit.briSensorActive = false;
                }
                curEdit.briSensor = sensors[sensor];
            } else if (sensors[sensor].type === "ZHASwitch") {
                curEdit.ZHASwitch = sensors[sensor];
                getGroupBoundByLight();
            }
        }

        if ((curEdit.moSensor !== undefined) && (curEdit.briSensor !== undefined) && (curEdit.ZHASwitch !== undefined)) {
            break;
        }
    }
    checkSensorCheckedState();
    return;
}

function displayGlowDuration() {
    var input = sensors[sensor].glowDuration;
    var sec = input%60;
    var min = (input - sec)/60;

    if ((sec > 10) && (sec < 20)) { sec = 10; }
    if ((sec > 20) && (sec < 30)) { sec = 20; }
    if ((sec > 30) && (sec < 40)) { sec = 30; }
    if ((sec > 40) && (sec < 50)) { sec = 40; }
    if (sec > 50) { sec = 50; }

    if ((min > 10) && (min < 15)) { min = 10; }
    if ((min > 15) && (min < 20)) { min = 15; }
    if ((min > 20) && (min < 25)) { min = 20; }
    if ((min > 25) && (min < 30)) { min = 25; }
    if ((min > 30) && (min < 45)) { min = 30; }
    if ((min > 45) && (min < 60)) { min = 45; }
    if (min > 60) { min = 60; }

    $("#minutes option[value='"+ min +"']").prop('selected', true);
    $("#seconds option[value='"+ sec +"']").prop('selected', true);
}

/**
* saves the actual settings in an Object and then sends the REST-API messages.
*/
function saveSettings() {
    var found = false;
    var rule;
    var ruleId;
    var rulesToDelete = [];
    curEdit.moSensorActive = document.getElementById("moSensor").checked;
    curEdit.briSensorActive = document.getElementById("briSensor").checked;
    var min = $('#minutes > option:selected').val();
    var sec = $('#seconds > option:selected').val();
    curEdit.glowDuration = ((parseInt(min)*60)+parseInt(sec));

    //send Rest-API
    if ((curEdit.light !== undefined) && ($('#curLight option:selected').val() !== "none")) {
        // all 3 sensors must be known
        if (curEdit.moSensor !== undefined && curEdit.briSensor !== undefined && curEdit.ZHASwitch !== undefined) {

            var groupId = (curEdit.group !== undefined) ? curEdit.group.id : "";
            rulesToDelete = []; // mark IDs of rules to delete
            // handle presence sensor
            curEdit.moSensor.on = $('#moSensor').prop('checked') ? true : false;

            updateMoSensorConfig();

            if (curEdit.moSensor.on && groupId !== "") {
              found = false;
              ruleId = undefined;

              // check if a desired rule already exists and
              // also check if rules exist which should be deleted
              for (r in rules) {
                  if (rules[r].conditions.length === 1 && rules[r].actions.length === 1) {
                      // related to ZHASwitch
                      if (rules[r].conditions[0].address === '/sensors/' + curEdit.ZHASwitch.id + '/state/buttonevent') {
                          // BIND rule with "body.on == true"
                          if (rules[r].actions[0].method === 'BIND' && rules[r].actions[0].body && rules[r].actions[0].body.on) {
                              // destination group correct?
                              if (rules[r].actions[0].address === '/groups/' + groupId + '/action') {
                                  found = true;
                                  ruleId = rules[r].id;
                                  console.log("presence rule already exists");
                              }
                              else {
                                  // non matching groupId -> discard this rule
                                  if (rulesToDelete.indexOf(rules[r].id) === -1) {
                                    rulesToDelete.push(rules[r].id);
                                  }
                              }
                          }
                      }
                  }
              }

              if (groupId !== "") {
                  if (!found) {
                      rule = {  "name": curEdit.ZHASwitch.id + "-on-grp-" + groupId,
                                    "conditions":[{"address":"/sensors/" + curEdit.ZHASwitch.id + "/state/buttonevent","operator":"eq","value":"12"}],
                                    "actions":[{"address":"/groups/" + groupId + "/action","method":"BIND", "body":{"on":true}}]
                                };
                      createRule(rule);
                  }
                  else {
                      // send dummy update so that verification starts immediatly
                      rule = { "name": curEdit.ZHASwitch.id + "-on-grp-" + groupId };
                      updateRule(ruleId, rule);
                  }
              }
            }
            else {
                // delete any rule for the sensor
                for (r in rules) {
                    if (rules[r].conditions.length === 1 && rules[r].actions.length === 1) {
                        // related to ZHASwitch
                        if (rules[r].conditions[0].address === '/sensors/' + curEdit.ZHASwitch.id + '/state/buttonevent') {
                            // BIND rule with "body.on == true"
                            if (rules[r].actions[0].method === 'BIND' && rules[r].actions[0].body && rules[r].actions[0].body.on) {
                                // discard this rule
                                if (rulesToDelete.indexOf(rules[r].id) === -1) {
                                    rulesToDelete.push(rules[r].id);
                                }
                            }
                        }
                    }
                }
            }

            // handle light sensor
            curEdit.briSensor.on = $('#briSensor').prop('checked') ? true : false;

            if (curEdit.briSensor.on  && groupId !== "") {
              found = false;
              ruleId = undefined;

              // check if a desired rule already exists and
              // also check if rules exist which should be deleted
              for (r in rules) {
                  if (rules[r].conditions.length === 1 && rules[r].actions.length === 1) {
                      // related to briSensor
                      if (rules[r].conditions[0].address === '/sensors/' + curEdit.ZHASwitch.id + '/state/buttonevent') {
                          // BIND rule with "body.bri >= 0"
                          if (rules[r].actions[0].method === 'BIND' && rules[r].actions[0].body && rules[r].actions[0].body.bri >= 0) {
                              // destination group correct?
                              if (rules[r].actions[0].address === '/groups/' + groupId + '/action') {
                                  found = true;
                                  ruleId = rules[r].id;
                                  console.log("brightness rule already exists");
                              }
                              else {
                                  // non matching groupId -> discard this rule
                                  if (rulesToDelete.indexOf(rules[r].id) === -1) {
                                    rulesToDelete.push(rules[r].id);
                                  }
                              }
                          }
                      }
                  }
              }

              if (groupId !== "") {
                  if (!found) {
                      rule = {  "name": curEdit.ZHASwitch.id + "-bri-grp-" + groupId,
                                "conditions":[{"address":"/sensors/" + curEdit.ZHASwitch.id + "/state/buttonevent","operator":"eq","value":"12"}],
                                "actions":[{"address":"/groups/" + groupId + "/action", "method":"BIND", "body":{"bri":1}}]
                             };
                      createRule(rule);
                  }
                  else {
                      // send dummy update so that verification starts immediatly
                      rule = { "name": curEdit.ZHASwitch.id + "-bri-grp-" + groupId };
                      updateRule(ruleId, rule);
                  }
              }
            }
            else {
                // delete any rule for the sensor
                for (r in rules) {
                    if (rules[r].conditions.length === 1 && rules[r].actions.length === 1) {
                        // related to briSensor
                        // TODO everything with body == "bri"?
                        if (rules[r].conditions[0].address === '/sensors/' + curEdit.ZHASwitch.id + '/state/buttonevent') {
                            // BIND rule with "body.bri >= 0"
                            if (rules[r].actions[0].method === 'BIND' && rules[r].actions[0].body && rules[r].actions[0].body.bri >= 0) {
                                // discard this rule
                                if (rulesToDelete.indexOf(rules[r].id) === -1) {
                                  rulesToDelete.push(rules[r].id);
                                }
                            }
                        }
                    }
                }
            }

            //
            for (var i = 0; i < rulesToDelete.length; i++) {
              deleteRule(rulesToDelete[i]);
            }

            getRules(false); //false = only reload rules into rules array
            updateBriSensorConfig();
        }
    } else {
        showAlert('alert-info', '<b>Attention!</b> No Light chosen. Nothing to save.');
    }
}

/**
* sets the sensor parameter editable/not editable in association with the sensor checkboxes status
*/
function checkSensorCheckedState() {
    if ($('#moSensor').prop('checked')) {
        $('.glowDurationRow').removeClass('not_reachable');
        $('#glowDuration').prop('disabled', false);
        $('#curGroup').prop('disabled', false);
        $('#minutes').prop('disabled', false);
        $('#seconds').prop('disabled', false);
    } else {
        $('.glowDurationRow').addClass('not_reachable');
        $('#glowDuration').prop('disabled', true);
        $('#minutes').prop('disabled', true);
        $('#seconds').prop('disabled', true);
    }
    if ($('#briSensor').prop('checked')) {
        $('.targetBriRow').removeClass('not_reachable');
        $('.actBriRow').removeClass('not_reachable');
        $('#curGroup').prop('disabled', false);
    } else {
        $('.targetBriRow').addClass('not_reachable');
        $('.actBriRow').addClass('not_reachable');
        //$('#briSelect').prop('disabled', true);
    }
    if (($('#briSensor').prop('checked') === false) && ($('#moSensor').prop('checked') === false)) {
        $('#curGroup').prop('disabled',true);
    }
}

/**
 * Returns the Sensors IDs found in Rule Conditions
 */
function getSensorIdsFromRule(rule) {
    var condAddr = "";
    var conditions = [];
    var conditionSensorIds = [];

    conditions = rule.conditions;

    for (c in conditions){
        condAddr = conditions[c].address;
        conditionSensorIds[conditionSensorIds.length] = /\d+/.exec(condAddr.substring(condAddr.indexOf("sensors")+8,condAddr.indexOf("sensors")+15))[0];
    }

    return conditionSensorIds;
}

/**
 * Returns the Group ID found in a Rule Action
 */
function getGroupIdFromRule(rule) {
    var actionAddr = rule.actions[0].address;
    var actionGroupId = /\d+/.exec(actionAddr.substring(actionAddr.indexOf("groups")+7,actionAddr.indexOf("groups")+13))[0];

    return actionGroupId;
}

/**
 * Sets the curEdit.group and displays the group, bound by a light, in the <select> element.
 */
function getGroupBoundByLight(sensorId) {
    //var boundGroupId = "";
    //reset current group:
    curEdit.group = undefined;
    /*
    for (rule in rules) {
        var conditionSensorIds = getSensorIdsFromRule(rules[rule]);
        for (cid in conditionSensorIds) {
            if (conditionSensorIds[cid] == sensorId) {
                boundGroupId = getGroupIdFromRule(rules[rule]);
                //save curEdit group object
                var group = {};
                group.id = boundGroupId;
                curEdit.group = group;
                //display group
                $('.groupOption').removeProp("selected");
                $('.groupOption[value="' + boundGroupId + '"]').prop("selected",true);
                return;
            }
        }
    }
*/
    if (curEdit.ZHASwitch !== undefined) {
        for (r in rules) {
            if (rules[r].conditions.length === 1 && rules[r].actions.length === 1) {
                // related to ZHASwitch
                if (rules[r].conditions[0].address === '/sensors/' + curEdit.ZHASwitch.id + '/state/buttonevent') {
                    // BIND rule with "body.on == true"
                    if (rules[r].actions[0].method === 'BIND' && rules[r].actions[0].body && (rules[r].actions[0].body.on || rules[r].actions[0].body.bri)) {
                        // destination group id
                        var ls = rules[r].actions[0].address.split("/"); // /groups/<id>/action

                        if (ls.length === 4 && ls[1] === "groups" && ls[3] === "action") {
                            var group = {};
                            group.id = ls[2];
                            curEdit.group = group;
                            //display group
                          //$('#curGroup option').removeProp("selected");
                            $('#curGroup option[value=' + ls[2] + ']').prop('selected',true);
                            return;
                        }
                    }
                }
            }
        }
    }

    $('#curGroup option[value="none"]').prop('selected',true);
}

/**
 * returns true when rules[] contain a rule with curEdit.group in Actions and Sensors of current Light in Conditions.
 */
function ruleExists() {
    for (rule in rules){
        var conditionSensorIds = getSensorIdsFromRule(rules[rule]);
        var actionGroupId = getGroupIdFromRule(rules[rule]);

        if ((actionGroupId === curEdit.group.id)) {
            if ((conditionSensorIds.indexOf(curEdit.moSensor.id) !== -1) || (conditionSensorIds.indexOf(curEdit.briSensor.id) !== -1)) {
                return true;
            }
        }
    }
    return false;
}

/**
 * returns true if a rule exists with the given rule Id and with the same Sensors as the current selected.
 */
function ruleHasCurrentSensors(ruleId) {
    var curCheckedSensorIds = [];
    if ($('#moSensor').prop('checked') === true){curCheckedSensorIds[curCheckedSensorIds.length] = curEdit.moSensor.id}
    if ($('#briSensor').prop('checked') === true){curCheckedSensorIds[curCheckedSensorIds.length] = curEdit.briSensor.id}

    for (rule in rules){
        if (rules[rule].id === ruleId){
            var conditionSensorIds = getSensorIdsFromRule(rules[rule]);
            if ((conditionSensorIds.length === 1) && (curCheckedSensorIds.length == 1)){
                if (conditionSensorIds[0] === curCheckedSensorIds[0]) {
                    return true;
                }
            } else if ((conditionSensorIds.length === 2) && (curCheckedSensorIds.length == 2)){
                if ((conditionSensorIds.indexOf(curCheckedSensorIds[0]) !== -1) && (conditionSensorIds.indexOf(curCheckedSensorIds[1]) !== -1)) {
                    return true;
                }
            }
            return false;
        }
    }
    return false;
}

/**
 * returns the rule ids for the selected light (if light has sensors and a rule with these sensors exists).
 */
function getRuleIdsForCurSensor() {
    var ruleIds = [];
    for (rule in rules){
        var conditionSensorIds =  getSensorIdsFromRule(rules[rule]);
        var doubles = false;
        for (cid in conditionSensorIds) {
            //check rule if match with currend sensors (=light)
            //todo: nochmal funktion testen!
            if ((curEdit.moSensor.id == conditionSensorIds[cid]) || (curEdit.briSensor.id == conditionSensorIds[cid])) {
                if (doubles == false) {
                    ruleIds[ruleIds.length] = rules[rule].id;
                }
                doubles = true;
            }
        }
    }
    return ruleIds;
}

/**
* GET /api/<apikey>/sensors
*/
function getSensors() {
    $.ajax({
        url: 'api/' + apikey + '/sensors',
        dataType: 'json',
        type: 'GET',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        headers: { 'Accept': apiversion },
        success: function(json, status, xhr) {
            for (let key in json){
                let s = json[key];
                if (!s.modelid || s.modelid.indexOf('FLS-NB') === -1)
                    continue;

                if (s.config && s.config.reachable === true) {
                    var sensor = {};
                    sensor.id = key;
                    sensor.type = s.type;
                    sensor.uniqueid = s.uniqueid;
                    sensor.mac = sensor.uniqueid.split('-')[0] || undefined;
                    sensor.on = s.config.on;

                    if (s.config.duration !== undefined) {
                        sensor.glowDuration = s.config.duration;
                    }
                    sensor.state = s.state;

                    if (sensor.state && sensor.mac) {
                        sensors[sensors.length] = sensor;
                    }
                }
            }
            getRules(true);
        },
        error: function(jqXHR, textStatus, errorThrown) {
            switch (jqXHR.status) {
            case 403:
                window.location.assign("/pwa/login.html");
                break;

            default:
                setTimeout(getSensors, 3000);
                showAlert('alert-error', '<b>Error!</b> Lost connection, retry in one second ...');
                break;
            }
        },
        timeout: 10000
    });
}

/**
* GET /api/<apikey>/rules
* opt == false: only reload rules into rules array
*/
function getRules(opt) {
    rules = [];
    $.ajax({
        url: 'api/' + apikey + '/rules',
        dataType: 'json',
        type: 'GET',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        headers: { 'Accept': apiversion },
        success: function(json, status, xhr) {
            for (key in json){
                let r = json[key];

                if (r.status !== "enabled")
                    continue;

                if (Array.isArray) {
                    if (!Array.isArray(r.actions))
                        continue;
                    if (!Array.isArray(r.conditions))
                        continue;
                }

                if (!r.actions || r.actions.length !== 1)
                    continue;

                if (!r.conditions || r.conditions.length !== 1)
                    continue;

                let unsup = r.actions.find(x => x.method !== 'BIND');
                if (unsup)
                    continue;

                var rule = {};
                rule.id = key;
                rule.actions = r.actions;
                rule.conditions = r.conditions;
                rules[rules.length] = rule;
            }
            if (opt !== false){
                getFullConfiguration();
            }
        },
        error: function(jqXHR, textStatus, errorThrown) {
            switch (jqXHR.status) {
            case 403:
                window.location.assign("/pwa/login.html");
                break;

            default:
                setTimeout(getRules, 3000);
                showAlert('alert-error', '<b>Error!</b> Lost connection, retry in one second ...');
                break;
            }
        },
        timeout: 10000
    });
}

/**
* GET /api/<apikey>/sensors
*/
function updateValues() {

    if (pageState === PAGE_STATE_IDLE) {
        pageState = PAGE_STATE_UPDATE;
    }
    else {
        setTimeout(updateValues, 1000);
        return;
    }

    $.ajax({
        url: 'api/' + apikey + '/sensors',
        dataType: 'json',
        type: 'GET',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        headers: { 'Accept': apiversion },
        success: function(json, status, xhr) {
            for (key in json) {
                if (curEdit.briSensor && curEdit.briSensor.type && curEdit.briSensor.uniqueid && curEdit.briSensor.state &&
                    json[key].type === curEdit.briSensor.type &&
                    json[key].uniqueid === curEdit.briSensor.uniqueid) {

                    // if sensor becomes unreachable reload page
                    if (json[key].config) {
                        if (json[key].config.reachable === false) {
                            location.reload(true);
                        }
                    }

                    if (json[key].state && json[key].state.lux) {
                        curEdit.briSensor.state.lux = json[key].state.lux;
                        console.log("update lux " + curEdit.briSensor.state.lux);

                        //display measured lux
                        $('#actBriValue').html(curEdit.briSensor.state.lux);
                    }
                }
            }

            pageState = PAGE_STATE_IDLE;
            setTimeout(updateValues, 5000);
        },
        error: function(jqXHR, textStatus, errorThrown) {
            pageState = PAGE_STATE_IDLE;
            switch (jqXHR.status) {
            case 403:
                window.location.assign("/pwa/login.html");
                break;

            default:
                setTimeout(updateValues, 3000);
                showAlert('alert-error', '<b>Error!</b> Lost connection, retry in one second ...');
                break;
            }
        },
        timeout: 10000
    });
}

/**
* GET /api/<apikey>
*/
function getFullConfiguration() {
    $.ajax({
        url: 'api/' + apikey,
        dataType: 'json',
        type: 'GET',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        headers: { 'Accept': apiversion },
        success: function(json, status, xhr) {
            for (key in json.lights) {
                if (json.lights[key].modelid === "FLS-NB1" || json.lights[key].modelid === "FLS-NB2") {
                    var light = {};
                    light.id = key;
                    light.name = json.lights[key].name;
                    light.uniqueid = json.lights[key].uniqueid;
                    light.mac = light.uniqueid.split('-')[0];

                    lights[lights.length] = light;
                }
            }

            for (key in json.groups){
                var group = {};
                group.id = key;
                group.name = json.groups[key].name;
                groups[groups.length] = group;
            }
            buildPage();

            var lightId = getURLParameter("light");

            if (lightId !== null) {
              getLightforId(lightId);
            }

            pageState = PAGE_STATE_IDLE;
            setTimeout(updateValues, 1000);
        },
        error: function(jqXHR, textStatus, errorThrown) {
            switch (jqXHR.status) {
            case 403:
                window.location.assign("/pwa/login.html");
                break;

            default:
                setTimeout(getLights, 3000);
                showAlert('alert-error', '<b>Error!</b> Lost connection, retry in one second ...');
                break;
            }
        },
        timeout: 10000
    });
}

/**
* GET /api/<apikey>/lights
*/
function getLights() {
    $.ajax({
        url: 'api/' + apikey + '/lights',
        dataType: 'json',
        type: 'GET',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        headers: { 'Accept': apiversion },
        success: function(json, status, xhr) {
            for (key in json){
                if (json[key].modelId === "FLS-NB1" || json[key].modelId === "FLS-NB2") {
                    var light = {};
                    light.id = key;
                    light.name = json[key].name;
                    light.uniqueid = json[key].uniqueid;
                    light.mac = light.uniqueid.split('-')[0];
                    lights[lights.length] = light;
                }
            }
            getGroups()
        },
        error: function(jqXHR, textStatus, errorThrown) {
            switch (jqXHR.status) {
            case 403:
                window.location.assign("/pwa/login.html");
                break;

            default:
                setTimeout(getLights, 3000);
                showAlert('alert-error', '<b>Error!</b> Lost connection, retry in one second ...');
                break;
            }
        },
        timeout: 10000
    });
}

/**
* GET /api/<apikey>/groups
*/
function getGroups() {
    $.ajax({
        url: 'api/' + apikey + '/groups',
        dataType: 'json',
        type: 'GET',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        headers: { 'Accept': apiversion },
        success: function(json, status, xhr) {
            for (key in json){
                var group = {};
                group.id = key;
                group.name = json[key].name;
                groups[groups.length] = group;
            }
            buildPage();
        },
        error: function(jqXHR, textStatus, errorThrown) {
            switch (jqXHR.status) {
            case 403:
                window.location.assign("/pwa/login.html");
                break;

            default:
                setTimeout(getGroups, 3000);
                showAlert('alert-error', '<b>Error!</b> Lost connection, retry in one second ...');
                break;
            }
        },
        timeout: 10000
    });
}

/**
* GET /api/<apikey>/lights/<id>
*/
function getLightforId(lid) {
    $.ajax({
        url: 'api/' + apikey + '/lights/' + lid,
        dataType: 'json',
        type: 'GET',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        headers: { 'Accept': apiversion },
        success: function(json, status, xhr) {
            curEdit.light = {};
            curEdit.light.state = json.state;
            curEdit.light.id = lid;
            curEdit.light.uniqueid = json.uniqueid;
            curEdit.light.mac = json.uniqueid.split('-')[0];

            $('#curLight option[value=' + lid + ']').prop('selected',true);

            checkLightHasSensor(curEdit.light.mac);
        },
        error: function(jqXHR, textStatus, errorThrown) {
            switch (jqXHR.status) {
            case 403:
                window.location.assign("/pwa/login.html");
                break;

            default:
                //setTimeout(getLightforId, 3000);
                showAlert('alert-error', '<b>Error!</b> Lost connection.');
                break;
            }
        },
        timeout: 10000
    });
}

function sendLightState() {
    $.ajax({
        url: 'api/' + apikey + '/lights/' + curEdit.light.id + '/state',
        dataType: 'json',
        type: 'PUT',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        headers: { 'Accept': apiversion },
        data: '{"fadeouttime":'+curEdit.fadeOutTime+',"dimupspeed":'+curEdit.dimUpSpeed+',"dimdownspeed":'+curEdit.dimDownSpeed+',"dimuphyst":'+curEdit.dimUpHyst+',"dimdownhyst":'+curEdit.dimDownHyst+'}',
        success: function(json) {
            showAlert('alert-success', '<b>OK!</b> Light state saved.');
            //todo: update lightArray
        },
        error: function(jqXHR, textStatus, errorThrown) {
            showAlert('alert-error', '<b>Error!</b> Failed to save Light state.');
            console.log("textStatus:", textStatus + ' ' + errorThrown);
            console.log(jqXHR.responseText);
            //setTimeout(sendLightState, 5000);
        },
        timeout: 10000,
    });
}

function sendOnWithTimedOff() {

    var onTime = curEdit.glowDuration * 10;

    if (!curEdit.group ||!curEdit.group.id || !(onTime > 0 && onTime <= 65535)) {
        return;
    }

    $.ajax({
        url: 'api/' + apikey + '/groups/' + curEdit.group.id + '/action',
        dataType: 'json',
        type: 'PUT',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        headers: { 'Accept': apiversion },
        data: '{"on": true, "ontime":' + onTime + ' }',
        success: function(json) {
//            showAlert('alert-success', '<b>OK!</b> Sent fake PIR event.');
        },
        error: function(jqXHR, textStatus, errorThrown) {
            showAlert('alert-error', '<b>Error!</b> Failed to sent fake PIR event.');
            console.log("textStatus:", textStatus + ' ' + errorThrown);
            console.log(jqXHR.responseText);
            //setTimeout(sendLightState, 5000);
        },
        timeout: 10000,
    });
}

function updateMoSensorConfig() {
    var sendData = {};

    if (curEdit.moSensor === undefined) {
        return;
    }

    if (curEdit.glowDuration === undefined || curEdit.moSensorActive === undefined) {
        return;
    }

    sendData.on = (curEdit.moSensorActive === true);

    if (curEdit.moSensorActive === true) {
        sendData.duration = curEdit.glowDuration;
    }

    $.ajax({
        url: 'api/' + apikey + '/sensors/' + curEdit.moSensor.id + '/config',
        dataType: 'json',
        type: 'PUT',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        headers: { 'Accept': apiversion },
        data: JSON.stringify(sendData),
        success: function(json) {
            showAlert('alert-success', '<b>OK!</b> Motion Sensor settings saved.');
            //update sensorArray
            for (sensor in sensors) {
                if (sensors[sensor].id === curEdit.moSensor.id) {
                    sensors[sensor].glowDuration = curEdit.glowDuration;
                    sensors[sensor].on = curEdit.moSensorActive;
                    break;
                }
            }

            if (curEdit.moSensorActive) {
                setTimeout(sendOnWithTimedOff, 500);
            }
        },
        error: function(jqXHR, textStatus, errorThrown) {
            showAlert('alert-error', '<b>Error!</b> Failed to save Motion Sensor settings.');
            console.log("textStatus:", textStatus + ' ' + errorThrown);
            console.log(jqXHR.responseText);
            //setTimeout(updateMoSensorConfig, 5000);
        },
        timeout: 10000,
    });
}

function updateBriSensorConfig() {
    if (curEdit.briSensor === undefined) {
        return;
    }

    if (curEdit.briSensorActive === undefined) {
        return;
    }

    var sendData = {};

    sendData.on = (curEdit.briSensorActive === true);

    if (curEdit.briSensorActive === true) {
        if (curEdit.targetBri !== undefined) {
            sendData.targetbrightness = curEdit.targetBri;
        }
    }

    $.ajax({
        url: 'api/' + apikey + '/sensors/' + curEdit.briSensor.id + '/config',
        dataType: 'json',
        type: 'PUT',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        headers: { 'Accept': apiversion },
        data: JSON.stringify(sendData),
        success: function(json) {
            showAlert('alert-success', '<b>OK!</b> Brightness Sensor settings saved.');
            for (sensor in sensors) {
                if (sensors[sensor].id == curEdit.briSensor.id) {
                    sensors[sensor].targetbrightness = curEdit.targetBri;
                    sensors[sensor].on = curEdit.briSensorActive;
                    break;
                }
            }
        },
        error: function(jqXHR, textStatus, errorThrown) {
            showAlert('alert-error', '<b>Error!</b> Failed to save Brightness Sensor settings.');
            console.log("textStatus:", textStatus + ' ' + errorThrown);
            console.log(jqXHR.responseText);
            //setTimeout(updateBriSensorConfig, 5000);
        },
        timeout: 10000,
    });
}

function createRule(rule) {
    $.ajax({
        url: 'api/' + apikey + '/rules/',
        dataType: 'json',
        type: 'POST',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        headers: { 'Accept': apiversion },
        data: JSON.stringify(rule),
        success: function(json) {
            console.log("created rule" + JSON.stringify(rule));
            getRules(false); //false = only reload rules into rules array
        },
        error: function(jqXHR, textStatus, errorThrown) {
            console.log("textStatus:", textStatus + ' ' + errorThrown);
            console.log(jqXHR.responseText);
            console.log("failed to create rule" + JSON.stringify(rule));
            //setTimeout(createNewRule, 5000);
        },
        timeout: 10000,
    });
}

function updateRule(ruleId, rule) {
    if (ruleId === undefined)
        return;

    $.ajax({
        url: 'api/' + apikey + '/rules/' + ruleId,
        dataType: 'json',
        type: 'PUT',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        headers: { 'Accept': apiversion },
        data: JSON.stringify(rule),
        success: function(json) {
            console.log("updated rule" + ruleId + " " + JSON.stringify(rule));
            getRules(false); //false = only reload rules into rules array
        },
        error: function(jqXHR, textStatus, errorThrown) {
            console.log("textStatus:", textStatus + ' ' + errorThrown);
            console.log(jqXHR.responseText);
            console.log("failed to update rule" + ruleId + " " + JSON.stringify(rule));
        },
        timeout: 10000,
    });
}


//uptdate rule if another group was chosen
/*
function updateRule(ruleId) {

    var newCond = "{\"conditions\":[";

    if ($('#moSensor').prop('checked') === true) {
        newCond += "{\"address\":\"/sensors/"+ curEdit.moSensor.id +"/state/presence\",\"operator\":\"eq\",\"value\":\"true\"}";
        mo = true;
    }
    if (($('#moSensor').prop('checked') === true) && ($('#briSensor').prop('checked') == true)) {
        newCond += ",";
    }
    if ($('#briSensor').prop('checked') === true) {
        newCond += "{\"address\":\"/sensors/"+ curEdit.briSensor.id +"/state/status\",\"operator\":\"dx\"}";
    }
    newCond += "]}";

    $.ajax({
        url: 'api/' + apikey + '/rules/' + ruleId,
        dataType: 'json',
        type: 'PUT',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        headers: { 'Accept': apiversion },
        data: newCond,
        success: function(json) {
            showAlert('alert-success', '<b>OK!</b> Binding updated.');
            getRules(false); //false = only reload rules into rules array
        },
        error: function(jqXHR, textStatus, errorThrown) {
            showAlert('alert-error', '<b>Error!</b> Failed to update Binding.');
            console.log("textStatus:", textStatus + ' ' + errorThrown);
            console.log(jqXHR.responseText);
            //setTimeout(createNewRule, 5000);
        },
        timeout: 10000,
    });
}
*/

function deleteRule(ruleId) {

    $.ajax({
        url: 'api/' + apikey + '/rules/' + ruleId,
        dataType: 'json',
        type: 'DELETE',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        headers: { 'Accept': apiversion },
        success: function(json) {
            console.log("deleted rule " + ruleId);
        },
        error: function(jqXHR, textStatus, errorThrown) {
            console.log("failed to deleted rule " + ruleId);
            console.log("textStatus:", textStatus + ' ' + errorThrown);
            console.log(jqXHR.responseText);
            //setTimeout(deleteRule, 5000);
        },
        timeout: 10000,
    });
}

function showAlert(alert, text, container) {
    if (typeof(container) === 'undefined') {
        container = '#alerts';
    }

    var d = new Date();
    var timestr = d.toUTCString();
    var txt = "";

    txt += '<div class="alert ' + alert + '">';
    txt += text;
    txt += ' <span class="pull-right hidden-phone">';
    txt += timestr;
    txt += '</span></div>';

    setTimeout(function() {
        $(container).html(txt).scrollintoview();
    }, 250);
}
