text: //
  //
  //  Botinator
  //  assignmentClientManager.js
  //  Created by Milad Nazeri on 2019-03-28
  //  Copyright 2019 High Fidelity, Inc.
  //
  //  Distributed under the Apache License, Version 2.0.
  //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
  //
  //  Manages the differnt ac scripts needed
  //
  //
  
  
  (function() {
  
      // *************************************
      // START UTILITY FUNCTIONS
      // *************************************
      // #region UTILITY FUNCTIONS   
      
  
      // Use a ring to cycle through the list for as many unique recordings as are available
      function findValue(index, array, offset) {
          offset = offset || 0;
          return array[(index + offset) % array.length];
      }
      
  
      // Random Float helper for the location
      function randFloat(low, high) {
          return low + Math.random() * (high - low);
      }
  
  
      // Get a random location based on the user's desired min and max range.   
      function getRandomLocation(contentBoundaryCorners) {
          contentBoundaryCorners = fixupContentBoundaryCorners(contentBoundaryCorners);
          return {
              x: randFloat(contentBoundaryCorners[0].x, contentBoundaryCorners[1].x), 
              y: randFloat(contentBoundaryCorners[0].y, contentBoundaryCorners[1].y), 
              z: randFloat(contentBoundaryCorners[0].z, contentBoundaryCorners[1].z) 
          };
      }
  
  
      // Stop all the bots currently playing
      function stopAllBots() {
          availableAssignmentClientPlayers.forEach(function(ac) {
              ac.stop();
          });
          botCount = 0;
          isPlaying = false;
      }
  
  
      // Update all the current positions for the bots
      function updateAllBotsPosition() {
          availableAssignmentClientPlayers.forEach(function(ac) {
              var position = getRandomLocation(contentBoundaryCorners);
              ac.setPosition(position);
          });
      }
  
      
      // Update all the current volumes for the bots
      function updateAllBotsVolume() {
          availableAssignmentClientPlayers.forEach(function(ac) {
              ac.setVolume(volume);
          });
      }
  
  
      // Start playing sequence to fill players with bots
      var AC_AVAILABILITY_CHECK_MS = 1000;
      function startSequence() {
          // Check to see how many bots are needed
          if (botCount >= totalNumberOfBotsNeeded) {
              return;
          }
  
          if (botCount < availableAssignmentClientPlayers.length) {
              var player = availableAssignmentClientPlayers[botCount];
              player.play();
              botCount++;
  
              if (botCount >= totalNumberOfBotsNeeded) {
                  return;
              }
          }
  
          Script.setTimeout(function() {
              startSequence();
          }, AC_AVAILABILITY_CHECK_MS);
      }
  
  
      // The following borrowed from Zach's push preventer script
      // A utility function used to ensure that all of the values in "box corner 1" are less than
      // those in "box corner 2"
      function maybeSwapCorners(contentBoundaryCorners, dimension) {
          var temp;
          if (contentBoundaryCorners[0][dimension] > contentBoundaryCorners[1][dimension]) {
              temp = contentBoundaryCorners[0][dimension];
              contentBoundaryCorners[0][dimension] = contentBoundaryCorners[1][dimension];
              contentBoundaryCorners[1][dimension] = temp;
          }
          return [contentBoundaryCorners[0][dimension], contentBoundaryCorners[1][dimension]];
      }
  
  
      // Ensures that all of the values in "box corner 1" are less than those in "box corner 2".
      function fixupContentBoundaryCorners(contentBoundaryCorners) {
          var x = maybeSwapCorners(contentBoundaryCorners, "x");
          var y = maybeSwapCorners(contentBoundaryCorners, "y");
          var z = maybeSwapCorners(contentBoundaryCorners, "z");
          return [
              {x: x[0], y: y[0], z: z[0]},
              {x: x[1], y: y[1], z: z[1]}
          ];
      }
  
      // #endregion
      // *************************************
      // END UTILITY FUNCTIONS
      // *************************************
  
      // *************************************
      // START CONSTS_AND_VARS
      // *************************************
      // #region CONSTS_AND_VARS
  
  
      var X = 0;
      var Y = 1;
      var Z = 2;
  
      // List of possible bots to use    
      var BOTS = Script.require("./botsToLoad.js");
  
      // The Assignment Client channel
      var ASSIGNMENT_MANAGER_CHANNEL = "ASSIGNMENT_MANAGER_CHANNEL";
      var ASSIGNMENT_CLIENT_MESSANGER_CHANNEL = "ASSIGNMENT_CLIENT_MESSANGER_CHANNEL";
  
      // Array of the assignment client players and their assignment client player object
      var availableAssignmentClientPlayers = [];
  
      // Total number of bots needed
      var totalNumberOfBotsNeeded = 0;
  
      // Current playing bot count we are at
      var botCount = 0;
  
      // Current registered bount count
      var botRegisterdCount = 0;
  
      // Range to pull the random location from    
      var contentBoundaryCorners = [{x: 0, y: 0, z: 0}, {x: 0, y: 0, z: 0}];
  
      // Check for if currently running
      var isPlaying = false;
  
      // Volume the bots should play at
      var volume = 1.0;
  
  
      // #endregion
      // *************************************
      // END CONSTS_AND_VARS
      // *************************************
  
      // *************************************
      // START ASSIGNMENT_CLIENT_PLAYER
      // *************************************
      // #region ASSIGNMENT_CLIENT_PLAYER
  
  
      // Individual AssignmentClientPlayerObject
      function AssignmentClientPlayerObject(uuid, fileToPlay, position, volume) {
          this.uuid = uuid;
          this.fileToPlay = fileToPlay;
          this.position = position;
          this.volume = volume;
      }
  
  
      // Sets the position to play this bot at
      function setPosition(position) {
          this.position = position;
      }
  
  
      // Sets the volume to play this bot at
      function setVolume(volume) {
          this.volume = volume;
      }
  
  
      // Play the current clip
      function play() {
          Messages.sendMessage(ASSIGNMENT_MANAGER_CHANNEL, JSON.stringify({
              action: "PLAY",
              fileToPlay: this.fileToPlay,
              position: this.position,
              uuid: this.uuid,
              volume: this.volume
          }));
      }
  
  
      // Stop the current clip
      function stop() {
          Messages.sendMessage(ASSIGNMENT_MANAGER_CHANNEL, JSON.stringify({
              action: "STOP",
              uuid: this.uuid
          }));
      }
  
  
      AssignmentClientPlayerObject.prototype = {
          setPosition: setPosition,
          play: play,
          setVolume: setVolume,
          stop: stop
      };
  
  
      // #endregion
      // *************************************
      // END ASSIGNMENT_CLIENT_PLAYER
      // *************************************
      
      // *************************************
      // START MESSAGES
      // *************************************
      // #region MESSAGES
  
  
      // Handle Messages received
      function onMangerChannelMessageReceived(channel, message, sender) {
          if (channel !== ASSIGNMENT_MANAGER_CHANNEL || sender === Agent.sessionUUID) {
              return;
          }
  
          try {
              message = JSON.parse(message);
          } catch (error) {
              console.log("invalid object");
              console.log("MESSAGE:", message);
  
              return;
          }
  
          switch (message.action) {
              case "REGISTER_ME":
                  var fileName = findValue(botRegisterdCount, BOTS);
                  var position = getRandomLocation(contentBoundaryCorners);
                  availableAssignmentClientPlayers.push( 
                      new AssignmentClientPlayerObject(message.uuid, fileName, position, volume));
                  botRegisterdCount++;
                  var messageToSend = JSON.stringify({
                      action: "AC_AVAILABLE_UPDATE",
                      newAvailableACs: availableAssignmentClientPlayers.length
                  });
                  Messages.sendMessage(ASSIGNMENT_CLIENT_MESSANGER_CHANNEL, messageToSend);
                  break;
              case "ARE_YOU_THERE_MANAGER_ITS_ME_BOT":
                  Messages.sendMessage(ASSIGNMENT_MANAGER_CHANNEL, JSON.stringify({
                      action: "REGISTER_MANAGER",
                      uuid: sender
                  }));
                  break;
              default:
                  console.log("unrecongized action in assignmentClientManger.js");
                  break;
          }
      }
  
  
      function onTabletChannelMessageReceived(channel, message, sender) {
          try {
              message = JSON.parse(message);
          } catch (error) {
              console.log("invalid object");
              console.log(error);
  
              return;
          }
  
          if (channel !== ASSIGNMENT_CLIENT_MESSANGER_CHANNEL || sender === Agent.sessionUUID) {
              return;
          }
  
          switch (message.action) {
              case "SEND_DATA":
                  if (isPlaying) {
                      stopAllBots();
                      isPlaying = false;
                  }   
  
                  if (JSON.stringify(contentBoundaryCorners) !== 
                      JSON.stringify(message.contentBoundaryCorners)) {
                      contentBoundaryCorners = message.contentBoundaryCorners;
                      updateAllBotsPosition();
                  }
  
                  if (volume !== message.volume) {
                      volume = message.volume;
                      updateAllBotsVolume();
                  }
  
                  totalNumberOfBotsNeeded = message.totalNumberOfBotsNeeded;
                  break;
              case "PLAY":
                  if (isPlaying) {
                      return;
                  }
                  isPlaying = true;
                  startSequence();
                  break;
              case "STOP":
                  if (isPlaying) {
                      stopAllBots();
                  }
                  break;
              case "GET_MANAGER_STATUS":
                  var messageToSend = JSON.stringify({
                      action: "GET_MANAGER_STATUS",
                      newAvailableACs: availableAssignmentClientPlayers.length,
                      isPlaying: isPlaying
                  });
                  Messages.sendMessage(ASSIGNMENT_CLIENT_MESSANGER_CHANNEL, messageToSend);
                  break;
              default:
                  console.log("unrecongized action in assignmentClientManger.js");
                  break;
          }
      }
  
  
      // #endregion
      // *************************************
      // END MESSAGES
      // *************************************
  
      // *************************************
      // START MAIN
      // *************************************
      // #region MAIN
      
      
      // Startup for the manager when it comes online
      function startUp() {
          Messages.subscribe(ASSIGNMENT_MANAGER_CHANNEL);
          Messages.subscribe(ASSIGNMENT_CLIENT_MESSANGER_CHANNEL);
          Messages.messageReceived.connect(onMangerChannelMessageReceived);
          Messages.messageReceived.connect(onTabletChannelMessageReceived);
          Script.scriptEnding.connect(onEnding);
      }    
      
      startUp();
      
  
      // #endregion
      // *************************************
      // END MAIN
      // *************************************
  
      // *************************************
      // START CLEANUP
      // *************************************
      // #region CLEANUP
  
      
      // Cleanup the manager and it's messages
      function onEnding() {
          Messages.messageReceived.disconnect(onMangerChannelMessageReceived);
          Messages.messageReceived.disconnect(onTabletChannelMessageReceived);
          var messageToSend = JSON.stringify({
              action: "GET_MANAGER_STATUS",
              newAvailableACs: 0,
              isPlaying: false,
              closeTablet: true
          });
          Messages.sendMessage(ASSIGNMENT_CLIENT_MESSANGER_CHANNEL, messageToSend);
          Messages.unsubscribe(ASSIGNMENT_MANAGER_CHANNEL);
          Messages.unsubscribe(ASSIGNMENT_CLIENT_MESSANGER_CHANNEL);
      }
  
  
      // #endregion
      // *************************************
      // END CLEANUP
      // *************************************
  
  })();
  
  