class SensorSelectModel extends ObservableModel {
    constructor(sensorType) {
        super();

        this.isChangeApplied = true;
        this.sensors = new Array();
        this.sensorSelectionData = new Object();
        this.sensorType = sensorType;
        this.switchingInterval = -1;
        this.widgets = new Object();

        this.init();
    }

    get hysteresis() {
        return this.sensorSelectionData['hysteresis'];
    }
    set hysteresis(hysteresis) {
        this.sensorSelectionData['hysteresis'] = hysteresis;
    }

    get selectionMode() {
        return (this.sensorSelectionData['switching'] == true ? this.selectionModeAutomatic : this.selectionModeManual);
    }
    set selectionMode(selectionMode) {
        this.sensorSelectionData['switching'] = (selectionMode == this.selectionModeAutomatic);
    }

    get selectedSource() {
        return parseInt(this.sensorSelectionData['default'].slice(-1));
    }
    set selectedSource(selectedSource) {
        if (selectedSource > -1) {
            this.sensorSelectionData['default'] = this.sensors[parseInt(selectedSource)].sensorSection;
        } else {
            this.sensorSelectionData['default'] = '';
        }
    }

    get switchingCriteria() {
        return this.sensorSelectionData['changeovermode'].toLowerCase();
    }
    set switchingCriteria(criteria) {
        this.sensorSelectionData['changeovermode'] = criteria;
        this.resetDrawableWidgets();
    }

    //ECMAScript class constants not yet in spec
    get selectionModeAutomatic() {
        return "automatic";
    }

    get selectionModeManual() {
        return "manual";
    }

    getTack() {
        var self = this;
        $.ajax({
            type: 'POST',
            url: '/helpers.php',
            data: {action: 'queryProcessor', query: 'fieldid=' + self.activeSensorField + '&senderid=ATProcessor&instance=0'},
            success: function(response) {
                var fieldMatchRegex = /(0x.*)=\s?(.*)\r/g;
                var match = null;
                if ((match = fieldMatchRegex.exec(response)) !== null) {
                    if ($.isNumeric(match[2]) && match[2] >= 0 && match[2] < self.sensors.length) {
                        if (self.selectedSource != match[2]) {
                            self.selectedSource = match[2];
                            self.resetDrawableWidgets();
                            self.notifyAll();
                        }
                    } else {
                        console.log('Invalid source: ' + match[2]);
                    }
                } else {
                    console.log('Processor failed to return current source.')
                }
            }
        });
    }

    init() {
        var async = new Array();
        var deferred = new $.Deferred();
        var self = this;

        var defaults = {
            default: this.sensorType.toLowerCase() + 'source0',
            changeovermode: 'heel',
            hysteresis: '10',
            port: this.sensorType.toLowerCase() + 'source0',
            starboard: this.sensorType.toLowerCase() + 'source1',
            switching: '',
            upwind: '',
            downwind: ''
        };
    
        async.push(deferred.promise());
        readSingle('Settings', this.sensorType + 'Inputs').done(function(inputCount) {
            var async = new Array();

            if (inputCount == '') inputCount = 1;

            if (inputCount > 1) {
                for (var i = 0; i < inputCount; i++) {
                    self.sensors.push(self.sensorType.toLowerCase() == "speed" ? new SpeedSensorModel(i) : new WindSensorModel(i));
                    async.push(self.sensors[i].init());
                }
            } else {
                window.location.replace(self.sensorType.toLowerCase() == "speed" ? "speedLog.php?src=0" : "windMeasured.php?src=0");
            }
            
            $.when.apply($, async).then(function() {
                deferred.resolve();
            })
        });
        
        async.push(readSection(self.sensorType).done(function(section) {
            self.sensorSelectionData = $.extend({}, defaults, JSON.parse(section));
        }));

        $.when.apply($, async).then(function() {
            self.setDrawableWidgets();
            if (self.selectionMode == self.selectionModeAutomatic) {
                self.startAutoSwitching();
            }
            self.notifyAll();
        });
    }

    persistData() {
        var databaseArray = new Array();
        var deferred = new $.Deferred();
        var self = this;

        $.each(self.sensorSelectionData, function(key, value) {
            databaseArray.push(
                {'section': self.sensorType, 'key': key, 'value': value}
            );
        });

        writeToDatabase(databaseArray, self.databaseCategory).done(function() {
            self.isChangeApplied = true;
            deferred.resolve();
        });

        return deferred.promise();
    }

    resetDrawableWidgets() {
        this.widgets = new Array();
        this.setDrawableWidgets();
    }

    selectModeAutomatic(portTackSensor, stbdTackSensor) {
        this.selectionMode = this.selectionModeAutomatic;

        this.portTackSensor = portTackSensor;
        this.stbdTackSensor = stbdTackSensor;
        this.isChangeApplied = false;

        this.startAutoSwitching();
    }

    selectModeManual(sensorNumber) {
        this.selectionMode = this.selectionModeManual;
        this.selectedSource = sensorNumber;

        if (this.switchingInterval != -1) {
            clearInterval(this.switchingInterval);
            this.switchingInterval = -1;
        }

        this.resetDrawableWidgets();
    }

    setDrawableWidgets() {
        this.widgets['drawRadioButtons'] = true;
        this.widgets['drawSourceDisplay'] = true;
        if (this.selectionMode == this.selectionModeManual) {
            this.widgets['drawArrow' + (this.selectedSource + 1)] = true;
            this.widgets['drawActiveSource'] = true;
        } else {
            if (this.isChangeApplied) {
                if (this.selectedSource == this.portTackSensor) {
                    this.widgets['drawArrow' + (this.portTackSensor + 1)] = true;
                } else {
                    this.widgets['drawArrow' + (this.stbdTackSensor + 1)] = true;
                }
                this.widgets['drawActiveSource'] = true;
            } else {
                this.widgets['drawApplyBox'] = true;
            }
        }
        if (this.switchingCriteria == "heel") {
            this.widgets['drawHeelField'] = true;
            this.widgets['drawTackField'] = false;
        } else if (this.switchingCriteria == "wind") {
            this.widgets['drawWindField'] = true;
            this.widgets['drawTackField'] = true;
        } else if (this.switchingCriteria == "twa") {
            this.widgets['drawTWAField'] = true;
            this.widgets['drawPOSField'] = true;
        }
        this.widgets['drawSwitchingCriteria'] = true;
        this.widgets['drawLoadingBox'] = false;
    }

    startAutoSwitching() {
        this.selectedSource = -1;

        //Hide active source
        this.widgets['drawActiveSource'] = false;
        this.widgets['drawArrow1'] = false;
        this.widgets['drawArrow2'] = false;

        //Show loading widget
        if (this.isChangeApplied) {
            this.widgets['drawLoadingBox'] = true;
        } else {
            this.widgets['drawApplyBox'] = true;
        }

        if (this.switchingInterval == -1) {
            this.switchingInterval = setInterval(this.getTack.bind(this), 1000);
        }
    }
}

class DepthSensorSelectModel extends SensorSelectModel {
    constructor() {
        super("Depth");

        this.activeSensorField = "0x0413";
        this.databaseCategory = "Depth";
    }

    init() {
        let async = [];
        let deferred = new $.Deferred();
        let self = this;

        let defaults = {
            default: this.sensorType.toLowerCase() + 'source0',
            changeovermode: 'heel',
            hysteresis: '10',
            port: this.sensorType.toLowerCase() + 'source0',
            starboard: this.sensorType.toLowerCase() + 'source1',
            switching: ''
        };

        async.push(deferred.promise());
        readSingle("Depth", "SourceCount").done(function(inputCount) {
            let sourceAsync = [];
            if (inputCount == '') inputCount = 1;

            if (inputCount > 1) {
                for (var i = 0; i < inputCount; i++) {
                    self.sensors.push(new DepthSensorModel(i));
                    sourceAsync.push(self.sensors[i].init());
                }
            } else {
                window.location.replace("depth.php?src=0");
            }

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

        async.push(readSection(self.sensorType).done(function(section) {
            self.sensorSelectionData = $.extend({}, defaults, JSON.parse(section));
        }));

        $.when.apply($, async).then(function() {
            self.setDrawableWidgets();
            if (self.selectionMode == self.selectionModeAutomatic) {
                self.startAutoSwitching();
            }
            self.notifyAll();
        });
    }    

    get portTackSensor() {
        return parseInt(this.sensorSelectionData['starboard'].slice(-1));
    }
    set portTackSensor(sensorNumber) {
        this.isChangeApplied = false;
        this.sensorSelectionData['starboard'] = this.sensors[sensorNumber].sensorSection;
    }

    get stbdTackSensor() {
        return parseInt(this.sensorSelectionData['port'].slice(-1));
    }
    set stbdTackSensor(sensorNumber) {
        this.isChangeApplied = false;
        this.sensorSelectionData['port'] = this.sensors[sensorNumber].sensorSection;
    } 
}

class SpeedSensorSelectModel extends SensorSelectModel {
    constructor() {
        super("Speed");

        this.activeSensorField = "0x1206";
        this.databaseCategory = "SpeedLog";
    }

    //Speed switching uses port sensor when on starboard tack
    get portTackSensor() {
        return parseInt(this.sensorSelectionData['starboard'].slice(-1));
    }
    set portTackSensor(sensorNumber) {
        this.isChangeApplied = false;
        this.sensorSelectionData['starboard'] = this.sensors[sensorNumber].sensorSection;
    }

    get stbdTackSensor() {
        return parseInt(this.sensorSelectionData['port'].slice(-1));
    }
    set stbdTackSensor(sensorNumber) {
        this.isChangeApplied = false;
        this.sensorSelectionData['port'] = this.sensors[sensorNumber].sensorSection; 
    }
}

class WindSensorSelectModel extends SensorSelectModel {
    constructor() {
        super("Wind");

        this.activeSensorField = "0x1317";
        this.databaseCategory = "WindData";
    }

    //Speed switching uses port sensor when on starboard tack
    get portTackSensor() {
        if (this.switchingCriteria != "twa") {
            return parseInt(this.sensorSelectionData['port'].slice(-1));
        } else {
            return parseInt(this.sensorSelectionData['upwind'].slice(-1));
        }
    }
    set portTackSensor(sensorNumber) {
        this.isChangeApplied = false;
        if (this.switchingCriteria != "twa") {
            this.sensorSelectionData['port'] = this.sensors[sensorNumber].sensorSection;
        } else {
            this.sensorSelectionData['upwind'] = this.sensors[sensorNumber].sensorSection;
        } 
    }

    get stbdTackSensor() {
        if (this.switchingCriteria != "twa") {
            return parseInt(this.sensorSelectionData['starboard'].slice(-1));
        } else {
            return parseInt(this.sensorSelectionData['downwind'].slice(-1));
        }
    }
    set stbdTackSensor(sensorNumber) {
        this.isChangeApplied = false;
        if (this.switchingCriteria != "twa") {
            this.sensorSelectionData['starboard'] = this.sensors[sensorNumber].sensorSection; 
        } else {
            this.sensorSelectionData['downwind'] = this.sensors[sensorNumber].sensorSection;
        }
    }
}