// Created by xander on 6/21/2021
import videojs from 'video.js';

import RecSys from 'extras/recsys/recsys';
const VERSION = '0.0.1';

/* RecSys */
const PlayerEvent = {
  event: {
    start: 0, // event types
    stop: 1,
    scrub: 2,
    speed: 3,
    ended: 4,
  },
};

function newRecsysPlayerEvent(eventType, offset, arg) {
  if (arg) {
    return {
      event: eventType,
      offset: offset,
      arg: arg,
    };
  } else {
    return {
      event: eventType,
      offset: offset,
    };
  }
}

const defaults = {
  videoId: null,
  userId: 0,
  debug: false,
  embedded: false,
};

const Component = videojs.getComponent('Component');
const registerPlugin = videojs.registerPlugin || videojs.plugin;

class RecsysPlugin extends Component {
  constructor(player, options) {
    super(player, options);

    // Plugin started
    if (options.debug) {
      this.log(`Created recsys plugin for: videoId:${options.videoId}`);
    }

    // To help with debugging, we'll add a global vjs object with the video js player
    window.vjs = player;

    this.player = player;
    this.lastTimeUpdate = null;
    this.currentTimeUpdate = null;
    this.inPause = false;

    // Plugin event listeners
    player.on('playing', (event) => this.onPlay(event));
    player.on('pause', (event) => this.onPause(event));
    player.on('ended', (event) => this.onEnded(event));
    player.on('ratechange', (event) => this.onRateChange(event));
    player.on('timeupdate', (event) => this.onTimeUpdate(event));
    player.on('seeked', (event) => this.onSeeked(event));

    // Event trigger to send recsys event
    player.on('dispose', (event) => this.onDispose(event));
  }

  onPlay(event) {
    const recsysEvent = newRecsysPlayerEvent(PlayerEvent.event.start, this.player.currentTime());
    this.log('onPlay', recsysEvent);
    RecSys.onRecsysPlayerEvent(this.options_.videoId, recsysEvent, this.options_.embedded);

    this.inPause = false;
    this.lastTimeUpdate = recsysEvent.offset;
  }

  onPause(event) {
    const recsysEvent = newRecsysPlayerEvent(PlayerEvent.event.stop, this.player.currentTime());
    this.log('onPause', recsysEvent);
    RecSys.onRecsysPlayerEvent(this.options_.videoId, recsysEvent);

    this.inPause = true;
  }

  onEnded(event) {
    const recsysEvent = newRecsysPlayerEvent(PlayerEvent.event.ended, this.player.currentTime());
    this.log('onEnded', recsysEvent);
    RecSys.onRecsysPlayerEvent(this.options_.videoId, recsysEvent);
  }

  onRateChange(event) {
    const recsysEvent = newRecsysPlayerEvent(
      PlayerEvent.event.speed,
      this.player.currentTime(),
      this.player.playbackRate()
    );
    this.log('onRateChange', recsysEvent);
    RecSys.onRecsysPlayerEvent(this.options_.videoId, recsysEvent);
  }

  onTimeUpdate(event) {
    const nextCurrentTime = this.player.currentTime();

    if (!this.inPause && Math.abs(this.lastTimeUpdate - nextCurrentTime) < 0.5) {
      // Don't update lastTimeUpdate if we are in a pause segment.
      //
      // However, if we aren't in a pause and the time jumped
      // the onTimeUpdate event probably fired before the pause and seek.
      // Don't update in that case, either.
      this.lastTimeUpdate = this.currentTimeUpdate;
    }

    this.currentTimeUpdate = nextCurrentTime;
  }

  onSeeked(event) {
    const curTime = this.player.currentTime();

    // There are three patterns for seeking:
    //
    // Assuming the video is playing,
    //
    // 1. Dragging the player head emits: onPause -> onSeeked -> onSeeked -> ... -> onPlay
    // 2. Key press left right emits: onSeeked -> onPlay
    // 3. Clicking a position emits: onPause -> onSeeked -> onPlay
    //
    // If the video is NOT playing,
    //
    // 1. Dragging the player head emits: onSeeked
    // 2. Key press left right emits: onSeeked
    // 3. Clicking a position emits: onSeeked
    const fromTime = this.lastTimeUpdate;

    if (fromTime !== curTime) {
      // This removes duplicates that aren't useful.
      const recsysEvent = newRecsysPlayerEvent(PlayerEvent.event.scrub, fromTime, curTime);
      this.log('onSeeked', recsysEvent);
      RecSys.onRecsysPlayerEvent(this.options_.videoId, recsysEvent);
    }
  }

  onDispose(event) {
    // Some browsers don't send onEnded event when closing player/browser, force it here
    const recsysEvent = newRecsysPlayerEvent(PlayerEvent.event.stop, this.player.currentTime());
    RecSys.onRecsysPlayerEvent(this.options_.videoId, recsysEvent);
    RecSys.onPlayerDispose(this.options_.videoId, this.options_.embedded);
  }

  log(...args) {
    if (this.options_.debug) {
      console.log(`Recsys Player Debug:`, JSON.stringify(args));
    }
  }
}

videojs.registerComponent('recsys', RecsysPlugin);

const onPlayerReady = (player, options) => {
  player.recsys = new RecsysPlugin(player, options);
};

/**
 * Initialize the plugin.
 *
 * @function plugin
 * @param    {Object} [options={}]
 */
const plugin = function (options) {
  this.ready(() => {
    onPlayerReady(this, videojs.mergeOptions(defaults, options));
  });
};

plugin.VERSION = VERSION;

registerPlugin('recsys', plugin);

export default plugin;