(function () {
    'use strict';
    angular.module('uniflash').service('ufPropertiesDB', ufPropertiesDB);
    ufPropertiesDB.$inject = ['$q', '$http', 'x2js', 'ufPropertiesValue', 'ufSearch', 'menuContribSrv', 'ufTargetConfig', 'ufPropertyRules', 'ufMemoryRanges'];
    function ufPropertiesDB($q, $http, x2js, ufPropertiesValue, ufSearch, menuContribSrv, ufTargetConfig, ufPropertyRules, ufMemoryRanges) {
        var propertiesJSON = [];
        var curConfigInfo;
        var includeGeneric = true;
        var init = false;
        var showID = false;
        var curFilterStr = '';
        var partnumLC = '';
        var flashPropertiesTranslatorJSON;
        var generatedCategoryList = [];
        function getPartnum() {
            return partnumLC;
        }
        // query for the properties data, given the device, connection, and configuration information
        function getPropertiesData(configInfo) {
            var deferred = $q.defer();
            ufPropertyRules.initialize(configInfo, this);
            if (!flashPropertiesTranslatorJSON) {
                ufTargetConfig.getFileContent("ccs_base/DebugServer/propertyDB/FlashPropertiesTranslator.xml")
                    .then(function (content) {
                    if (content instanceof Blob) {
                        var reader = new FileReader();
                        reader.onload = function (readContent) {
                            flashPropertiesTranslatorJSON = x2js.xml_str2json(readContent.currentTarget.result); // convert XML to JSON
                            getPropertiesDataInternal(configInfo, deferred);
                        };
                        return reader.readAsText(content);
                    }
                    else {
                        flashPropertiesTranslatorJSON = x2js.xml_str2json(content); // convert XML to JSON
                        getPropertiesDataInternal(configInfo, deferred);
                    }
                });
            }
            else {
                getPropertiesDataInternal(configInfo, deferred);
            }
            menuContribSrv.editMenu('Save Session', false);
            return deferred.promise;
        }
        function getPropertiesDataInternal(configInfo, deferred) {
            curConfigInfo = configInfo;
            var url = '';
            partnumLC = curConfigInfo.partnum.toLowerCase();
            if (curConfigInfo.connectionXMLFile.indexOf('j-link') !== -1) {
                // don't need to get the flash properties for j-link; we should be checking 'disableInternalFlashManager', but we are not currently getting the driver properties right now
                propertiesJSON = [];
                deferred.resolve();
                return;
            }
            // special case for certain msp430 g2 devices
            if ((partnumLC === 'f20x1_g2x0x_g2x1x') || (partnumLC === 'f20x1_g2x0x_g2x1x')) {
                partnumLC = 'msp430';
            }
            // look up the property file name
            var matchedPropertyXML = matchPartToPropertiesXML(partnumLC, curConfigInfo.connectionXMLFile);
            if (matchedPropertyXML.type === 'internal') {
                url = 'flash_properties/data/' + matchedPropertyXML.propertyFileName;
                $http.get(url).then(function (response) {
                    initPropertiesData(response.data);
                    init = true;
                    deferred.resolve();
                }, function (error) {
                    console.log(url + ': ' + error.data);
                    propertiesJSON = [];
                    deferred.resolve();
                });
            }
            else {
                ufTargetConfig.getFileContent('ccs_base/DebugServer/propertyDB/' + matchedPropertyXML.propertyFileName)
                    .then(function (content) {
                    if (content instanceof Blob) {
                        var reader = new FileReader();
                        reader.onload = function (readContent) {
                            if (readContent.currentTarget.result !== 'File not found') {
                                initPropertiesData(readContent.currentTarget.result);
                                init = true;
                                deferred.resolve();
                            }
                            else {
                                console.log('ccs_base/DebugServer/propertyDB/' + matchedPropertyXML.propertyFileName + ': File not found');
                                propertiesJSON = [];
                                deferred.resolve();
                            }
                        };
                        return reader.readAsText(content);
                    }
                    else {
                        initPropertiesData(content);
                        init = true;
                        deferred.resolve();
                    }
                }, function (error) {
                    console.log(url + ': ' + error.data);
                    propertiesJSON = [];
                    deferred.resolve();
                });
            }
        }
        function matchPartToPropertiesXML(partnumLC, connectionXML) {
            var rtnVal = { type: '', propertyFileName: '' };
            // internal matching first, these are special XML files that were modified for UniFlash used.
            // These will not be needed when all of the existing XML files are converted.
            rtnVal.type = 'internal';
            if ((partnumLC.indexOf('bootloader') === -1) &&
                ((partnumLC.indexOf('msp430') !== -1) ||
                    (partnumLC.indexOf('rf430') !== -1) ||
                    (partnumLC.indexOf('cc430') !== -1) ||
                    ((partnumLC.indexOf('msp432') !== -1) && (connectionXML.indexOf('TIMSP430-USB') !== -1)))) {
                rtnVal.propertyFileName = 'PropertiesDB.xml';
                includeGeneric = false;
            }
            if (!rtnVal.propertyFileName) {
                // get the property XML from ticloudagent
                rtnVal.type = 'ticloudagent';
                var matchedFlashPropertyRule = _.find(flashPropertiesTranslatorJSON.FlashPropertiesTranslator.FlashProperties, function (curFlashPropertyRules) {
                    if (curFlashPropertyRules.partnum._beginsWith || curFlashPropertyRules.partnum._endsWith || curFlashPropertyRules.partnum._contains || curFlashPropertyRules.partnum._regex) {
                        // single <partnum> rule available. Check if the current partnum matches the rule
                        return matchRule(curFlashPropertyRules.partnum, partnumLC);
                    }
                    else {
                        // multiple <partnum> rule available. Check if any of the rules matched
                        var curPartnumRule = _.find(curFlashPropertyRules.partnum, function (curPartnum) {
                            return matchRule(curPartnum, partnumLC);
                        });
                        return curPartnumRule;
                    }
                });
                if (matchedFlashPropertyRule) {
                    // if a rule matched, use the matching 'name' of the XML file to use
                    rtnVal.propertyFileName = matchedFlashPropertyRule._name + '_FlashProperties.xml';
                }
                else if ((partnumLC.indexOf('msp430') !== -1) ||
                    (partnumLC.indexOf('rf430') !== -1) ||
                    (partnumLC.indexOf('cc430') !== -1) ||
                    ((partnumLC.indexOf('msp432') !== -1) && (connectionXML.indexOf('TIMSP430-USB') !== -1))) {
                    // special case for MSP430, use the PropertiesDB.xml
                    rtnVal.propertyFileName = 'PropertiesDB.xml';
                }
                else {
                    // no rules matches, just use the name of the partnum
                    rtnVal.propertyFileName = curConfigInfo.partnum + '_FlashProperties.xml';
                }
            }
            return rtnVal;
        }
        // return true if rule matches the partnum
        function matchRule(rule, partnum) {
            var partnumToMatch;
            var ruleToMatch;
            if (rule._regex) {
                return partnum.match(new RegExp(rule._regex, 'gi'));
            }
            else {
                if (rule._beginsWith) {
                    ruleToMatch = rule._beginsWith.toLowerCase();
                    partnumToMatch = partnum.substring(0, rule._beginsWith.length);
                }
                else if (rule._endsWith) {
                    ruleToMatch = rule._endsWith.toLowerCase();
                    partnumToMatch = partnum.substring(partnum.length - rule._endsWith.length, partnum.length);
                }
                // wildcard not supported in 'contains' rules yet
                if (ruleToMatch.indexOf('*') !== -1) {
                    for (var i = 0; i < partnumToMatch.length; i++) {
                        if ((ruleToMatch.charAt(i) !== '*') && (partnumToMatch.charAt(i) !== ruleToMatch.charAt(i))) {
                            return false;
                        }
                    }
                    return true;
                }
                else {
                    return ((rule._beginsWith && (partnumToMatch === ruleToMatch) && checkMSP432FETRule(rule, partnum)) ||
                        (rule._endsWith && (partnumToMatch === ruleToMatch) && checkMSP432FETRule(rule, partnum)) ||
                        (rule._contains && (partnum.indexOf(rule._contains.toLowerCase()) !== -1) && checkMSP432FETRule(rule, partnum)));
                }
            }
        }
        // handle msp432fet rule in FlashPropertiesTranslator.xml
        // return true if rule does not exist, or if exist and we are currently using MSP432+FET
        function checkMSP432FETRule(rule, partnum) {
            if (rule._msp432fet) {
                return isMSP432FET(partnum, curConfigInfo.connectionXMLFile);
            }
            return true;
        }
        // parse the XML content coming from the selected properties XML file
        function initPropertiesData(content) {
            propertiesJSON = [];
            generatedCategoryList = [];
            ufPropertiesValue.reset();
            ufMemoryRanges.clear();
            angular.forEach(curConfigInfo.cores, function (core) {
                if (core.isa === 'CS_DAP' || core.isa === 'CS_DAP_PC' || core.isa === 'CS_ETB' || core.isa === 'ETB11' || core.isa === 'ICEPICK_B' || core.isa === 'ICEPICK_C' || core.isa === 'ICEPICK_D' || core.isa === 'cs_child') {
                    return;
                }
                if (content !== '') {
                    var curPropertiesJSON_1 = x2js.xml_str2json(content); // convert XML to JSON
                    var filteredPropertiesList_1 = [];
                    var curISA_1 = translateToISA(core.isa);
                    _.each(curPropertiesJSON_1.PropertyList.property, function (curProperty) {
                        if (curProperty.target === 'generic' || ((typeof curProperty.target === 'string') && (curProperty.target === curISA_1))) {
                            filteredPropertiesList_1.push(curProperty);
                        }
                        else {
                            // target is an array instead of string (multiple ISAs given), need to find a match in the array
                            var match = _.find(curProperty.target, function (curTarget) {
                                return (curTarget === curISA_1);
                            });
                            if (match !== undefined) {
                                filteredPropertiesList_1.push(curProperty);
                            }
                        }
                    });
                    // add custom properties (ie; FlashVerbose)
                    filteredPropertiesList_1.push({
                        '_id': 'FlashVerboseMode',
                        'defaultValue': '0',
                        'name': 'Verbose Output',
                        'target': 'generic',
                        'valueType': 'Boolean'
                    });
                    // update the properties list
                    curPropertiesJSON_1.PropertyList.property = filteredPropertiesList_1;
                    // initialize the value list
                    ufPropertiesValue.initialize(curPropertiesJSON_1, curConfigInfo, core);
                    _.each(curPropertiesJSON_1.PropertyList.property, function (property) {
                        var curValueObj = ufPropertiesValue.getValueObj(property._id, core.name);
                        if (_.contains(property._id, "MemoryRange")) {
                            var dashIndex = curValueObj.value.indexOf("-");
                            var startAddress = curValueObj.value.substring(0, dashIndex);
                            var endAddress = curValueObj.value.substring(dashIndex + 1);
                            ufMemoryRanges.addMemoryRange({
                                address: startAddress,
                                length: parseInt(endAddress) - parseInt(startAddress) + 1,
                                name: property.name,
                            });
                        }
                        else {
                            // change boolean strings to values
                            if (property.valueType === 'Boolean') {
                                curValueObj.value = (curValueObj.value === '1' ? true : false);
                            }
                            // change numeric strings to int
                            if (property.valueType === 'Numeric') {
                                curValueObj.value = parseInt(curValueObj.value);
                            }
                            // if enableIf rules or hidden tag exist, the property is disabled by default
                            if (property.enableIf || (property.hidden === '')) {
                                curValueObj.disabled = true;
                            }
                            // parse each property and apply the rules (enable/disable/uniquDefault)
                            ufPropertyRules.addRule(property, curISA_1, core.name);
                        }
                    });
                    propertiesJSON.push({ 'name': core.name, 'properties': curPropertiesJSON_1 });
                    // search through groups and nodes to tag property parents
                    var mainNodeChildrenList = [];
                    if ((partnumLC.indexOf('bootloader') === -1) &&
                        ((partnumLC.indexOf('msp430') !== -1) ||
                            (partnumLC.indexOf('rf430') !== -1) ||
                            (partnumLC.indexOf('cc430') !== -1) ||
                            isMSP432FET(partnumLC, curConfigInfo.connectionXMLFile))) {
                        var mainNode = _.find(curPropertiesJSON_1.PropertyList.node, function (curNode) { return (curNode._id === 'MSP430PropertiesNode'); });
                        mainNodeChildrenList = mainNode.children.child;
                    }
                    else {
                        mainNodeChildrenList = curPropertiesJSON_1.PropertyList.node.children.child;
                    }
                    // parse the Nodes and fill out the parent information
                    _.each(mainNodeChildrenList, function (id) {
                        var curProp = getProperty(id, core.name);
                        if (curProp) {
                            curProp.parentId = curPropertiesJSON_1.PropertyList.node._id;
                            if (!curProp.categories) {
                                curProp.categories = {};
                                curProp.categories.category = curPropertiesJSON_1.PropertyList.node.name;
                            }
                            if (curProp.categories.category && !_.contains(generatedCategoryList, curProp.categories.category)) {
                                generatedCategoryList.push(curProp.categories.category);
                            }
                        }
                    });
                    // parse the Groups and fill out the parent information
                    _.each(curPropertiesJSON_1.PropertyList.group, function (group) {
                        if (group.children.child instanceof Array) {
                            _.each(group.children.child, function (id) {
                                var curProp = getProperty(id, core.name);
                                if (curProp) {
                                    curProp.parentId = group._id;
                                    if (!curProp.categories) {
                                        curProp.categories = {};
                                        curProp.categories.category = group.name;
                                    }
                                    if (curProp.categories.category && !_.contains(generatedCategoryList, curProp.categories.category)) {
                                        generatedCategoryList.push(curProp.categories.category);
                                    }
                                }
                            });
                        }
                        else {
                            var curProp = getProperty(group.children.child, core.name);
                            if (curProp) {
                                curProp.parentId = group._id;
                                if (!curProp.categories) {
                                    curProp.categories = {};
                                    curProp.categories.category = group.name;
                                }
                                if (curProp.categories.category && !_.contains(generatedCategoryList, curProp.categories.category)) {
                                    generatedCategoryList.push(curProp.categories.category);
                                }
                            }
                        }
                    });
                    init = true;
                }
            });
            ufPropertyRules.resolveAllRules();
        }
        // if device contains MSP432 and connection is TIMSP430-USB, we are using MSP432+FET
        function isMSP432FET(partnum, connectionXML) {
            return (partnum.indexOf('msp432') !== -1) && (connectionXML.indexOf('TIMSP430-USB') !== -1);
        }
        // return the full properties list (Note: no nodes or groups)
        function getPropertiesList(filtered, core) {
            var properties = getPropertyByCore(core);
            if (properties && properties.PropertyList) {
                if (filtered) {
                    var matchedProperties = _.filter(properties.PropertyList.property, function (prop) {
                        var idMatched = (prop._id.toLowerCase().indexOf(curFilterStr.toLowerCase()) !== -1);
                        var nameMatched = (prop.name.toLowerCase().indexOf(curFilterStr.toLowerCase()) !== -1);
                        return (idMatched && nameMatched);
                    });
                    return matchedProperties;
                }
                else {
                    properties.PropertyList.property;
                }
            }
            else {
                return [];
            }
        }
        // return properties list by 1 or more categories
        function getPropertiesListByCategory(categories, includeButton, core, searchable) {
            var properties = getPropertyByCore(core);
            if (properties && properties.PropertyList && (properties.PropertyList.categories || generatedCategoryList.length > 0)) {
                var matchedParentIDs = [];
                if (searchable) {
                    _.each(properties.PropertyList.property, function (prop) {
                        if ((prop.hidden !== '') &&
                            ((prop._id.toLowerCase().indexOf(curFilterStr.toLowerCase()) !== -1) ||
                                (prop.name.toLowerCase().indexOf(curFilterStr.toLowerCase()) !== -1))) {
                            matchedParentIDs.push(prop.parentId);
                        }
                    });
                }
                var matchedProperties = _.filter(properties.PropertyList.property, function (prop) {
                    prop.matchedId = false;
                    // custom code to remove the Flash Validate properties from the UI for now
                    if (prop._id.indexOf('FlashProfileValidate') !== -1 || (prop._id === 'Separator_Text_Prop') || (prop.hidden === '')) {
                        return false;
                    }
                    if (!includeButton && (prop.valueType === 'Button')) {
                        return false;
                    }
                    var matched = _.find(categories, function (category) {
                        return matchCategory(prop, category);
                    });
                    var matched2 = true;
                    var idMatched = false;
                    var nameMatched = false;
                    var valueMatchedList = [];
                    var parentMatched = false;
                    if (searchable) {
                        if (curFilterStr !== '') {
                            idMatched = (prop._id.toLowerCase().indexOf(curFilterStr.toLowerCase()) !== -1);
                            nameMatched = (prop.name.toLowerCase().indexOf(curFilterStr.toLowerCase()) !== -1);
                            if (prop.valueType === 'StringList' || prop.valueType === 'ChoiceList') {
                                valueMatchedList = _.filter(prop.values.value, function (curValue) {
                                    return (curValue.toLowerCase().indexOf(curFilterStr.toLowerCase()) !== -1);
                                });
                            }
                            var foundProp = _.find(matchedParentIDs, function (parentId) {
                                return (parentId === prop.parentId);
                            });
                            parentMatched = (foundProp !== undefined);
                            matched2 = (idMatched || nameMatched || parentMatched || valueMatchedList.length > 0);
                        }
                        if (matched2 && (prop._id.toLowerCase().indexOf(curFilterStr.toLowerCase()) !== -1) &&
                            (prop.name.toLowerCase().indexOf(curFilterStr.toLowerCase()) === -1) &&
                            (valueMatchedList.length === 0)) {
                            prop.matchedId = true;
                        }
                    }
                    var curValueObj = ufPropertiesValue.getValueObj(prop._id, core);
                    return (matched !== undefined) && matched2 && (!curValueObj || !curValueObj.disabled);
                });
                return matchedProperties;
            }
            else {
                return [];
            }
        }
        function getFilter() {
            return curFilterStr;
        }
        function setFilter(filterStr) {
            curFilterStr = filterStr;
        }
        function getCategoryList(core) {
            var properties = getPropertyByCore(core);
            if (properties && properties.PropertyList.categories) {
                if (typeof getPropertyByCore(core).PropertyList.categories.category === 'string') {
                    var rtnArr = [];
                    rtnArr.push(getPropertyByCore(core).PropertyList.categories.category);
                    return rtnArr;
                }
                else {
                    return getPropertyByCore(core).PropertyList.categories.category;
                }
            }
            else {
                if (generatedCategoryList.length > 0) {
                    return generatedCategoryList;
                }
                else {
                    return ['All']; // default category array
                }
            }
        }
        function getCategoryName(category) {
            if (typeof category === 'string') {
                return category;
            }
            else {
                return category.__text;
            }
        }
        function getCategoryToggle(category) {
            if (typeof category === 'string') {
                return false;
            }
            else {
                return true;
            }
        }
        function getProperty(propID, core) {
            if (getPropertyByCore(core)) {
                return _.find(getPropertyByCore(core).PropertyList.property, function (curProp) { return (curProp._id.toLowerCase() === propID.toLowerCase()); });
            }
            return null;
        }
        function matchTarget(prop, targetToMatch) {
            if (typeof prop.target === 'string') {
                return ((targetToMatch === prop.target) || (includeGeneric && (prop.target === 'generic')));
            }
            else {
                var match = _.find(prop.target, function (curTarget) {
                    return ((targetToMatch === curTarget) || (includeGeneric && (curTarget === 'generic')));
                });
                if (match !== undefined) {
                    return true;
                }
            }
            return false;
        }
        function matchCategory(prop, catToMatch) {
            if (prop.categories) {
                if (typeof prop.categories.category === 'string') {
                    return (catToMatch.toLowerCase() === prop.categories.category.toLowerCase());
                }
                else {
                    var match = _.find(prop.categories.category, function (curCat) {
                        return (catToMatch.toLowerCase() === curCat.toLowerCase());
                    });
                    if (match !== undefined) {
                        return true;
                    }
                }
            }
            return false;
        }
        function getPropertyByCore(coreName) {
            var coreProperties = _.find(propertiesJSON, function (coreProperties) { return (coreProperties.name === coreName); });
            if (coreProperties) {
                return coreProperties.properties;
            }
            else {
                return null;
            }
        }
        function hasCategory(category, core) {
            return _.find(getCategoryList(core), function (curCategory) {
                if (curCategory === undefined) {
                    var here = true;
                }
                else if (curCategory.__text) {
                    // category has attributes; extract the string from __text
                    return (curCategory.__text === category);
                }
                else {
                    return (curCategory === category);
                }
            });
        }
        function translateToISA(coreID) {
            coreID = coreID.toUpperCase();
            if (coreID.indexOf('MSP430') !== -1) {
                return 'MSP430';
            }
            else if (coreID === 'CORTEX_M0' ||
                coreID === 'CORTEX_M0P' ||
                coreID === 'CORTEX_M3' ||
                coreID === 'CORTEX_M4' ||
                coreID === 'CORTEX_M4_CC' ||
                coreID === 'CORTEX_M4_432') {
                return 'TMS470REX';
            }
            else if (coreID === 'CORTEX_R4') {
                return 'TMS470RDX';
            }
            else if (coreID === 'CORTEX_R5') {
                return 'TMS470RDX';
            }
            else if (coreID === 'TMS320C28XX') {
                return 'TMS320C28XX';
            }
            else if (coreID === 'ARM7') {
                return 'TMS470R1X';
            }
            return coreID;
        }
        /* code for adding entries to the global search
        function updateSearchList(sStr) {
            var properties = getPropertyByCore(core);

            if (properties) {
                _.each(getPropertyByCore(core).PropertyList.property, function(prop: any) {
                    if ((prop.valueType === 'Button') &&
                        ((prop._id.toLowerCase().indexOf(sStr.toLowerCase()) !== -1) ||
                            (prop.name.toLowerCase().indexOf(sStr.toLowerCase()) !== -1))) {
                        ufSearch.addSearchEntry('Action', prop.name, 'propertiesActionSrv.addAction(\'' + prop.action + '\')');
                    }
                });
            }
        }
        ufSearch.registerObserverCallback(updateSearchList);
        */
        return {
            getPartnum: getPartnum,
            getPropertiesData: getPropertiesData,
            getPropertiesList: getPropertiesList,
            getPropertiesListByCategory: getPropertiesListByCategory,
            getCategoryList: getCategoryList,
            getCategoryName: getCategoryName,
            getCategoryToggle: getCategoryToggle,
            getProperty: getProperty,
            matchTarget: matchTarget,
            matchCategory: matchCategory,
            setFilter: setFilter,
            getFilter: getFilter,
            showID: showID,
            init: init,
            hasCategory: hasCategory
        };
    }
})();
