content: /*
   This file is part of Airsonic.
  
   Airsonic is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
  
   Airsonic is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
  
   You should have received a copy of the GNU General Public License
   along with Airsonic.  If not, see <http://www.gnu.org/licenses/>.
  
   Copyright 2016 (C) Airsonic Authors
   Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
   */
  package org.airsonic.player.service;
  
  import org.airsonic.player.domain.MediaFile;
  import org.airsonic.player.domain.PlayStatus;
  import org.airsonic.player.domain.Player;
  import org.airsonic.player.domain.TransferStatus;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.stereotype.Service;
  
  import java.io.File;
  import java.util.*;
  
  /**
   * Provides services for maintaining the list of stream, download and upload statuses.
   * <p/>
   * Note that for stream statuses, the last inactive status is also stored.
   *
   * @author Sindre Mehus
   * @see TransferStatus
   */
  @Service
  public class StatusService {
  
      @Autowired
      private MediaFileService mediaFileService;
  
      private final List<TransferStatus> streamStatuses = new ArrayList<TransferStatus>();
      private final List<TransferStatus> downloadStatuses = new ArrayList<TransferStatus>();
      private final List<TransferStatus> uploadStatuses = new ArrayList<TransferStatus>();
      private final List<PlayStatus> remotePlays = new ArrayList<PlayStatus>();
  
      // Maps from player ID to latest inactive stream status.
      private final Map<Integer, TransferStatus> inactiveStreamStatuses = new LinkedHashMap<Integer, TransferStatus>();
  
      public synchronized TransferStatus createStreamStatus(Player player) {
          // Reuse existing status, if possible.
          TransferStatus status = inactiveStreamStatuses.get(player.getId());
          if (status != null) {
              status.setActive(true);
          } else {
              status = createStatus(player, streamStatuses);
          }
          return status;
      }
  
      public synchronized void removeStreamStatus(TransferStatus status) {
          // Move it to the map of inactive statuses.
          status.setActive(false);
          inactiveStreamStatuses.put(status.getPlayer().getId(), status);
          streamStatuses.remove(status);
      }
  
      public synchronized List<TransferStatus> getAllStreamStatuses() {
  
          List<TransferStatus> result = new ArrayList<TransferStatus>(streamStatuses);
  
          // Add inactive status for those players that have no active status.
          Set<Integer> activePlayers = new HashSet<Integer>();
          for (TransferStatus status : streamStatuses) {
              activePlayers.add(status.getPlayer().getId());
          }
  
          for (Map.Entry<Integer, TransferStatus> entry : inactiveStreamStatuses.entrySet()) {
              if (!activePlayers.contains(entry.getKey())) {
                  result.add(entry.getValue());
              }
          }
          return result;
      }
  
      public synchronized List<TransferStatus> getStreamStatusesForPlayer(Player player) {
          List<TransferStatus> result = new ArrayList<TransferStatus>();
          for (TransferStatus status : streamStatuses) {
              if (status.getPlayer().getId().equals(player.getId())) {
                  result.add(status);
              }
          }
  
          // If no active statuses exists, add the inactive one.
          if (result.isEmpty()) {
              TransferStatus inactiveStatus = inactiveStreamStatuses.get(player.getId());
              if (inactiveStatus != null) {
                  result.add(inactiveStatus);
              }
          }
  
          return result;
      }
  
      public synchronized TransferStatus createDownloadStatus(Player player) {
          return createStatus(player, downloadStatuses);
      }
  
      public synchronized void removeDownloadStatus(TransferStatus status) {
          downloadStatuses.remove(status);
      }
  
      public synchronized List<TransferStatus> getAllDownloadStatuses() {
          return new ArrayList<TransferStatus>(downloadStatuses);
      }
  
      public synchronized TransferStatus createUploadStatus(Player player) {
          return createStatus(player, uploadStatuses);
      }
  
      public synchronized void removeUploadStatus(TransferStatus status) {
          uploadStatuses.remove(status);
      }
  
      public synchronized List<TransferStatus> getAllUploadStatuses() {
          return new ArrayList<TransferStatus>(uploadStatuses);
      }
  
      public synchronized void addRemotePlay(PlayStatus playStatus) {
          remotePlays.removeIf(PlayStatus::isExpired);
          remotePlays.add(playStatus);
      }
  
      public synchronized List<PlayStatus> getPlayStatuses() {
          Map<Integer, PlayStatus> result = new LinkedHashMap<Integer, PlayStatus>();
          for (PlayStatus remotePlay : remotePlays) {
              if (!remotePlay.isExpired()) {
                  result.put(remotePlay.getPlayer().getId(), remotePlay);
              }
          }
  
          List<TransferStatus> statuses = new ArrayList<TransferStatus>();
          statuses.addAll(inactiveStreamStatuses.values());
          statuses.addAll(streamStatuses);
  
          for (TransferStatus streamStatus : statuses) {
              Player player = streamStatus.getPlayer();
              File file = streamStatus.getFile();
              if (file == null) {
                  continue;
              }
              MediaFile mediaFile = mediaFileService.getMediaFile(file);
              if (player == null || mediaFile == null) {
                  continue;
              }
              Date time = new Date(System.currentTimeMillis() - streamStatus.getMillisSinceLastUpdate());
              result.put(player.getId(), new PlayStatus(mediaFile, player, time));
          }
          return new ArrayList<PlayStatus>(result.values());
      }
  
      private synchronized TransferStatus createStatus(Player player, List<TransferStatus> statusList) {
          TransferStatus status = new TransferStatus();
          status.setPlayer(player);
          statusList.add(status);
          return status;
      }
  
      public void setMediaFileService(MediaFileService mediaFileService) {
          this.mediaFileService = mediaFileService;
      }
  }
  