/**
 * Class definitions for Garmin User Data configuration.
 * 
 * @category Webserver
 * @package ATProcessor
 * @author Sam Meredith <sam@aandtinstruments.com>
 * @version 0.1
 * 
 */

const DEFAULT_EXPORT_DATA = "0x0000,Not Configured";
const DEFAULT_USER_DATA = ",,false,,0x0f00";
const MAX_USER_DATA = 18;

/**
 * Defines User Data I/O
 */
class GarminUserModel extends ObservableModel {
    constructor() {
        super();

        this.exportData;
        this.exportString = "";
        this.expeditionString = '<option value="0x0000">Not Configured</option>';
        this.userData;
    }

    init() {
        let self = this;
        let defaultUserData = {

        };

        $.when(readSection("GarminUserData"), readSingle("ATPExport", "Channels"), readSection("Expedition")).done(
            function(userData, exportData, expeditionData)
        {
            // Load in the Expedition channels
            let expeditionSection = JSON.parse(expeditionData[0]);
            expeditionSection = expeditionSection.channels.split(',');

            let expeditionChannels = [];
            for (let i = 0; i < expeditionSection.length; i += 2) 
            {
                expeditionChannels.push({ 'channel': expeditionSection[i], 'name': expeditionSection[i + 1] });
            }

            expeditionChannels.sort(function(a, b) 
            {
                return a.name.localeCompare(b.name, 'en', { numeric: true });
            });

            for (let i = 0; i < expeditionChannels.length; i ++) 
            {
                self.expeditionString +=
                    '<option value="' + (parseInt(expeditionChannels[i].channel) + 0x6800) + '">' + expeditionChannels[i].name + '</option>';
            }

            // Load in the normal ATP channels
            self.exportData = exportData[0] || DEFAULT_EXPORT_DATA;
            self.exportData = self.exportData.split(',');
            for (let i = 0; i < self.exportData.length; i += 2)
            {
                self.exportString += 
                    '<option value="' + self.exportData[i] + '">' + self.exportData[i + 1] + '</option>';
            }

            self.userData = $.extend({}, defaultUserData, JSON.parse(userData[0]));

            self.notifyAll();
        });
    }

    persistData() {
        var databaseArray = new Array();

        $.each(this.userData, function(key, value) {
            databaseArray.push({ 'section' : "GarminUserData", 'key' : key, 'value' : value });
        });
        
        return writeToDatabase(databaseArray, 'Garmin');
    }

    setChannel(index, channel) {
        let data = this.userData["userdata" + index] || DEFAULT_USER_DATA;

        data = data.split(',');
        data[0] = channel;

        this.userData["userdata" + index] = data.join(',');
    }

    setChannelEnable(index, enable) {
        let data = this.userData["userdata" + index] || DEFAULT_USER_DATA;

        data = data.split(',');
        data[2] = enable;

        this.userData["userdata" + index] = data.join(',');
    }

    setChannelFormat(index, format) {
        let data = this.userData["userdata" + index] || DEFAULT_USER_DATA;

        data = data.split(',');
        data[1] = format;

        this.userData["userdata" + index] = data.join(',');
    }

    setChannelName(index, name) {
        let data = this.userData["userdata" + index] || DEFAULT_USER_DATA;

        data = data.split(',');
        data[3] = name;

        this.userData["userdata" + index] = data.join(',');
    }

    setInstance(index, instance)
    {
        let data = this.userData["userdata" + index] || DEFAULT_USER_DATA;
        let split = data.split(',');
        let pair = parseInt(split[4] || "0x0f00");
        let sender = pair >> 8;
        
        split[4] = (sender << 8) | instance;

        this.userData["userdata" + index] = split.join(',');
    }

    setSender(index, sender)
    {
        let data = this.userData["userdata" + index] || DEFAULT_USER_DATA;
        let split = data.split(',');
        let pair = parseInt(split[4] || "0x0f00");
        let instance = pair & 0xff;
        
        split[4] = (sender << 8) | instance;

        this.userData["userdata" + index] = split.join(',');
    }
}

/**
 * View -> Model interaction for Garmin User Data
 */
class GarminUserController extends BasicController {
    constructor(model) {
        super(model);
    }

    changeChannel(e) {
        let index = $(e.target).attr('data-index');

        this.model.setChannel(index, $(e.target).val());
    }

    changeChannelFormat(e) {
        let index = $(e.target).attr('data-index');

        this.model.setChannelFormat(index, $(e.target).val());
    }

    changeChannelName(e) {
        let index = $(e.target).attr('data-index');

        this.model.setChannelName(index, $(e.target).val());
    }

    changeInstance(e)
    {
        let index = $(e.target).attr('data-index');
        this.model.setInstance(index, $(e.target).val());
    }

    changeSource(e)
    {
        let index = $(e.target).attr('data-index');
        let sender = $(e.target).find('option:selected').val();

        this.model.setSender(index, sender);
        // Reset the instance when swapping back to processor
        if (sender == "15") this.model.setInstance(index, 0);

        this.model.notifyAll();
    }

    toggleOutput(e) {
        let index = $(e.target).attr('data-index');

        this.model.setChannelEnable(index, $(e.target).is(':checked'));
    }
}

/**
 * Handle display for Garmin User Data
 */
class GarminUserView extends BasicView {
    constructor(controller) {
        super(controller);

        this.controller.model.init();
    }

    bindEvents() {
        super.bindEvents();

        $('#user-data-container').on('change', 'input[type="checkbox"]', this.controller.toggleOutput.bind(this.controller));
        $('#user-data-container').on('change', 'select[name="channel"]', (e) =>
            {
                if (!$('#user-data-container').hasClass('advanced'))
                {
                    // Reset the sender and instance
                    const index = $(e.target).attr('data-index');

                    $('#user-data-container select[name="sender"][data-index="' + index + '"] option[value="15"]').prop('selected', true);
                    $('#user-data-container select[name="sender"][data-index="' + index + '"]').change();

                    $('#user-data-container input[name="instance"][data-index="' + index + '"]').val(0);
                    $('#user-data-container input[name="instance"][data-index="' + index + '"]').change();
                }

                this.controller.changeChannel.call(this.controller, e);
            });
        $('#user-data-container').on('change', 'select[name="channel-format"]', this.controller.changeChannelFormat.bind(this.controller));
        $('#user-data-container').on('change', 'input[name="channel-name"]', this.controller.changeChannelName.bind(this.controller));
        $('#user-data-container').on('change', 'input[name="instance"]', this.controller.changeInstance.bind(this.controller));
        $('#user-data-container').on('change', 'select[name="sender"]', this.controller.changeSource.bind(this.controller));

        $('.slider-container input').on('change', () => $('#user-data-container').toggleClass('advanced'));
    }

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

        let container = $('#user-data-container');
        container.empty().append(
                '<div></div>'
            +   '<h2>Field</h2>'
            +   '<h2>Sender</h2>'
            +   '<h2>Instance</h2>'
            +   '<h2>Name</h2>'
            +   '<h2>Format</h2>'
            +   '<h2>Enable</h2>'
        );

        const senderOptions = '<option value="15" selected>Processor</option>'
            +   '<option value="1">Speed Board</option>'
            +   '<option value="2">Speed/Depth Board</option>'
            +   '<option value="5">Wind Board</option>'
            +   '<option value="10">Analogue Board</option>'
            +   '<option value="11">Fastnet</option>'
            +   '<option value="12">NMEA0183</option>'
            +   '<option value="13">CanBus</option>'
            +   '<option value="14">Main Interface Board</option>'
            +   '<option value="17">DLA2 Board</option>'
            +   '<option value="18">Expedition</option>'
            +   '<option value="34">Modbus</option>'
            +   '<option value="35">Cyclops</option>'
            +   '<option value="37">NMEA Interface</option>'
            +   '<option value="38">DLA3 Board</option>'
            +   '<option value="48">NodeRED</option>';


        let toggleAdvanced = false;
        for (let i = 0; i <= MAX_USER_DATA; i++)
        {
            let data = model.userData["userdata" + i] || DEFAULT_USER_DATA;
            data = data.split(',');

            let pair = parseInt(data[4] || "0x0f00");
            if (pair != 0x0f00) toggleAdvanced = true;
            let sender = pair >> 8;
            let instance = pair & 0xff;

            let row = $(
                    '<h2>User ' + i + '</h2>'
                +   '<select name="channel" data-index=' + i + '>' + (sender == 0x12 ? model.expeditionString : model.exportString) + '</select>'
                +   '<select name="sender" data-index=' + i + '>' + senderOptions + '</select>'
                +   '<input name="instance" data-index=' + i + '>'
                +   '<input name="channel-name" data-index=' + i + ' value="' + (data[3] || "") + '">'
                +   '<select name="channel-format" data-index=' + i + '>'
                +       '<option value="0">Invalid [----]</option>'
                +       '<option value="2">Integer 0dp</option>'
                +       '<option value="3">Decimal</option>'
                +       '<option value="6">Angle &plusmn;180&deg;</option>'
                +       '<option value="5">Bearing 360&deg;</option>'
                +       '<option value="21">Time HH:MM:SS</option>'
                +   '</select>'
                +   '<input data-index=' + i + ' type="checkbox" ' + (data[2] == "true" ? "checked" : "") + '>'
            );
            container.append(row);

            // If the selected field does not appear in the list, add a dummy entry
            const option = $(row[1]).find('option[value="' + data[0] + '"]');
            if (option.length == 0 && data[0] && parseInt(data[0]) != 0)
            {
                $(row[1]).prepend('<option value="' + data[0] + '" selected disabled>Field 0x' + parseInt(data[0]).toString(16) + '</option>');
            }
            else option.prop('selected', true);

            $(row[2]).find('option[value="' + sender + '"]').prop('selected', true);
            $(row[3]).val(instance);
            $(row[5]).find('option[value="' + data[1] + '"]').prop('selected', true);
        }

        // If ANY sender/instance are set to non-default values, force the advanced toggle
        if (toggleAdvanced) $('.slider-container input').prop('checked', true);
        if ($('.slider-container input').is(':checked'))
        {
            $('#user-data-container').addClass('advanced');
        };
    }
}
