/*
 * Copyright © 2016-2019 The Thingsboard Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
export default angular.module('thingsboard.api.attribute', [])
    .factory('attributeService', AttributeService)
    .name;

/*@ngInject*/
function AttributeService($http, $q, $filter, types, telemetryWebsocketService) {

    var entityAttributesSubscriptionMap = {};

    var service = {
        getEntityKeys: getEntityKeys,
        getEntityTimeseriesValues: getEntityTimeseriesValues,
        getEntityAttributesValues: getEntityAttributesValues,
        getEntityAttributes: getEntityAttributes,
        subscribeForEntityAttributes: subscribeForEntityAttributes,
        unsubscribeForEntityAttributes: unsubscribeForEntityAttributes,
        saveEntityAttributes: saveEntityAttributes,
        deleteEntityAttributes: deleteEntityAttributes
    }

    return service;

    function getEntityKeys(entityType, entityId, query, type, config) {
        var deferred = $q.defer();
        var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/';
        if (type === types.dataKeyType.timeseries) {
            url += 'timeseries';
        } else if (type === types.dataKeyType.attribute) {
            url += 'attributes';
        }
        $http.get(url, config).then(function success(response) {
            var result = [];
            if (response.data) {
                if (query) {
                    var dataKeys = response.data;
                    var lowercaseQuery = angular.lowercase(query);
                    for (var i=0; i<dataKeys.length;i++) {
                        if (angular.lowercase(dataKeys[i]).indexOf(lowercaseQuery) === 0) {
                            result.push(dataKeys[i]);
                        }
                    }
                } else {
                    result = response.data;
                }
            }
            deferred.resolve(result);
        }, function fail(response) {
            deferred.reject(response.data);
        });
        return deferred.promise;
    }

    function getEntityTimeseriesValues(entityType, entityId, keys, startTs, endTs, limit) {
        var deferred = $q.defer();
        var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/values/timeseries';
        url += '?keys=' + keys;
        url += '&startTs=' + startTs;
        url += '&endTs=' + endTs;
        if (angular.isDefined(limit)) {
            url += '&limit=' + limit;
        }
        $http.get(url, null).then(function success(response) {
            deferred.resolve(response.data);
        }, function fail(response) {
            deferred.reject(response.data);
        });
        return deferred.promise;
    }

    function getEntityAttributesValues(entityType, entityId, attributeScope, keys, config) {
        var deferred = $q.defer();
        var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/values/attributes/' + attributeScope;
        if (keys && keys.length) {
            url += '?keys=' + keys;
        }
        $http.get(url, config).then(function success(response) {
            deferred.resolve(response.data);
        }, function fail() {
            deferred.reject();
        });
        return deferred.promise;
    }

    function processAttributes(attributes, query, deferred, successCallback, update, apply) {
        attributes = $filter('orderBy')(attributes, query.order);
        if (query.search != null) {
            attributes = $filter('filter')(attributes, {key: query.search});
        }
        var responseData = {
            count: attributes.length
        }
        var startIndex = query.limit * (query.page - 1);
        responseData.data = attributes.slice(startIndex, startIndex + query.limit);
        successCallback(responseData, update, apply);
        if (deferred) {
            deferred.resolve();
        }
    }

    function getEntityAttributes(entityType, entityId, attributeScope, query, successCallback, config) {
        var deferred = $q.defer();
        var subscriptionId = entityType + entityId + attributeScope;
        var eas = entityAttributesSubscriptionMap[subscriptionId];
        if (eas) {
            if (eas.attributes) {
                processAttributes(eas.attributes, query, deferred, successCallback);
                eas.subscriptionCallback = function(attributes) {
                    processAttributes(attributes, query, null, successCallback, true, true);
                }
            } else {
                eas.subscriptionCallback = function(attributes) {
                    processAttributes(attributes, query, deferred, successCallback, false, true);
                    eas.subscriptionCallback = function(attributes) {
                        processAttributes(attributes, query, null, successCallback, true, true);
                    }
                }
            }
        } else {
            var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/values/attributes/' + attributeScope;
            $http.get(url, config).then(function success(response) {
                processAttributes(response.data, query, deferred, successCallback);
            }, function fail() {
                deferred.reject();
            });
        }
        return deferred;
    }

    function onSubscriptionData(data, subscriptionId) {
        var entityAttributesSubscription = entityAttributesSubscriptionMap[subscriptionId];
        if (entityAttributesSubscription) {
            if (!entityAttributesSubscription.attributes) {
                entityAttributesSubscription.attributes = [];
                entityAttributesSubscription.keys = {};
            }
            var attributes = entityAttributesSubscription.attributes;
            var keys = entityAttributesSubscription.keys;
            for (var key in data) {
                var index = keys[key];
                var attribute;
                if (index > -1) {
                    attribute = attributes[index];
                } else {
                    attribute = {
                        key: key
                    };
                    index = attributes.push(attribute)-1;
                    keys[key] = index;
                }
                var attrData = data[key][0];
                attribute.lastUpdateTs = attrData[0];
                attribute.value = attrData[1];
            }
            if (entityAttributesSubscription.subscriptionCallback) {
                entityAttributesSubscription.subscriptionCallback(attributes);
            }
        }
    }

    function subscribeForEntityAttributes(entityType, entityId, attributeScope) {
        var subscriptionId = entityType + entityId + attributeScope;
        var entityAttributesSubscription = entityAttributesSubscriptionMap[subscriptionId];
        if (!entityAttributesSubscription) {
            var subscriptionCommand = {
                entityType: entityType,
                entityId: entityId,
                scope: attributeScope
            };

            var type = attributeScope === types.latestTelemetry.value ?
                types.dataKeyType.timeseries : types.dataKeyType.attribute;

            var subscriber = {
                subscriptionCommands: [subscriptionCommand],
                type: type,
                onData: function (data) {
                    if (data.data) {
                        onSubscriptionData(data.data, subscriptionId);
                    }
                }
            };
            entityAttributesSubscription = {
                subscriber: subscriber,
                attributes: null
            };
            entityAttributesSubscriptionMap[subscriptionId] = entityAttributesSubscription;
            telemetryWebsocketService.subscribe(subscriber);
        }
        return subscriptionId;
    }

    function unsubscribeForEntityAttributes(subscriptionId) {
        var entityAttributesSubscription = entityAttributesSubscriptionMap[subscriptionId];
        if (entityAttributesSubscription) {
            telemetryWebsocketService.unsubscribe(entityAttributesSubscription.subscriber);
            delete entityAttributesSubscriptionMap[subscriptionId];
        }
    }

    function saveEntityAttributes(entityType, entityId, attributeScope, attributes) {
        var deferred = $q.defer();
        var attributesData = {};
        var deleteAttributes = [];
        for (var a=0; a<attributes.length;a++) {
            if (angular.isDefined(attributes[a].value) && attributes[a].value !== null) {
                attributesData[attributes[a].key] = attributes[a].value;
            } else {
                deleteAttributes.push(attributes[a]);
            }
        }
        var deleteEntityAttributesPromise;
        if (deleteAttributes.length) {
            deleteEntityAttributesPromise = deleteEntityAttributes(entityType, entityId, attributeScope, deleteAttributes);
        }
        if (Object.keys(attributesData).length) {
            var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/' + attributeScope;
            $http.post(url, attributesData).then(function success(response) {
                if (deleteEntityAttributesPromise) {
                    deleteEntityAttributesPromise.then(
                        function success() {
                            deferred.resolve(response.data);
                        },
                        function fail() {
                            deferred.reject();
                        }
                    )
                } else {
                    deferred.resolve(response.data);
                }
            }, function fail() {
                deferred.reject();
            });
        } else if (deleteEntityAttributesPromise) {
            deleteEntityAttributesPromise.then(
                function success() {
                    deferred.resolve();
                },
                function fail() {
                    deferred.reject();
                }
            )
        } else {
            deferred.resolve();
        }
        return deferred.promise;
    }

    function deleteEntityAttributes(entityType, entityId, attributeScope, attributes) {
        var deferred = $q.defer();
        var keys = '';
        for (var i = 0; i < attributes.length; i++) {
            if (i > 0) {
                keys += ',';
            }
            keys += attributes[i].key;
        }
        var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/' + attributeScope + '?keys=' + keys;
        $http.delete(url).then(function success() {
            deferred.resolve();
        }, function fail() {
            deferred.reject();
        });
        return deferred.promise;
    }


}