content: /*
   *  Copyright (c) 2021 Freya Arbjerg and contributors
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the "Software"), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in all
   *  copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   *  SOFTWARE.
   *
   */
  
  package lavalink.server.player;
  
  import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
  import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter;
  import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
  import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  public class AudioLossCounter extends AudioEventAdapter {
  
      public static final int EXPECTED_PACKET_COUNT_PER_MIN = (60 * 1000) / 20; // 20ms packets
      private static final Logger log = LoggerFactory.getLogger(AudioLossCounter.class);
      private static final int ACCEPTABLE_TRACK_SWITCH_TIME = 100; //ms
  
      private long curMinute = 0;
      private int curLoss = 0;
      private int curSucc = 0;
  
      private int lastLoss = 0;
      private int lastSucc = 0;
  
      private long playingSince = Long.MAX_VALUE;
      private long lastTrackStarted = Long.MAX_VALUE / 2;
      private long lastTrackEnded = Long.MAX_VALUE;
  
      void onLoss() {
          checkTime();
          curLoss++;
      }
  
      void onSuccess() {
          checkTime();
          curSucc++;
      }
  
      public int getLastMinuteLoss() {
          return lastLoss;
      }
  
      public int getLastMinuteSuccess() {
          return lastSucc;
      }
  
      public boolean isDataUsable() {
          //log.info("\n" + lastTrackStarted + "\n" + lastTrackEnded + "\n" + playingSince);
  
          // Check that there isn't a significant gap in playback. If no track has ended yet, we can look past that
          if (lastTrackStarted - lastTrackEnded > ACCEPTABLE_TRACK_SWITCH_TIME
              && lastTrackEnded != Long.MAX_VALUE) {
              return false;
          }
  
          // Check that we have at least stats for last minute
          long lastMin = System.currentTimeMillis() / 60000 - 1;
          //log.info((playingSince < lastMin * 60000) + "");
          return playingSince < lastMin * 60000;
      }
  
      private void checkTime() {
          long actualMinute = System.currentTimeMillis() / 60000;
  
          if (curMinute != actualMinute) {
              lastLoss = curLoss;
              lastSucc = curSucc;
              curLoss = 0;
              curSucc = 0;
              curMinute = actualMinute;
          }
      }
  
      @Override
      public void onTrackEnd(AudioPlayer __, AudioTrack ___, AudioTrackEndReason ____) {
          lastTrackEnded = System.currentTimeMillis();
      }
  
      @Override
      public void onTrackStart(AudioPlayer __, AudioTrack ___) {
          lastTrackStarted = System.currentTimeMillis();
  
          if (lastTrackStarted - lastTrackEnded > ACCEPTABLE_TRACK_SWITCH_TIME
              || playingSince == Long.MAX_VALUE) {
              playingSince = System.currentTimeMillis();
              lastTrackEnded = Long.MAX_VALUE;
          }
      }
  
      @Override
      public void onPlayerPause(AudioPlayer player) {
          onTrackEnd(null, null, null);
      }
  
      @Override
      public void onPlayerResume(AudioPlayer player) {
          onTrackStart(null, null);
      }
  
      @Override
      public String toString() {
          return "AudioLossCounter{" +
              "lastLoss=" + lastLoss +
              ", lastSucc=" + lastSucc +
              ", total=" + (lastSucc + lastLoss) +
              '}';
      }
  }
  