/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

import { GeckoViewModule } from "resource://gre/modules/GeckoViewModule.sys.mjs";

export class GeckoViewMediaControl extends GeckoViewModule {
  onInit() {
    debug`onInit`;
  }

  onInitBrowser() {
    debug`onInitBrowser`;

    const options = {
      mozSystemGroup: true,
      capture: false,
    };

    this.controller.addEventListener("activated", this, options);
    this.controller.addEventListener("deactivated", this, options);
    this.controller.addEventListener("supportedkeyschange", this, options);
    this.controller.addEventListener("positionstatechange", this, options);
    this.controller.addEventListener("metadatachange", this, options);
    this.controller.addEventListener("playbackstatechange", this, options);
  }

  onDestroyBrowser() {
    debug`onDestroyBrowser`;

    this.controller.removeEventListener("activated", this);
    this.controller.removeEventListener("deactivated", this);
    this.controller.removeEventListener("supportedkeyschange", this);
    this.controller.removeEventListener("positionstatechange", this);
    this.controller.removeEventListener("metadatachange", this);
    this.controller.removeEventListener("playbackstatechange", this);
  }

  onEnable() {
    debug`onEnable`;

    if (this.controller.isActive) {
      this.handleActivated();
    }

    this.registerListener([
      "GeckoView:MediaSession:Play",
      "GeckoView:MediaSession:Pause",
      "GeckoView:MediaSession:Stop",
      "GeckoView:MediaSession:NextTrack",
      "GeckoView:MediaSession:PrevTrack",
      "GeckoView:MediaSession:SeekForward",
      "GeckoView:MediaSession:SeekBackward",
      "GeckoView:MediaSession:SkipAd",
      "GeckoView:MediaSession:SeekTo",
      "GeckoView:MediaSession:MuteAudio",
    ]);
  }

  onDisable() {
    debug`onDisable`;

    this.unregisterListener();
  }

  get controller() {
    return this.browser.browsingContext.mediaController;
  }

  onEvent(aEvent, aData) {
    debug`onEvent: event=${aEvent}, data=${aData}`;

    switch (aEvent) {
      case "GeckoView:MediaSession:Play":
        this.controller.play();
        break;
      case "GeckoView:MediaSession:Pause":
        this.controller.pause();
        break;
      case "GeckoView:MediaSession:Stop":
        this.controller.stop();
        this.eventDispatcher.sendRequest({
          type: "GeckoView:MediaSession:Playback:None",
        });
        break;
      case "GeckoView:MediaSession:NextTrack":
        this.controller.nextTrack();
        break;
      case "GeckoView:MediaSession:PrevTrack":
        this.controller.prevTrack();
        break;
      case "GeckoView:MediaSession:SeekForward":
        this.controller.seekForward();
        break;
      case "GeckoView:MediaSession:SeekBackward":
        this.controller.seekBackward();
        break;
      case "GeckoView:MediaSession:SkipAd":
        this.controller.skipAd();
        break;
      case "GeckoView:MediaSession:SeekTo":
        this.controller.seekTo(aData.time, aData.fast);
        break;
      case "GeckoView:MediaSession:MuteAudio":
        if (aData.mute) {
          this.browser.mute();
        } else {
          this.browser.unmute();
        }
        break;
    }
  }

  // eslint-disable-next-line complexity
  handleEvent(aEvent) {
    debug`handleEvent: ${aEvent.type}`;

    switch (aEvent.type) {
      case "activated":
        this.handleActivated();
        break;
      case "deactivated":
        this.handleDeactivated();
        break;
      case "supportedkeyschange":
        this.handleSupportedKeysChanged();
        break;
      case "positionstatechange":
        this.handlePositionStateChanged(aEvent);
        break;
      case "metadatachange":
        this.handleMetadataChanged();
        break;
      case "playbackstatechange":
        this.handlePlaybackStateChanged();
        break;
      default:
        warn`Unknown event type ${aEvent.type}`;
        break;
    }
  }

  handleActivated() {
    debug`handleActivated`;

    this.eventDispatcher.sendRequest({
      type: "GeckoView:MediaSession:Activated",
    });
  }

  handleDeactivated() {
    debug`handleDeactivated`;

    this.eventDispatcher.sendRequest({
      type: "GeckoView:MediaSession:Deactivated",
    });
  }

  handlePositionStateChanged(aEvent) {
    debug`handlePositionStateChanged`;

    this.eventDispatcher.sendRequest({
      type: "GeckoView:MediaSession:PositionState",
      state: {
        duration: aEvent.duration,
        playbackRate: aEvent.playbackRate,
        position: aEvent.position,
      },
    });
  }

  handleSupportedKeysChanged() {
    const supported = this.controller.supportedKeys;

    debug`handleSupportedKeysChanged ${supported}`;

    // Mapping it to a key-value store for compatibility with the JNI
    // implementation for now.
    const features = new Map();
    supported.forEach(key => {
      features[key] = true;
    });

    this.eventDispatcher.sendRequest({
      type: "GeckoView:MediaSession:Features",
      features,
    });
  }

  handleMetadataChanged() {
    let metadata = null;
    try {
      metadata = this.controller.getMetadata();
    } catch (e) {
      warn`Metadata not available`;
    }
    debug`handleMetadataChanged ${metadata}`;

    if (metadata) {
      this.eventDispatcher.sendRequest({
        type: "GeckoView:MediaSession:Metadata",
        metadata,
      });
    }
  }

  handlePlaybackStateChanged() {
    const state = this.controller.playbackState;
    let type = null;

    debug`handlePlaybackStateChanged ${state}`;

    switch (state) {
      case "none":
        type = "GeckoView:MediaSession:Playback:None";
        break;
      case "paused":
        type = "GeckoView:MediaSession:Playback:Paused";
        break;
      case "playing":
        type = "GeckoView:MediaSession:Playback:Playing";
        break;
    }

    if (!type) {
      return;
    }

    this.eventDispatcher.sendRequest({
      type,
    });
  }
}

const { debug, warn } = GeckoViewMediaControl.initLogging(
  "GeckoViewMediaControl"
);
