/*
 * Copyright 2016 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of fiware-iotagent-lib
 *
 * fiware-iotagent-lib is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * fiware-iotagent-lib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with fiware-iotagent-lib.
 * If not, seehttp://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::daniel.moranjimenez@telefonica.com
 *
 * Developed by: Federico M. Facca - Martel Innovate
 */

/* eslint-disable consistent-return */
/* eslint-disable no-self-assign */
/* eslint-disable no-unused-vars */

const jexl = require('jexl');
const errors = require('../errors');
const logger = require('logops');
const config = require('../commonConfig');
const logContext = {
    op: 'IoTAgentNGSI.JEXL'
};

jexl.addTransform('indexOf', (val, char) => String(val).indexOf(char));
jexl.addTransform('length', (val) => String(val).length);
jexl.addTransform('trim', (val) => String(val).trim());
jexl.addTransform('substr', (val, int1, int2) => String(val).substr(int1, int2));

function parse(expression, context, callback) {
    let result;
    let error;

    try {
        result = jexl.evalSync(expression, context);
        logger.debug(logContext, 'parse expression "[%j]" over "[%j]" result "[%j]" ', expression, context, result);
    } catch (e) {
        error = new errors.InvalidExpression(expression);
        if (callback) {
            callback(error);
        } else {
            throw error;
        }
    }

    if (callback) {
        callback(null, result);
    } else {
        return result;
    }
}

function extractContext(attributeList) {
    const context = {};
    let value;

    for (let i = 0; i < attributeList.length; i++) {
        if (
            !Number.isNaN(Number.parseFloat(attributeList[i].value)) &&
            !Number.isInteger(Number.parseFloat(attributeList[i].value))
        ) {
            value = Number.parseFloat(attributeList[i].value);
        } else if (!Number.isNaN(Number.parseInt(attributeList[i].value))) {
            value = Number.parseInt(attributeList[i].value);
        } else if (String(attributeList[i].value) === 'true') {
            value = true;
        } else if (String(attributeList[i].value) === 'false') {
            value = false;
        } else {
            value = attributeList[i].value;
        }
        if (attributeList[i].name) {
            context[attributeList[i].name] = value;
        }
        /*jshint camelcase: false */
        if (attributeList[i].object_id) {
            context[attributeList[i].object_id] = value;
        }
        /*jshint camelcase: true */
    }

    return context;
}

function applyExpression(expression, context, typeInformation) {
    const result = jexl.evalSync(expression, context);
    logger.debug(logContext, 'applyExpression "[%j]" over "[%j]" result "[%j]" ', expression, context, result);
    let expressionResult = result !== undefined ? result : expression;
    return expressionResult;
}

function expressionApplier(context, typeInformation) {
    return function (attribute) {
        /**
         * Determines if a value is of type float
         *
         * @param      {String}   value       Value to be analyzed
         * @return     {boolean}              True if float, False otherwise.
         */
        function isFloat(value) {
            return !isNaN(value) && value.toString().indexOf('.') !== -1;
        }

        const newAttribute = {
            name: attribute.name,
            type: attribute.type
        };

        /*jshint camelcase: false */
        if (config.isCurrentNgsi() && attribute.object_id) {
            newAttribute.object_id = attribute.object_id;
        }

        newAttribute.value = applyExpression(attribute.expression, context, typeInformation);
        return newAttribute;
    };
}

function isTransform(identifier) {
    return jexl.getTransform(identifier) !== (null || undefined);
}

function contextAvailable(expression, context) {
    let error;
    try {
        const lexer = jexl._getLexer();
        const identifiers = lexer.tokenize(expression).filter(function (token) {
            return token.type === 'identifier';
        });
        const keys = Object.keys(context);
        let validContext = true;
        identifiers.some(function (element) {
            if (!keys.includes(element.value) && !isTransform(element.value)) {
                validContext = false;
                logger.warn(
                    logContext,
                    'For expression "[%s]" context "[%j]" does not have element %s',
                    expression,
                    context,
                    element.value
                );
            }
            // FIXME: Workaround to allow keys of JSON geopoints
            if (element.value === 'type' || element.value === 'coordinates') {
                validContext = true;
            }
            return validContext === false;
        });
        if (validContext) {
            jexl.evalSync(expression, context);
        }
        return validContext;
    } catch (e) {
        error = new errors.InvalidExpression(expression);
        throw error;
    }
}

function processExpressionAttributes(typeInformation, list, context) {
    return list
        .filter(function (item) {
            return item.expression && contextAvailable(item.expression, context);
        })
        .map(expressionApplier(context, typeInformation));
}

exports.extractContext = extractContext;
exports.processExpressionAttributes = processExpressionAttributes;
exports.contextAvailable = contextAvailable;
exports.applyExpression = applyExpression;
exports.parse = parse;

@@ -4556,977 +4556,33 @@
     
-const lexer = jexl._getLexer();%0A        const identifiers = lexer.tokenize(expression).filter(function (token) %7B%0A            return token.type === 'identifier';%0A        %7D);%0A        const keys = Object.keys(context);%0A        let validContext = true;%0A        identifiers.some(function (element) %7B%0A            if (!keys.includes(element.value) && !isTransform(element.value)) %7B%0A                validContext = false;%0A                logger.warn(%0A                    logContext,%0A                    'For expression %22%5B%25s%5D%22 context %22%5B%25j%5D%22 does not have element %25s',%0A                    expression,%0A                    context,%0A                    element.value%0A                );%0A            %7D%0A            // FIXME: Workaround to allow keys of JSON geopoints%0A            if (element.value === 'type' %7C%7C element.value === 'coordinates') %7B%0A                validContext = true;%0A            %7D%0A            return validContext === false;%0A        %7D);%0A        if (validContext) %7B%0A    
+let validContext = true;%0A
     
@@ -4621,26 +4621,16 @@
 ntext);%0A
-        %7D%0A
         

simplify contextAvailable for jexl parser