<commit_before>import GAMES from 'anyware/lib/game-logic/constants/games';
import SculptureStore from 'anyware/lib/game-logic/sculpture-store';
import SculptureActionCreator from 'anyware/lib/game-logic/actions/sculpture-action-creator';

import SerialManager from '../serial/serial-manager';
import * as SerialProtocol from '../serial/serial-protocol';
const {SerialProtocolCommandBuilder} = SerialProtocol;

export default class HandshakeView {
  constructor(store, config, dispatcher, serialManager) {
    this.store = store;
    this.config = config;
    this.serialManager = serialManager;
    this.serialManager.on(SerialManager.EVENT_COMMAND, this._handleCommand.bind(this));

    this.sculptureActionCreator = new SculptureActionCreator(dispatcher);

    this._pulseInterval = null;
    this._complete = false;

    this.store.on(SculptureStore.EVENT_CHANGE, this._handleChanges.bind(this));
  }

  reset() {
  }

  get handshakes() {
    return this.store.data.get('handshakes');
  }

  _handleChanges(changes) {
    this._handleCurrentGameChanges(changes);
    this._handleHandshakesChanges(changes);
  }

  _handleHandshakesChanges(changes) {
    const handshakesChanges = changes.handshakes;
    if (!handshakesChanges || !this.store.isReady) {
      return;
    }

    this._updateHandshakeVibrationIntensity();

    for (const sculptureId of Object.keys(handshakesChanges)) {
      if (handshakesChanges[sculptureId]) {
        this._activateLocationPanel(sculptureId);

        if (sculptureId === this.store.me) {
          // Pulsing needs to be stopped if the user has activated the
          // handshake because both cannot happen at once
          this._endPulsing();
          this._activateMiddlePanel();
        }
      }
      else {
        this._deactivateLocationPanel(sculptureId);

        if (sculptureId === this.store.me) {
          this._deactivateMiddlePanel();
        }
      }
    }
  }

  _activateLocationPanel(sculptureId) {
    const intensity = this.config.HANDSHAKE_HARDWARE.LOCATION_PANEL_ON_INTENSITY;
    const color = this.config.getLocationColor(sculptureId);
    const easing = this.config.HANDSHAKE_HARDWARE.LOCATION_PANEL_ON_EASING;

    this._locationPanelSet(sculptureId, {intensity, color, easing});
  }

  _deactivateLocationPanel(sculptureId) {
    const intensity = this.config.HANDSHAKE_HARDWARE.LOCATION_PANEL_OFF_INTENSITY;
    const easing = this.config.HANDSHAKE_HARDWARE.LOCATION_PANEL_OFF_EASING;

    this._locationPanelSet(sculptureId, {intensity, easing});
  }

  _activateMiddlePanel() {
    const intensity = this.config.HANDSHAKE_HARDWARE.MIDDLE_ON_INTENSITY;
    const color = this.config.HANDSHAKE_HARDWARE.MIDDLE_ON_COLOR || this.store.locationColor;
    const easing = this.config.HANDSHAKE_HARDWARE.MIDDLE_ON_EASING;

    this._middlePanelSet({intensity, color, easing});
  }

  _deactivateMiddlePanel() {
    const intensity = this.config.HANDSHAKE_HARDWARE.MIDDLE_OFF_INTENSITY;
    const color = this.config.HANDSHAKE_HARDWARE.MIDDLE_OFF_COLOR;
    const easing = this.config.HANDSHAKE_HARDWARE.MIDDLE_OFF_EASING;

    this._middlePanelSet({intensity, color, easing});
  }

  _locationPanelSet(sculptureId, {intensity, color, easing}) {
    const locationPanel = this.config.HANDSHAKE_HARDWARE.LOCATION_PANELS[sculptureId];

    this._handshakePanelSet(locationPanel, {intensity, color, easing});
  }

  _middlePanelSet({intensity, color, easing}) {
    const panel = this.config.HANDSHAKE_HARDWARE.MIDDLE_PANEL;
    this._handshakePanelSet(panel, {intensity, color, easing});
  }

  _handshakePanelSet(panel, {intensity, color, easing}) {
    const commandString = SerialProtocolCommandBuilder.buildPanelSet({
      stripId: this.config.LIGHTS.HANDSHAKE_STRIP,
      panelId: panel,
      intensity: intensity,
      color: color,
      easing: easing
    });
    this.serialManager.dispatchCommand(commandString);
  }

  _updateHandshakeVibrationIntensity() {
    const handshakes = this.handshakes;
    const count = Array.from(handshakes).reduce((total, sculptureId) => total + (handshakes.get(sculptureId) ? 1 : 0), 0);
    const commandString = SerialProtocolCommandBuilder.buildHandshake({
      numUsers: count
    });
    this.serialManager.dispatchCommand(commandString);
  }

  _handleCurrentGameChanges(changes) {
    if (!changes.hasOwnProperty("currentGame")) {
      return;
    }

    if (changes.currentGame === GAMES.HANDSHAKE) {
      // starting handshake game
      this._complete = false;
      this._beginPulsing();
    }
    else {
      this._endPulsing();
      this._complete = true;
    }
  }

  _beginPulsing() {
    // ensure that we can never orphan a timer
    this._endPulsing();

    this._pulse();

    this._pulseInterval = setInterval(() => {
      if (this._complete) {
        return;
      }

      this._pulse();
    }, this.config.HANDSHAKE_HARDWARE.PULSE_DELAY);
  }

  _endPulsing() {
    clearInterval(this._pulseInterval);
    this._pulseInterval = null;
  }

  _pulse() {
    const commandString = SerialProtocolCommandBuilder.buildPanelPulse({
      stripId: this.config.LIGHTS.HANDSHAKE_STRIP,
      panelId: '3',
      intensity: 100,
      color: 'white',
      easing: 'sleep'
    });
    this.serialManager.dispatchCommand(commandString);
  }

  _handleCommand(commandName, commandArgs) {
    if (commandName === SerialProtocol.HANDSHAKE_COMMAND) {
      const {numUsers} = commandArgs;

      if (parseInt(numUsers) > 0) {
        this.sculptureActionCreator.sendHandshakeActivate(this.store.me);
      }
      else {
        this.sculptureActionCreator.sendHandshakeDeactivate(this.store.me);
      }
    }
  }
}<commit_msg>Adapted to latest handshake changes, added activity timeout<commit_after>import GAMES from 'anyware/lib/game-logic/constants/games';
import SculptureStore from 'anyware/lib/game-logic/sculpture-store';
import SculptureActionCreator from 'anyware/lib/game-logic/actions/sculpture-action-creator';

import SerialManager from '../serial/serial-manager';
import * as SerialProtocol from '../serial/serial-protocol';
const {SerialProtocolCommandBuilder} = SerialProtocol;

export default class HandshakeView {
  constructor(store, config, dispatcher, serialManager) {
    this.store = store;
    this.config = config;
    this.serialManager = serialManager;
    this.serialManager.on(SerialManager.EVENT_COMMAND, this._handleCommand.bind(this));

    this.sculptureActionCreator = new SculptureActionCreator(dispatcher);

    this._pulseInterval = null;
    this._complete = false;
    this._activityTimeout = null;

    this.store.on(SculptureStore.EVENT_CHANGE, this._handleChanges.bind(this));
  }

  reset() {
  }

  get handshakes() {
    return this.store.data.get('handshakes');
  }

  _handleChanges(changes) {
    this._handleCurrentGameChanges(changes);
    this._handleHandshakesChanges(changes);
  }

  _handleCurrentGameChanges(changes) {
    if (!changes.hasOwnProperty("currentGame")) return;

    if (changes.currentGame === GAMES.HANDSHAKE) {
      // starting handshake game
      this._complete = false;
      this._beginPulsing();
    }
    else {
      this._endPulsing();
      this._complete = true;
    }
  }

  _handleHandshakesChanges(changes) {
    const handshakesChanges = changes.handshakes;
    if (!handshakesChanges || !this.store.isReady) return;

    this._updateHandshakeVibrationIntensity();

    console.log(JSON.stringify(handshakesChanges));
    for (const sculptureId of Object.keys(handshakesChanges)) {
      switch (handshakesChanges[sculptureId]) {
      case SculptureStore.HANDSHAKE_ACTIVE:
        this._activateLocationPanel(sculptureId);

        if (sculptureId === this.store.me) {
          // Pulsing needs to be stopped if the user has activated the
          // handshake because both cannot happen at once
          this._endPulsing();
          this._activateMiddlePanel();
        }
        break;
      case SculptureStore.HANDSHAKE_PRESENT:
        if (sculptureId === this.store.me) {
          this._deactivateMiddlePanel();
        }
        break;
      case SculptureStore.HANDSHAKE_OFF:
        this._deactivateLocationPanel(sculptureId);
        break;
      }
    }
  }

  _activateLocationPanel(sculptureId) {
    const intensity = this.config.HANDSHAKE_HARDWARE.LOCATION_PANEL_ON_INTENSITY;
    const color = this.config.getLocationColor(sculptureId);
    const easing = this.config.HANDSHAKE_HARDWARE.LOCATION_PANEL_ON_EASING;

    this._locationPanelSet(sculptureId, {intensity, color, easing});
  }

  _deactivateLocationPanel(sculptureId) {
    const intensity = this.config.HANDSHAKE_HARDWARE.LOCATION_PANEL_OFF_INTENSITY;
    const easing = this.config.HANDSHAKE_HARDWARE.LOCATION_PANEL_OFF_EASING;

    this._locationPanelSet(sculptureId, {intensity, easing});
  }

  _activateMiddlePanel() {
    const intensity = this.config.HANDSHAKE_HARDWARE.MIDDLE_ON_INTENSITY;
    const color = this.config.HANDSHAKE_HARDWARE.MIDDLE_ON_COLOR || this.store.locationColor;
    const easing = this.config.HANDSHAKE_HARDWARE.MIDDLE_ON_EASING;

    this._middlePanelSet({intensity, color, easing});
  }

  _deactivateMiddlePanel() {
    const intensity = this.config.HANDSHAKE_HARDWARE.MIDDLE_OFF_INTENSITY;
    const color = this.config.HANDSHAKE_HARDWARE.MIDDLE_OFF_COLOR;
    const easing = this.config.HANDSHAKE_HARDWARE.MIDDLE_OFF_EASING;

    this._middlePanelSet({intensity, color, easing});
  }

  _locationPanelSet(sculptureId, {intensity, color, easing}) {
    const locationPanel = this.config.HANDSHAKE_HARDWARE.LOCATION_PANELS[sculptureId];

    this._handshakePanelSet(locationPanel, {intensity, color, easing});
  }

  _middlePanelSet({intensity, color, easing}) {
    const panel = this.config.HANDSHAKE_HARDWARE.MIDDLE_PANEL;
    this._handshakePanelSet(panel, {intensity, color, easing});
  }

  _handshakePanelSet(panel, {intensity, color, easing}) {
    const commandString = SerialProtocolCommandBuilder.buildPanelSet({
      stripId: this.config.LIGHTS.HANDSHAKE_STRIP,
      panelId: panel,
      intensity: intensity,
      color: color,
      easing: easing
    });
    console.log(commandString);
    this.serialManager.dispatchCommand(commandString);
  }

  _updateHandshakeVibrationIntensity() {
    const handshakes = this.handshakes;
    const numUsers = Array.from(handshakes).reduce((total, sculptureId) => total + (handshakes.get(sculptureId) === SculptureStore.HANDSHAKE_ACTIVE ? 1 : 0), 0);
    const commandString = SerialProtocolCommandBuilder.buildHandshake({ numUsers });
    this.serialManager.dispatchCommand(commandString);
  }

  _beginPulsing() {
    // ensure that we can never orphan a timer
    this._endPulsing();

    this._pulse();

    this._pulseInterval = setInterval(() => {
      if (this._complete) {
        return;
      }

      this._pulse();
    }, this.config.HANDSHAKE_HARDWARE.PULSE_DELAY);
  }

  _endPulsing() {
    clearInterval(this._pulseInterval);
    this._pulseInterval = null;
  }

  _pulse() {
    const commandString = SerialProtocolCommandBuilder.buildPanelPulse({
      stripId: this.config.LIGHTS.HANDSHAKE_STRIP,
      panelId: '3',
      intensity: 100,
      color: 'white',
      easing: 'sleep'
    });
    this.serialManager.dispatchCommand(commandString);
  }

  _refreshActivityTimeout() {
    if (this._activityTimeout) clearTimeout(this._activityTimeout);
    this._activityTimeout = setTimeout(this._activityTimeoutCB.bind(this), this.config.ACTIVITY_TIMEOUT);
  }

  _activityTimeoutCB() {
    this._activityTimeout = null;
    this.sculptureActionCreator.sendHandshakeAction(this.store.me, SculptureStore.HANDSHAKE_OFF);
  }

  _handleCommand(commandName, commandArgs) {

    if (this._activityTimeout) this._refreshActivityTimeout();

    if (commandName === SerialProtocol.HANDSHAKE_COMMAND) {
      // numUsers is really just an active flag (0 or 1), but has this name for historic reasons
      const {numUsers} = commandArgs;

      if (parseInt(numUsers) > 0) {
        this.sculptureActionCreator.sendHandshakeAction(this.store.me, SculptureStore.HANDSHAKE_ACTIVE);
      }
      else {
        this.sculptureActionCreator.sendHandshakeAction(this.store.me, SculptureStore.HANDSHAKE_PRESENT);
      }
      if (!this._activityTimeout) this._refreshActivityTimeout();
    }
  }
}
{'fraction_non_alphanumeric': 0.10526315789473684, 'fraction_numerical': 0.0014642479459855203, 'mean_word_length': 3.7762237762237763, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3604403', 'n_tokens_mistral': 3988, 'n_tokens_neox': 3842, 'n_words': 618}