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

        this.category = 'Heel';
        this.data = new Object();
        this.widgets = new Object();
        this.listOfCanDevices = [];

        this.isValidOrientation = {
            'vertical': {
                'aft': {
                    'down': true,
                    'port': true,
                    'starboard': true
                },
                'forward': {
                    'down': true,
                    'port': true,
                    'starboard': true
                },
                'port': {
                    'down': true,
                    'forward': true,
                    'aft': true
                },
                'starboard': {
                    'down': true,
                    'forward': true,
                    'aft': true
                }
            },
            'horizontal': {
                'down': {
                    'forward': true,
                    'aft': true,
                    'port': true,
                    'starboard': true
                },
                'up': {
                    'forward': true,
                    'aft': true,
                    'port': true,
                    'starboard': true
                }
            }
        }

        this.init();
    }

    get backPlateOrientation() {
        return this.data['Heel']['backplate'];
    }

    get cableExitOrientation() {
        return this.data['Heel']['cableexit'];
    }

    get canBusDevice() {
        return this.sources.find(source => source.internalName == dataSouceDisplayNames.CANbus.toLowerCase());
    }

    get heelOffset() {
        return this.data['Heel']['heel calibration'];
    }
    set heelOffset(heelOffset) {
        this.data['Heel']['heel calibration'] = heelOffset;
    }

    get heelSource() {
        return this.data['Heel']['source'];
    }
    set heelSource(heelSource) {
        this.data['Heel']['source'] = heelSource;
        this.setDefaultInstance('Heel', heelSource);
        this.resetDrawableWidgets();
    }

    get heelSources() {
        var heelSources = new Array();

        this.sources.forEach(function(dataSource) {
            dataSource.inputs.forEach(function(channel) {
                if (channel.sourceData['function'] == 3) {
                    heelSources.push(channel);
                }
            });
        });

        heelSources.sort(function(a, b) {
            return (a.internalName > b.internalName) ? 1 : -1;
        });

        return heelSources;
    }

    get heelSourceInstance() {
        return this.data['Heel']['instance'];
    }
    set heelSourceInstance(instance) {
        this.data['Heel']['instance'] = instance;
        this.notifyAll();
    }

    get nmeaSource() {
        return this.sources.find(source => source.internalName == dataSouceDisplayNames.NMEA.toLowerCase());
    }

    get trimOffset() {
        return this.data['Heel']['trim calibration'];
    }
    set trimOffset(trimOffset) {
        this.data['Heel']['trim calibration'] = trimOffset;
    }

    get trimSource() {
        return this.data['Trim']['source'];
    }
    set trimSource(trimSource) {
        this.data['Trim']['source'] = trimSource;
        this.setDefaultInstance('Trim', trimSource);
        this.resetDrawableWidgets();
    }

    get trimSources() {
        var trimSources = new Array();

        this.sources.forEach(function(dataSource) {
            dataSource.inputs.forEach(function(channel) {
                if (channel.sourceData['function'] == 4) {
                    trimSources.push(channel);
                }
            });
        });

        trimSources.sort(function(a, b) {
            return (a.internalName > b.internalName) ? 1 : -1;
        });

        return trimSources;
    }

    get trimSourceInstance() {
        return this.data['Trim']['instance'];
    }
    set trimSourceInstance(instance) {
        this.data['Trim']['instance'] = instance;
        this.notifyAll();
    }

    get useInternalSensor() {
        return (this.data['Heel']['enabled'] == 'true');
    }
    set useInternalSensor(useInternalSensor) {
        this.data['Heel']['enabled'] = useInternalSensor;
        this.resetDrawableWidgets();
    }

    get heelUniqueid() {
        return this.data.Heel.uniqueid;
    }

    set heelUniqueid(heelUniqueid) {
        this.data.Heel.uniqueid = heelUniqueid;
    }

    get trimUniqueid() {
        return this.data.Trim.uniqueid;
    }

    set trimUniqueid(trimUniqueid) {
        this.data.Trim.uniqueid = trimUniqueid;
    }

    get heelCanbusSourceAddress() {
        const canDevice = this.listOfCanDevices.find(device => {
            return device.uniqueid === this.heelUniqueid;
        });
        
        return canDevice === undefined ? "0" : canDevice.address;
    }

    get trimCanbusSourceAddress() {
        const canDevice = this.listOfCanDevices.find(device => {
            return device.uniqueid === this.trimUniqueid;
        });
        
        return canDevice === undefined ? "0" : canDevice.address;
    }

    changeOrientation(orientation, backplate, cableexit) {
        if (this.isValidOrientation[orientation][backplate][cableexit]) {
            this.data['Heel']['orientation'] = orientation;
            this.data['Heel']['backplate'] = backplate;
            this.data['Heel']['cableexit'] = cableexit;
        } else {
            this.data['Heel']['orientation'] = orientation;
            this.data['Heel']['backplate'] = backplate;
            //Set to first valid value
            this.data['Heel']['cableexit'] = Object.keys(this.isValidOrientation[orientation][backplate])[0];
        }
        this.resetDrawableWidgets();
        return this.isValidOrientation[orientation][backplate][cableexit] || false;
    }

    init() {
        var async = new Array();
        var defaults = {
            'Heel': {
                'backplate': 'starboard',
                'cableexit': 'down',
                'enabled': 'true',
                'heel calibration': '0.0',
                'inertiadata': true,
                'instance': '1',
                'orientation': 'vertical',
                'source': 'NMEA0183',
                'trim calibration': '0.0',
                'uniqueid': '0'
            },
            'Trim': {
                'instance': '1',
                'source': 'NMEA0183',
                'uniqueid': '0'
            }
        }
        var self = this;

        this.data = $.extend(true, {}, this.data, defaults);

        async.push(readSection('Heel').done(function(sectionJSON) {
            $.extend(self.data['Heel'], JSON.parse(sectionJSON));
        }));

        async.push(readSection('Trim').done(function(sectionJSON) {
            $.extend(self.data['Trim'], JSON.parse(sectionJSON));
        }));

        async.push(getSources("Heel").done(function(sources, canbusDevices) {
            self.sources = sources;
            self.listOfCanDevices = canbusDevices;
        }));

        $.when.apply($, async).then(function() {
            // Can't have a instance value of null, so have to overwrite if wrongly saved to DB.
            if (self.data['Heel']['instance'] === "")
                self.data['Heel']['instance'] = "1";
        
            if (self.data['Trim']['instance'] === "")
                self.data['Trim']['instance'] = "1";

            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.setDrawableWidgets();
            self.notifyAll();
        })
    }

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

    setDefaultInstance(attitude, source) {
        switch (source) {
            case 'Analogue':
                if (attitude == 'Heel') {
                    this.data[attitude].instance = (this.heelSources.length > 0) ? 
                        this.heelSources[0].internalName.split(':')[1] + this.heelSources[0].channelNumber : '';
                } else if (attitude == 'Trim') {
                    this.data[attitude].instance = (this.trimSources.length > 0) ? 
                        this.trimSources[0].internalName.split(':')[1] + this.trimSources[0].channelNumber : '';
                }
                break;
            case 'CanBus':
                this.data[attitude].instance = this.canBusDevice.address;
                break;
            case 'NMEA0183':
                this.data[attitude].instance = (this.nmeaSource.ports.length > 0) ? this.nmeaSource.ports[0].internalName : '';
                break;
        }
    }

    setDrawableWidgets() {
        if (this.useInternalSensor) {
            this.widgets.displayInternalSensorWidget = true;
        } else {
            this.widgets.displayExternalSensorWidget = true;
            this.widgets.displayHeelInstanceWidget = this.heelSource;
            this.widgets.displayTrimInstanceWidget = this.trimSource;
        }
    }
}