(function () {
    'use strict';
    angular.module('uniflash').service('ufPropertyRules', ufPropertyRules);
    ufPropertyRules.$inject = ['ufPropertiesValue'];
    function ufPropertyRules(ufPropertiesValue) {
        var dynamicRules = [];
        var configInfo;
        function initialize(info) {
            dynamicRules = [];
            configInfo = info;
        }
        // parse the property object for rules to add and apply
        function addRule(property, isa, coreName) {
            if (property.uniqueDefault) {
                parseRuleTypeObj(property.uniqueDefault, 'uniqueDefault', property, isa, coreName);
            }
            if (property.disableIf) {
                parseRuleTypeObj(property.disableIf, 'disableIf', property, isa, coreName);
            }
            if (property.enableIf) {
                parseRuleTypeObj(property.enableIf, 'enableIf', property, isa, coreName);
            }
        }
        // private function for handling different types of rules (uniqueDefault, disableIf, enableIf)
        function parseRuleTypeObj(typeObj, type, property, isa, coreName) {
            var value;
            if (type === 'uniqueDefault') {
                value = convertValue(property.valueType, typeObj._value);
            }
            // parse the rule by getting the key and value pairs
            for (var key in typeObj) {
                if (typeObj.hasOwnProperty(key) && (key !== '_value')) {
                    // if rule contains multiple different sub rules, it will be stored as an array
                    if (typeObj[key] instanceof Array) {
                        _.each(typeObj[key], function (curRuleOption) {
                            if (key === 'property') {
                                // rules based on other properties are stored as dynamic rules
                                addDynamicRule(property._id, key, curRuleOption, type, value, isa, coreName, typeObj[key].length);
                            }
                            else {
                                // rules based on attributes will be parse immediately
                                evaluateStaticRule(property._id, key, curRuleOption, type, value, isa, coreName);
                            }
                        });
                    }
                    else {
                        // parsing single rules
                        if (key === 'property') {
                            addDynamicRule(property._id, key, typeObj[key], type, value, isa, coreName, 1);
                        }
                        else {
                            evaluateStaticRule(property._id, key, typeObj[key], type, value, isa, coreName);
                        }
                    }
                }
            }
        }
        // private function for converting value to necessary type
        function convertValue(type, value) {
            if (type === 'Boolean') {
                return (value === '1' ? true : false);
            }
            if (type === 'Numeric') {
                return parseInt(value);
            }
            return value;
        }
        // private function for determining if a rule matches to the given conditions
        function evaluateStaticRule(id, key, value, type, result, isa, coreName) {
            var rule = {
                'id': id,
                'key': key,
                'value': value,
                'type': type,
                'result': result,
                'coreName': coreName
            };
            if (((rule.key === 'target') && (rule.value === isa)) ||
                ((rule.key === 'os') && (rule.value === TICloudAgent.getOS())) ||
                ((rule.key === 'connection') && (configInfo.connectionXMLFile.toLowerCase().indexOf(rule.value.toLowerCase()) !== -1)) ||
                matchKey(rule)) {
                applyMatchedRule(rule, coreName);
            }
        }
        // private function for determining if a rule matches to the given conditions
        function addDynamicRule(id, key, value, type, result, isa, coreName, addDynamicRule) {
            var rule = {
                'id': id,
                'key': key,
                'type': type,
                'result': result,
                'coreName': coreName,
                'dependentId': value._id,
                'dependentValue': value.__text
            };
            dynamicRules.push(rule);
        }
        // private function for determine if a key matches any of the provided config properties
        function matchKey(rule) {
            // try to match one of the connection properties
            if (configInfo.connectionProperties && configInfo.connectionProperties[rule.key] && (configInfo.connectionProperties[rule.key] === rule.value)) {
                return true;
            }
            // try to match one of the device properties
            if (configInfo.deviceProperties && configInfo.deviceProperties[rule.key] && (configInfo.deviceProperties[rule.key].Value === rule.value)) {
                return true;
            }
            // try to match partnum
            if (rule.key === 'partnum' && rule.value === configInfo.partnum) {
                return true;
            }
            // custom rule for the TargetVoltage "FROM_DEVICE" unique_default rule
            if ((rule.key === 'target_vcc_default') && isNaN(rule.result) && configInfo.deviceProperties[rule.key]) {
                rule.result = parseInt(configInfo.deviceProperties[rule.key].Value);
                return true;
            }
            return false;
        }
        // after a rule has been matched, apply the logic of the rule
        function applyMatchedRule(rule, coreName) {
            var valueObj = ufPropertiesValue.getValueObj(rule.id, coreName);
            if (rule.type === 'uniqueDefault') {
                valueObj.value = rule.result;
            }
            else if (rule.type === 'disableIf') {
                valueObj.disabled = true;
            }
            else if (rule.type === 'enableIf') {
                valueObj.disabled = false;
            }
        }
        // resolve all dynamic rules related to a property ID and core.
        // call this when a property value has change
        function resolvePropertyRules(id, coreName, valueObj) {
            _.each(dynamicRules, function (curRule) {
                if ((id === curRule.dependentId) && (coreName === curRule.coreName)) {
                    var affectedPropertyValue = ufPropertiesValue.getValueObj(curRule.id, curRule.coreName);
                    var condition = (valueObj.value === convertValue(valueObj.valueType, curRule.dependentValue));
                    // change the disabled status
                    // a matched condition means that we should apply the rule (disable if evaluating disableIf, enable if evaluating enableIf)
                    // or if there are no related rules, it applies directly
                    if (condition) {
                        affectedPropertyValue.disabled = (curRule.type === 'disableIf');
                    }
                    else if (curRule.numRelated === 1) {
                        affectedPropertyValue.disabled = !(curRule.type === 'disableIf');
                    }
                    else {
                        affectedPropertyValue.disabled = evaluateAllRelatedRules(curRule);
                    }
                }
            });
        }
        // evaluate all related rules to see if we should disable or enable a property
        function evaluateAllRelatedRules(rule) {
            var matchingRelatedRules = _.filter(dynamicRules, function (curRule) {
                if ((curRule.id === rule.id) && (curRule.type === rule.type) && (curRule.coreName === rule.coreName)) {
                    var valueObj = ufPropertiesValue.getValueObj(curRule.dependentId, curRule.coreName);
                    return (valueObj.value === convertValue(valueObj.valueType, curRule.dependentValue));
                }
            });
            // at least one related rule matched
            if (matchingRelatedRules.length > 0) {
                return (rule.type === 'disableIf');
            }
            else {
                return (rule.type !== 'disableIf');
            }
        }
        // evaluate and resolve all 
        function resolveAllRules() {
            _.each(dynamicRules, function (curRule) {
                var curValueObj = ufPropertiesValue.getValueObj(curRule.dependentId, curRule.coreName);
                if (curValueObj) {
                    resolvePropertyRules(curRule.dependentId, curRule.coreName, curValueObj);
                }
            });
        }
        return {
            initialize: initialize,
            addRule: addRule,
            resolvePropertyRules: resolvePropertyRules,
            resolveAllRules: resolveAllRules
        };
    }
})();
