package com.turn.ttorrent.client;

import com.turn.ttorrent.common.AnnounceableInformation;
import com.turn.ttorrent.common.Pair;

import java.util.*;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class TorrentsStorage {

  private final ReadWriteLock readWriteLock;
  private final Map<String, SharedTorrent> activeTorrents;
  private final Map<String, LoadedTorrent> loadedTorrents;

  public TorrentsStorage() {
    readWriteLock = new ReentrantReadWriteLock();
    activeTorrents = new HashMap<String, SharedTorrent>();
    loadedTorrents = new HashMap<String, LoadedTorrent>();
  }

  public boolean hasTorrent(String hash) {
    try {
      readWriteLock.readLock().lock();
      return loadedTorrents.containsKey(hash);
    } finally {
      readWriteLock.readLock().unlock();
    }
  }

  public LoadedTorrent getLoadedTorrent(String hash) {
    try {
      readWriteLock.readLock().lock();
      return loadedTorrents.get(hash);
    } finally {
      readWriteLock.readLock().unlock();
    }
  }

  public void peerDisconnected(String torrentHash) {
    final SharedTorrent torrent;
    try {
      readWriteLock.writeLock().lock();
      torrent = activeTorrents.get(torrentHash);
      if (torrent == null) return;

      boolean isTorrentFinished = torrent.isFinished();
      if (torrent.getDownloadersCount() == 0 && isTorrentFinished) {
        activeTorrents.remove(torrentHash);
      } else {
        return;
      }
    } finally {
      readWriteLock.writeLock().unlock();
    }
    torrent.close();
  }

  public SharedTorrent getTorrent(String hash) {
    try {
      readWriteLock.readLock().lock();
      return activeTorrents.get(hash);
    } finally {
      readWriteLock.readLock().unlock();
    }
  }

  public void addTorrent(String hash, LoadedTorrent torrent) {
    try {
      readWriteLock.writeLock().lock();
      loadedTorrents.put(hash, torrent);
    } finally {
      readWriteLock.writeLock().unlock();
    }
  }

  public SharedTorrent putIfAbsentActiveTorrent(String hash, SharedTorrent torrent) {
    try {
      readWriteLock.writeLock().lock();
      final SharedTorrent old = activeTorrents.get(hash);
      if (old != null) return old;

      return activeTorrents.put(hash, torrent);
    } finally {
      readWriteLock.writeLock().unlock();
    }
  }

  public Pair<SharedTorrent, LoadedTorrent> remove(String hash) {
    final Pair<SharedTorrent, LoadedTorrent> result;
    try {
      readWriteLock.writeLock().lock();
      final SharedTorrent sharedTorrent = activeTorrents.remove(hash);
      final LoadedTorrent loadedTorrent = loadedTorrents.remove(hash);
      result = new Pair<SharedTorrent, LoadedTorrent>(sharedTorrent, loadedTorrent);
    } finally {
      readWriteLock.writeLock().unlock();
    }
    if (result.first() != null) {
      result.first().close();
    }
    return result;
  }

  public List<SharedTorrent> activeTorrents() {
    try {
      readWriteLock.readLock().lock();
      return new ArrayList<SharedTorrent>(activeTorrents.values());
    } finally {
      readWriteLock.readLock().unlock();
    }
  }

  public List<AnnounceableInformation> announceableTorrents() {
    List<AnnounceableInformation> result = new ArrayList<AnnounceableInformation>();
    try {
      readWriteLock.readLock().lock();
      for (LoadedTorrent loadedTorrent : loadedTorrents.values()) {
        result.add(loadedTorrent.createAnnounceableInformation());
      }
      return result;
    } finally {
      readWriteLock.readLock().unlock();
    }
  }

  public List<LoadedTorrent> getLoadedTorrents() {
    try {
      readWriteLock.readLock().lock();
      return new ArrayList<LoadedTorrent>(loadedTorrents.values());
    } finally {
      readWriteLock.readLock().unlock();
    }
  }

  public void clear() {
    final Collection<SharedTorrent> sharedTorrents;
    try {
      readWriteLock.writeLock().lock();
      sharedTorrents = new ArrayList<SharedTorrent>(activeTorrents.values());
      loadedTorrents.clear();
      activeTorrents.clear();
    } finally {
      readWriteLock.writeLock().unlock();
    }
    for (SharedTorrent sharedTorrent : sharedTorrents) {
      sharedTorrent.close();
    }
  }
}

@@ -122,16 +122,54 @@
 on.Pair;
+%0Aimport org.apache.commons.io.IOUtils;
 %0A%0Aimport
@@ -2856,24 +2856,128 @@
 ck();%0A    %7D%0A
+    if (result.second() != null) %7B%0A      IOUtils.closeQuietly(result.second().getPieceStorage());%0A    %7D%0A
     if (resu
@@ -4033,16 +4033,68 @@
 rrents;%0A
+    final Collection%3CLoadedTorrent%3E loadedTorrents;%0A
     try 
@@ -4087,32 +4087,32 @@
 ents;%0A    try %7B%0A
-
       readWriteL
@@ -4225,32 +4225,120 @@
   loadedTorrents
+ = new ArrayList%3CLoadedTorrent%3E(this.loadedTorrents.values());%0A      this.loadedTorrents
 .clear();%0A      
@@ -4478,24 +4478,24 @@
 Torrents) %7B%0A
-
       shared
@@ -4513,18 +4513,142 @@
 se();%0A    %7D%0A
+    for (LoadedTorrent loadedTorrent : loadedTorrents) %7B%0A      IOUtils.closeQuietly(loadedTorrent.getPieceStorage());%0A    %7D%0A
   %7D%0A%7D%0A

fix possible removing torrents from storage without closing resources