/**
 * Data-field daemon
 * 
 * @category    Daemons
 * @package     ATProcessor
 * @author      Sam Meredith <sam@aandtinstruments.com>
 * @version     0.1
 */

// Regex pattern to match ATP return values
const fieldFormatRegex = /(0x.*?)=\s?(.*)\r/g;

/**
 * Class to handle communication of field requests between ATP and Webserver
 */
class FieldDaemon {

    constructor() {
        // Callback function takes the container and field value as an input
        // Carries out any pre-processing needed
        this.callback = function() { return undefined; };
        this.fields = {};
        this.isPaused = false;

        $(document).on('fields-changed', function() {
            // Check for dynamically added containers
            if ($('.field-container').length > 0) {
                this.handleFields.call(this);
                // Add hold button if it does not already exist
                if (!$('body .content-wrapper').attr('data-no-hold-button') && $('#hold-button').length == 0) {
                    $('body').append(
                        '<a class="button button-gray button-medium hold-button" id="hold-button">Hold</a>'
                    );
                }
            }
        }.bind(this));
    }

    // Query the processor for field values
    // Expects fields to be an array of fieldIds
    getFieldValues(fieldArray, id, instance) {
        let self = this;
        
        if (self.isPaused == false) {
            $.ajax({
                type: 'POST',
                url: '/atp.fields.php',
                data: {'action': 'getFields', 'fields': fieldArray, 'id': id, 'instance': instance},
                success: function(jsonResponse) {
                    let response = JSON.parse(jsonResponse);

                    if (response.status) {
                        self.parseFieldValues.call(self, response.response, id, instance);
                    } else {
                        if (0 === response.error_code)
                            console.log('Field query failed. No response from processor.');
                        else
                            console.log(response.error);
                    }
                }
            });
        }
    }

    // Scrape the fields from the page and begin polling the processor
    handleFields() {
        this.fields = {};
        this.scrapeFieldContainers();
        
        for (let property in this.fields) {
            if (this.fields.hasOwnProperty(property)) {
                let senderId = property;
                for (let i = 0; i < this.fields[senderId].length; i++) {
                    let senderInstance = this.fields[senderId][i].instance;
                    let fieldArray = [];
                    for (let j = 0; j < this.fields[senderId][i].fieldContainers.length; j++) {
                        fieldArray.push(this.fields[senderId][i].fieldContainers[j].fieldId);
                    }
                    
                    // Poll processor every second
                    let interval = setInterval(this.getFieldValues.bind(this), 1000, fieldArray,
                        senderId, senderInstance);
                    // Clear the interval
                    $(document).on('fields-changed', function() { clearInterval(interval); });
                }
            }
        }
    }

    // Parse field values returned from the processor
    parseFieldValues(data, id, instance) {
        let match = null;
        let parseFields = [];
        let self = this;

        // match[1] will contain the fieldId and match[2] the value
        while ((match = fieldFormatRegex.exec(data)) !== null ) {
            parseFields.push( { "fieldId" : match[1], "value" : match[2] } );
        }

        try {
            let fieldArray = self.fields[id].find(x => x.instance == instance).fieldContainers;
            for (let j = 0; j < parseFields.length; j++) {
                // Hex characters need to be lower case
                let containerArray = fieldArray.find(x => x.fieldId.toLowerCase() == parseFields[j].fieldId).containers;
                for (let i = 0; i < containerArray.length; i++) {
                    let $container = $('#' + containerArray[i]);
                    let valueReference = { 'valueString' : parseFields[j].value };
                    // Javascript passes strings by value but objects by reference
                    self.callback($container, valueReference);
                    $container.html(valueReference.valueString);
                }
            }
        } catch (e) {
            console.log(e.message);
        }
    }

    // Reads in the set of fields, containers and [senderId, senderInstance] pairs from the DOM
    scrapeFieldContainers() {
        let self = this;

        $(document).find('.field-container').each(function() {
            let senderId = $(this).attr('data-sender');
            // self.fields contains the senderIds found on the page
            if (self.fields[senderId] === undefined)
                self.fields[senderId] = [];

            let intInstance = parseInt($(this).attr('data-instance'));
            let senderInstance = isNaN(intInstance) ? "" : intInstance;
            // self.fields.id is an array of senderInstance, field pairs
            if (self.fields[senderId].find(x => x.instance == senderInstance) === undefined)
                self.fields[senderId].push( { "instance" : senderInstance, "fieldContainers" : [] } );
                
            let containerId = $(this).attr("id");
            let field = $(this).attr('data-field');
            // self.fields.id[].fields is an array of fieldId, container pairs
            let fieldArray = self.fields[senderId].find(x => x.instance == senderInstance).fieldContainers;
            if (fieldArray.find(x => x.fieldId == field) === undefined)
                // A given fieldId my be used by multiple containers
                fieldArray.push( { "fieldId" : field, "containers" : [containerId] } );
            else 
                fieldArray.find(x => x.fieldId == field).containers.push(containerId);
        });
    }
}
