const gpsSourcePrefix = "GpsSource";

class PositionModel extends ObservableModel {
    constructor() {
        super();

        this.availableSources = [];

        this.maxInputNumber = 1;
        this.currentSelectedInput = "GpsSource0";
        this.data = {};

        this.listOfCanDevices = [];

        this.latArray = [];
        this.longArray = [];
        this.storedLatObj = { value: "0" };
        this.storedLongObj = { value: "0" };
    }

    get inputName() {
        return this.data[this.currentSelectedInput].name;
    }

    set inputName(inputName) {
        this.data[this.currentSelectedInput].name = inputName;
    }

    get instance() {
        return this.data[this.currentSelectedInput].instance;
    }

    get source() {
        return this.data[this.currentSelectedInput].source;
    }

    set source(source) {
        this.data[this.currentSelectedInput].source = source;
        this.notifyAll();
    }

    set instance(instance) {
        this.data[this.currentSelectedInput].instance = instance;
    }

    get uniqueid() {
        return this.data[this.currentSelectedInput].uniqueid;
    }

    set uniqueid(uniqueid) {
        this.data[this.currentSelectedInput].uniqueid = uniqueid;
    }

    init() {
        let self = this;

        $.when(readSingle("GPS", "SourceCount"),
               readSingle("GPS", "Default"), 
               readSingle("Last Known Position", "Latitude"),
               readSingle("Last Known Position", "Longitude"),
               getSources("Position")).done(function(sourceCount, defaultPosition, latArrayObj, longArrayObj, getSourcesArray) {

            let async = [];

            // readSingle("GPS", "SourceCount") -------------------------------------------------------------------------
            self.maxInputNumber = Math.max(sourceCount[0], 1);
            for (let i = 0; i < self.maxInputNumber; i++) {
                async.push(readSection(gpsSourcePrefix + i).done(function(GpsSourceSection) {
                    let obj = JSON.parse(GpsSourceSection);
                    
                    if ($.isEmptyObject(obj))
                        obj = {};

                    if (!obj.hasOwnProperty("name"))
                        obj.name = "";
                    
                    if (!obj.hasOwnProperty("instance"))
                        obj.instance = "";

                    if (!obj.hasOwnProperty("source"))
                        obj.source = "No source";

                    if (!obj.hasOwnProperty("uniqueid"))
                        obj.uniqueid = "0";

                    self.data[gpsSourcePrefix +  i] = obj;
                }));
            }

            // readSingle("GPS", "Default") -----------------------------------------------------------------------------
            if (defaultPosition[0].toLowerCase().startsWith(gpsSourcePrefix.toLowerCase())) {
                let currentSelectedInputNumber = parseInt(defaultPosition[0].slice(gpsSourcePrefix.length), 10);
                self.currentSelectedInput = gpsSourcePrefix + currentSelectedInputNumber;
            }

            // ----------------------------------------------------------------------------------------------------------
            // readSingle("Last Known Position", "Latitude") ------------------------------------------------------------         
            self.latArray = decimalDegreesToDecimalMinutesArray(latArrayObj[0], "Latitude");
            self.storedLatObj.value = !isNaN(parseFloat(latArrayObj[0])) ? latArrayObj[0] : "0";

            // readSingle("Last Known Position", "Longitude") -----------------------------------------------------------
            self.longArray = decimalDegreesToDecimalMinutesArray(longArrayObj[0], "Longitude");
            self.storedLongObj.value = !isNaN(parseFloat(longArrayObj[0])) ? longArrayObj[0] : "0";

            // getSources("Position") -----------------------------------------------------------------------------------
            self.availableSources = getSourcesArray[0];
            self.availableSources.unshift(
                { displayName: "No Source", internalName: "no source" }
            );

            self.listOfCanDevices = getSourcesArray[1];

            // ----------------------------------------------------------------------------------------------------------
            $.when.apply($, async).done(function() {
                
                Object.keys(self.data).forEach(source => {
                    // If uniqueid for source isn't stored in listOfCanDevices, it is probably not connected. Add it to the list.
                    if (self.data[source].uniqueid !== "" && self.data[source].uniqueid !== "0") {
                        if (self.listOfCanDevices.find(device => device.uniqueid === self.data[source].uniqueid) === undefined) {
                            self.listOfCanDevices.push({ 
                                "uniqueid" : self.data[source].uniqueid,
                                "model" : "",
                                "address" : "0",
                                "serialNumber" : "",
                                "displayName": "Unconnected Device [ID:" + self.data[source].uniqueid + "]",
                                "isConnected" : false
                            });
                        }
                    }                    
                }); 

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

    persistData() {

        var databaseArray = new Array();
        let self = this;

        // Store position data
        for (let i = 0; i < self.maxInputNumber; i++) {
            $.each(this.data[gpsSourcePrefix + i], function(key, value) {
                databaseArray.push(
                    {'section': gpsSourcePrefix + i, 'key': key, 'value': value}
                );
            });
        }

        databaseArray.push(
            {'section': "Last Known Position", 'key': "Latitude", 'value': this.storedLatObj.value}
        );

        databaseArray.push(
            {'section': "Last Known Position", 'key': "Longitude", 'value': this.storedLongObj.value}
        );
        
        // Store selected position identifier
        databaseArray.push(
            {'section': 'GPS', 'key': 'default', 'value': self.currentSelectedInput}
        );

        return writeToDatabase(databaseArray, "GPS");
    }
}

class PositionController extends BasicController {
    constructor(model) {
        super(model);
    }

    changeInstance(e) {
        this.model.instance = $(e.target).val();
        this.model.notifyAll();
    }

    canbusRedirect(e) {
        window.location.href = "canFilter.php";
    }

    changeSource(e) {
        this.model.source = $(e.target).val();
        this.model.notifyAll();
    }

    nmeaRedirect(e) {
        window.location.href = "nmeaFilter.php";
    }

    changeInput(e) {
        this.model.currentSelectedInput = $(e.target).val();
        this.model.notifyAll();
    }

    changeInputName(e) {
        this.model.inputName = $(e.target).val();
    }

    changeCanbusDevice(e) {
        this.model.uniqueid = $(e.target).val();
        this.model.notifyAll();
    }

    changeLatitude(e) {
        validateLatitude($('#lat-degrees'), $('#lat-minutes'), $('#lat-direction'), this.model.storedLatObj);
    }

    changeLongitude(e) {
        validateLongitude($('#long-degrees'), $('#long-minutes'), $('#long-direction'), this.model.storedLongObj);
    }
}

class PositionView extends BasicView {
    constructor(controller) {
        super(controller);

        this.controller.model.init();
    }

    bindEvents() {
        super.bindEvents();

        $('#input-select-container').on('change', '#input-select', this.controller.changeInput.bind(this.controller));

        $('#input-type-select').on('change', this.controller.changeSource.bind(this.controller));

        $('#source-config-container').on('click', '#nmea-configuration-redirect', this.controller.nmeaRedirect.bind(this.controller));
        $('#source-config-container').on('change', '#nmea-instance-select', this.controller.changeInstance.bind(this.controller));

        $('#input-name').on('change', this.controller.changeInputName.bind(this.controller));

        $('#lat-degrees, #lat-minutes, #lat-direction').on('change', this.controller.changeLatitude.bind(this.controller));
        $('#long-degrees, #long-minutes, #long-direction').on('change', this.controller.changeLongitude.bind(this.controller));

        $('#source-config-container').on('change', '#can-devices', this.controller.changeCanbusDevice.bind(this.controller));
    }

    drawCanbusContainer(model) {
        $('#source-config-container').empty();

        let optionString = "<option value='0' data-address='0'>No Device</option>";
        model.listOfCanDevices.forEach(function(device) {
            optionString += "<option value='" + device.uniqueid + "' data-address='" + device.address + "'>" + device.displayName + "</option>";
        });

        $('#source-config-container').append(
                '<div>'
            +       '<div class="flex-row">'
            +           '<div class="flex-heading">'
            +               '<h2>CANbus Device:</h2>'
            +           '</div>'
            +           '<select id="can-devices">'
            +               optionString
            +           '<select>'
            +       '</div>'
            +   '</div>'
        );

        // Set selected option as the one that has been read from the database
        $('#can-devices').find('option[value="' + model.uniqueid + '"]').prop('selected', true);

        const canDevice = model.listOfCanDevices.find(device => {
            return device.uniqueid === model.uniqueid;
        });

        let address = "0";
        if (canDevice !== undefined)
        {
            address = canDevice.address;

            if (!canDevice.isConnected)
                $('#can-devices').addClass('bad-source');
        }
        else
            $('#can-devices').addClass('bad-source');

        $('.field-container:not(#variation)').attr('data-sender', 'canbus');
        $('.field-container:not(#variation)').attr('data-instance', address);

        $('#source-config-container').show();
    }

    drawNMEAContainer(model) {
        $('#source-config-container').empty();
        $('#source-config-container').append(
                '<div class="flex-half">'
            +       '<div class="flex-row">'
            +           '<div class="flex-heading">'
            +               '<h2>NMEA0183 Channel:</h2>'
            +           '</div>'
            +           '<select id="nmea-instance-select"></select>'
            +       '</div>'
            +       '<a id="nmea-configuration-redirect" class="button button-gray button-medium redirect-button">NMEA0183 Settings</a>'
            +   '</div>'
        );
        $('#source-config-container').show();

        let device = model.availableSources.find(source => (source.internalName == dataSouceDisplayNames.NMEA.toLowerCase()));
        device.ports.forEach(function(port) {
            $('#nmea-instance-select').append(
                '<option value="' + port.internalName + '">' + port.displayName + '</option>'
            );
        });

        let selectedOptionObj = $('#nmea-instance-select').find('option[value="' + model.instance + '"]');
        
        if (selectedOptionObj.length == 0) {
            model.instance = $('#nmea-instance-select').val();
            selectedOptionObj = $('#nmea-instance-select').find('option[value="' + model.instance + '"]');
        }

        selectedOptionObj.prop('selected', true);

        $('#nmea-instance-select').find('option[value="' + model.instance + '"]').prop('selected', true);

        $('.field-container:not(#variation)').attr('data-sender', 'nmea0183');
        $('.field-container:not(#variation)').attr('data-instance', model.instance);
    }

    update(model) {
        
        let optionString = "";
        for (let i = 0; i < model.maxInputNumber; i++) {
            optionString += "<option value='" + gpsSourcePrefix + i + "'>" + "Position " + (i + 1) + " - " + model.data[gpsSourcePrefix + i].name + "</option>";
        };

        $('#input-select-container').empty();
        $('#input-select-container').append(
                '<div>'
            +       '<div class="flex-row">'
            +           '<select id="input-select">'
            +               optionString
            +           '<select>'
            +       '</div>'
            +   '</div>'
        );

        $('#input-select').find('option[value="' + model.currentSelectedInput + '"]').prop('selected', true);

        $('#input-name').val(model.inputName);

        $('#input-type-select').empty();
        model.availableSources.forEach(function(source) {
            if (source.hasOwnProperty("internalName") && source.hasOwnProperty("displayName")) {
                $('#input-type-select').append(
                    '<option value="' + source.internalName + '">' + source.displayName + '</option>'
                );
            }
        });
        let source = $('#input-type-select').find('option[value="' + model.source.toLowerCase() + '"]');
        if (source.length == 0) {
            $('#input-type-select').css('outline', '1px solid red');
        } else {
            source.prop('selected', true);
        }

        switch (model.source.toLowerCase())
        {
            case "canbus":
                this.drawCanbusContainer(model);
                break;

            case "nmea0183":
                this.drawNMEAContainer(model);
                break;

            case "atpilot":
            case "fastnet":
            default:
                $('#source-config-container').hide();
                if (model.source.includes("main interface") || model.source.includes("analogue")) {

                    $('.field-container:not(#variation)').attr('data-sender',  "Processor");
                    $('.field-container:not(#variation)').attr('data-instance', 0);
                }
                else {

                    $('.field-container:not(#variation)').attr('data-sender', model.source);
                    $('.field-container:not(#variation)').attr('data-instance', 0);
                }
                break;
        }

        $('#lat-degrees').inputmask({
            alias: "decimal",
            digits: 0,
            allowMinus: false,   
            allowPlus: false,     
            digitsOptional: false,
            autoUnmask: true,
            clearMaskOnLostFocus: false,
            min: "0",
            max: "90"
        });

        $('#long-degrees').inputmask({
            alias: "decimal",
            digits: 0,
            allowMinus: false,   
            allowPlus: false,     
            digitsOptional: false,
            autoUnmask: true,
            clearMaskOnLostFocus: false,
            min: "0",
            max: "180"
        });

        $('#lat-minutes, #long-minutes').inputmask({
            alias: "decimal",
            digits: 3,
            allowMinus: false,   
            allowPlus: false,     
            digitsOptional: false,
            autoUnmask: true,
            clearMaskOnLostFocus: false,
            max: "59.999"
        });

        $('#lat-degrees').val(model.latArray[0]);
        $('#lat-minutes').val(model.latArray[1]);

        $('#long-degrees').val(model.longArray[0]);
        $('#long-minutes').val(model.longArray[1]);

        $('#lat-direction').val(model.latArray[2] == "N" ? "North" : "South").prop('selected', true);
        $('#long-direction').val(model.longArray[2] == "E" ? "East" : "West").prop('selected', true);
    }
}