// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported SensorDaemon */

const { Gio } = imports.gi;

const { loadInterfaceXML } = imports.misc.dbusUtils;
const { ServiceImplementation } = imports.dbusService;
const Signals = imports.signals;

const SensorDaemonIface = loadInterfaceXML('org.gnome.Shell.SensorDaemon');

const SensorProxyIface = loadInterfaceXML('net.hadess.SensorProxy');
const SensorProxyProxy = Gio.DBusProxy.makeProxyWrapper(SensorProxyIface);

const SensorTypes = {
    AMBIENT_LIGHT: 0,
    PROXIMITY: 1,
    COMPASS: 2,
    ACCELEROMETER: 3,
}

var SensorDaemon = class extends ServiceImplementation {
    constructor() {
        super(SensorDaemonIface, '/org/gnome/Shell/SensorDaemon');

        this._autoShutdown = false;

        this._monitoredSensors = new Array(SensorTypes.length).fill(0);
        this._claimedSensors = new Set();

        this._proxy = new SensorProxyProxy(Gio.DBus.system,
            'net.hadess.SensorProxy',
            '/net/hadess/SensorProxy');

        this._proxy.connect('g-properties-changed',
            this._propertiesChanged.bind(this));
    }

    async _propertiesChanged(proxy, properties) {
        for (const prop in properties.deepUnpack()) {
            switch (prop) {
            case 'HasAmbientLight':
                await this._claimOrReleaseSensor(SensorTypes.AMBIENT_LIGHT);
                if (this._claimedSensors.has(SensorTypes.AMBIENT_LIGHT))
                    this.emit('ambient-light-level', this._proxy.LightLevel);
                break;
            case 'HasProximity':
                await this._claimOrReleaseSensor(SensorTypes.PROXIMITY);
                if (this._claimedSensors.has(SensorTypes.PROXIMITY))
                    this.emit('proximity-near', this._proxy.ProximityNear);
                break;
            case 'LightLevel':
                this.emit('ambient-light-level', this._proxy.LightLevel);
                break;
            case 'ProximityNear':
                this.emit('proximity-near', this._proxy.ProximityNear);
                break;
            default:
                break;
            }
        }
    }

    _sensorAvailable(sensorType) {
        switch (sensorType) {
        case SensorTypes.AMBIENT_LIGHT:
            return this._proxy.HasAmbientLight;

        case SensorTypes.PROXIMITY:
            return this._proxy.HasProximity;
        }

        return false;
    }

    async _claimSensor(sensorType) {
        try {
            switch (sensorType) {
            case SensorTypes.AMBIENT_LIGHT:
                await this._proxy.ClaimLightAsync();
                break;
            case SensorTypes.PROXIMITY:
                await this._proxy.ClaimProximityAsync();
                break;
            }

            this._claimedSensors.add(sensorType);
        } catch(e) {
            log(`Claiming ${sensorType} sensor failed: ${e.message}`);
        }
    }

    async _releaseSensor(sensorType) {
        switch (sensorType) {
        case SensorTypes.AMBIENT_LIGHT:
            await this._proxy.ReleaseLightAsync();
            break;
        case SensorTypes.PROXIMITY:
            await this._proxy.ReleaseProximityAsync();
            break;
        }

        this._claimedSensors.delete(sensorType);
    }

    async _claimOrReleaseSensor(sensorType) {
        log("SENSORDAEMON: claiming/releasing sensor: " + sensorType + " avail " + this._sensorAvailable(sensorType));

        if (this._monitoredSensors[sensorType] > 0) {
            if (!this._claimedSensors.has(sensorType) && this._sensorAvailable(sensorType))
                await this._claimSensor(sensorType);
        } else {
            if (this._claimedSensors.has(sensorType))
                await this._releaseSensor(sensorType);
        }
    }

    async claimProximity() {
        this._monitoredSensors[SensorTypes.PROXIMITY]++;
        if (this._monitoredSensors[SensorTypes.PROXIMITY] === 1)
            await this._claimOrReleaseSensor(SensorTypes.PROXIMITY);

        return this._claimedSensors.has(SensorTypes.PROXIMITY);
    }

    async releaseProximity() {
        this._monitoredSensors[SensorTypes.PROXIMITY]--;
        if (this._monitoredSensors[SensorTypes.PROXIMITY] === 0)
            await this._claimOrReleaseSensor(SensorTypes.PROXIMITY);
    }

    get proximityNear() {
        return this._proxy.ProximityNear;
    }
};
Signals.addSignalMethods(SensorDaemon.prototype);
