let interfacesLoaded = $.getScript('/js/atp.interfaces.js');

const ANALOGUE_CHANNEL_BASE = 0x1800;

const DEFAULT_DAMPING_FIELD = {

    fieldId: 0x00,
    sender: eSenderId.Processor,
    instance: eSenderInstance.Internal,
    displayName: "Unknown",
    value: '1.0'
}

const DEFAULT_SOURCE = {

    sender: eSenderId.Processor,
    instance: eSenderInstance.Internal,
    displayName: "A + T Processor"
};

class DampingModel extends ObservableModel {

    constructor() {
        super();

        this.sources = [];  // Data sources
        this.fields = [];   // Fields to damp.

        this.networkDamping = false;

        this.init();
    }

    init() {

        let self = this;

        readSingle("distribution", "dampnetworkdata").done(function(response)
        {
            self.networkDamping = Boolean(response);
        });

        self.loadBoardData().done(function() {
            self.loadDampingData().done(function() {

                self.sources.sort(function(a, b) {

                    // Sort sources by sender id and instance, processor should always come first.
                    if (a.sender === eSenderId.Processor)
                        return -1;
                    if (b.sender === eSenderId.Processor)
                        return 1;
                    if (a.sender - b.sender !== 0)
                        return a.sender - b.sender;

                    return a.instance - b.instance;
                });

                self.notifyAll();
            });
        });
    }

    persistData() {

        let dbArray = [];

        $.each(this.fields, function(_idx, field) {
            let key = field.fieldId + ';' + toHexField(makeSenderPair(field.sender, field.instance));
            dbArray.push({ 'section': 'damping', 'key' : key, 'value': field.value});
            
        });

        dbArray.push(
            {
                'section': 'distribution',
                'key': 'dampnetworkdata',
                'value': this.networkDamping
            }
        );

        return writeToDatabase(dbArray, 'damping');
    }

    /** Load in the damping fields. */
    loadDampingData() {

        let self = this;
        let deferred = new $.Deferred();

        readSection("Damping").done(function(dampingJson) {

            let damping = JSON.parse(dampingJson);

            $.each(damping, function(key, value) {

                // The database key can have two formats:
                // Old: fieldId
                // New: fieldId;senderPair

                let keyData = key.split(';');
                let fieldData = $.extend({}, DEFAULT_DAMPING_FIELD);

                fieldData.displayName = getFieldNameById(keyData[0]);
                fieldData.fieldId = keyData[0];
                fieldData.value = value;

                if (keyData[1]) {

                    // If the database key has the new format parse the senderPair.
                    fieldData.sender = getSenderId(keyData[1]);
                    fieldData.instance = getSenderInstance(keyData[1]);
                }

                if ( fieldData.sender == eSenderId.Speed_Board 
                    || fieldData.sender == eSenderId.Speed_Depth_Board
                    || fieldData.sender == eSenderId.Main_Iface_Board
                    || fieldData.sender == eSenderId.Analog_Board
                    || fieldData.sender == eSenderId.Wind_Board
                    || fieldData.sender == eSenderId.Depth_Board
                    || fieldData.sender == eSenderId.DLA2_Board) {

                    // Special case for analog fields as these should be added by
                    // the load boards method, we just need to set the damping value
                    let analogField = self.getField(fieldData.fieldId, fieldData.sender, fieldData.instance);

                    if (analogField)
                        analogField.value = fieldData.value;
                }
                else {
                    self.fields.push(fieldData);
                    self.addUniqueSource(fieldData.sender, fieldData.instance, getSenderName(fieldData.sender), getSenderName(fieldData.sender));
                }
            });

            deferred.resolve();
        });

        return deferred.promise();
    }

    loadBoardData() {
        let self = this;
        let deferred = new $.Deferred();

        $.when(readSection("Boards"), getServerMillis(), interfacesLoaded).done(function(boardJson, milliseconds) {

            let async = [];
            let boards = JSON.parse(boardJson[0]);
            let interfaceFactory = new InterfaceFactory();
            let millis = JSON.parse(milliseconds[0]);

            // Create an interface for each active board
            $.each(boards, function(board, timestamp) {

                if (validateTimestamp(timestamp) && (millis - timestamp) < maxInactiveTime_ms) {
                    async.push(interfaceFactory.getInterface(board).done(function(concreteInterface) {
                        
                        // Check the board type is correct.
                        if (concreteInterface.interfaceType == eInterfaceType["MainInterface"]
                        ||  concreteInterface.interfaceType == eInterfaceType["Analogue"]) {

                            let showSource = false;
                            let split = concreteInterface.internalName.split(':');
                            let sender = getSenderIdFromName(split[0]);
                            let instance = parseInt(split[1]);

                            let inputs = concreteInterface.analogueInputs;

                            for(let i = 0; i < inputs.length; i++) {

                                // Check the input type is one we want to damp.
                                if (inputs[i].data.enabled && (
                                       inputs[i].analogueType == eAnalogueType["Input Name"]
                                    || inputs[i].analogueType == eAnalogueType["User Defined"]
                                    || inputs[i].analogueType == eAnalogueType["AirTemp"]
                                    || inputs[i].analogueType == eAnalogueType["SeaTemp"]
                                    || inputs[i].analogueType == eAnalogueType["Keel Angle"]
                                    || inputs[i].analogueType == eAnalogueType["Canard Angle"]
                                    || inputs[i].analogueType == eAnalogueType["Daggerboard Position"]
                                    || inputs[i].analogueType == eAnalogueType["Boom Position"]
                                )) {
                                    
                                    showSource = true;
                                    
                                    let fieldData = $.extend( {}, DEFAULT_DAMPING_FIELD );
                                    fieldData.fieldId = toHexField(ANALOGUE_CHANNEL_BASE + inputs[i].channel);
                                    fieldData.sender = sender;
                                    fieldData.instance = instance;
                                    fieldData.displayName = inputs[i].displayName;
                                    fieldData.internalName = toTitleCase(inputs[i].internalName);
                                    fieldData.value = '0.0';    // Default value of 0.
                                    self.fields.push(fieldData);
                                } 
                            }

                            if (showSource)
                                self.addUniqueSource(sender, instance, concreteInterface.displayName, concreteInterface.internalName);
                        }
                    }));
                }
            });

            $.when.apply($, async).then(function() {
                deferred.resolve();
            });
        });

        return deferred.promise();
    }

    /** Add a source object to the array if it does not already exist. */
    addUniqueSource(senderId, senderInstance, sourceName, internalName) {

        if (!this.getSource(senderId, senderInstance)) {

            let newSource = $.extend({}, DEFAULT_SOURCE);
            newSource.sender = senderId;
            newSource.instance = senderInstance;
            newSource.displayName = sourceName;
            newSource.internalName = toTitleCase(internalName);
            this.sources.push(newSource);
        }
    }

    /** Get a source object with matching sender id and instance from the array */
    getSource(sender, instance) {

        for (let i = 0; i < this.sources.length; i++) {

            if (this.sources[i].sender === sender && this.sources[i].instance === instance)
                return this.sources[i];
        }

        return false;
    }

    /** Get a field object with matching field id, sender id and instance from the array */
    getField(fieldId, sender, instance) {

        for (let i = 0; i < this.fields.length; i++) {

            if (this.fields[i].fieldId === fieldId && this.fields[i].sender === sender && this.fields[i].instance === instance)
                return this.fields[i];
        }

        return false;
    }
 
}

class DampingController extends BasicController {

    constructor(model) {
        super(model);
    }

    /** Update damping time in the model. */
    onChangeDampingTime(e) {

        // Range is 0-60 seconds.
        validateRange($(e.target), 1, 0, 60, 0 );

        let container = $(e.target).closest('.damping-table');
        let fieldId = $(e.target).attr('data-field');
        let fieldData = this.model.getField(
            $(e.target).attr('data-field'),
            parseInt(container.attr('data-sender')),
            parseInt(container.attr('data-instance'))
        );

        if (fieldData)
            fieldData.value = $(e.target).val();

        if (getFieldBearingById(fieldId) === eBearingMode.true) {

            // If field has true bearing, find its sibling magnetic field and also set the value.
            let magField = getFieldSiblingById(fieldId);
            let magData = this.model.getField(magField.fieldId, magField.sender, magField.instance);

            if (magData)
                magData.value = $(e.target).val();
        }
    }

    toggleNetworkDamping(e)
    {
        this.model.networkDamping = $(e.target).is(':checked');
    }
}

class DampingView extends BasicView {

    constructor(controller) {
        super(controller);
    }

    bindEvents() {
        super.bindEvents();

        $('#damping-content, #fixed-damping-content').on('change', '.damping-table input', this.controller.onChangeDampingTime.bind(this.controller));
        $('#network-damping').on('change', this.controller.toggleNetworkDamping.bind(this.controller));
    }

    update(model) 
    {
        console.log(model);

        $('#damping-content').empty();

        $('#network-damping').prop('checked', model.networkDamping);

        /** Create a fieldset for each source */
        $.each(model.sources, function(_idx, src) 
        {
            if (src.sender == eSenderId.Processor) return;

            const legendhtml =
                    '<div class="tooltip-wrapper">'
                +       '<h2>' + (src.displayName || src.internalName) + '</h2>'
                +           '<div class="tooltip">'
                +               '<p>' + src.internalName + '</p>'
                +           '</div>'
                +       '</div>';

            $('#damping-content').append(
                '<div class="box top-wrapper damping-table" data-sender="' + src.sender + '" data-instance="' + src.instance + '">'
            +       '<fieldset>'
            +           '<legend>'
            +               legendhtml
            +           '</legend>'
            +           '<div class="flex-row">'
            +               '<div class="flex-fourth">'
            +                   '<h2>Field</h2>'
            +               '</div>'
            +               '<div class="flex-fourth">'
            +                   '<h2>Damping Time</h2>'
            +               '</div>'
            +               '<div class="flex-fourth">'
            +                   '<h2>Calculated Value</h2>'
            +               '</div>'
            +               '<div class="flex-fourth">'
            +                   '<h2>Damped Value</h2>'
            +               '</div>'
            +           '</div>'
            +       '</fieldset>'
            +   '</div>'
            );
        });

        /** Append a row for each field to the correct sender */
        $.each(model.fields, function(_idx, field) 
        {
            if (field.sender === eSenderId.Processor)
            {
                // Just update the input for the corresponding field
                $('.fixed-damping-grid .input-unit input[data-field="' + field.fieldId + '"]').val(field.value);
            }
            else
            {
                let isMag = getFieldBearingById(field.fieldId) === eBearingMode.mag;
                let containerId = '' + parseInt(field.fieldId) + '-' + parseInt(field.sender) + '-' + parseInt(field.instance);

                const fieldName = 
                        '<div class="tooltip-wrapper">'
                    +       '<div>' + (field.displayName || field.internalName) + '</div>'
                    +       '<div class="tooltip">'
                    +           '<p>' + field.internalName + '</p>'
                    +       '</div>'
                    +   '</div>';

                $('.damping-table[data-sender="' + field.sender + '"][data-instance="' + field.instance + '"]').children('fieldset').append(
                    '<div class="flex-row">'
                +       '<div class="flex-fourth">'
                +           fieldName
                +       '</div>'
                +       '<div class="flex-fourth">'
                +           '<div class="input-unit">'
                +               '<input class="damping-time" type="number" value="' + field.value + '" data-field="' + field.fieldId + '" ' + (isMag ? 'hidden' : '') + '>'
                +               '<span ' + (isMag ? 'hidden' : '') + '>s</span>'
                +           '</div>'
                +       '</div>'
                +       '<div class="flex-fourth">'
                +           '<div id="' + containerId + '" class="field-container" data-field="' + field.fieldId + '" data-sender="' + getSenderName(field.sender) + '" data-instance="' + field.instance + '"></div>'
                +       '</div>'
                +       '<div class="flex-fourth">'
                +           '<div id="' + containerId + 'damped" class="field-container" data-field="' + field.fieldId + '" data-sender="Damping" data-instance="' + field.instance + '"></div>'
                +       '</div>'
                +   '</div>'
                );
            }
        });
        
        $(document).trigger('fields-changed');
    }
}