content: /*
   * Copyright (c) 2018-2025, Alden Torres
   *
   * Licensed under the terms of the MIT license.
   * Copy of the license at https://opensource.org/licenses/MIT
   */
  
  package org.libtorrent4j;
  
  import org.libtorrent4j.alerts.CacheFlushedAlert;
  import org.libtorrent4j.alerts.FileErrorAlert;
  import org.libtorrent4j.alerts.FileRenameFailedAlert;
  import org.libtorrent4j.alerts.FileRenamedAlert;
  import org.libtorrent4j.alerts.HashFailedAlert;
  import org.libtorrent4j.alerts.PieceFinishedAlert;
  import org.libtorrent4j.alerts.ReadPieceAlert;
  import org.libtorrent4j.alerts.StorageMovedAlert;
  import org.libtorrent4j.alerts.StorageMovedFailedAlert;
  import org.libtorrent4j.alerts.TorrentNeedCertAlert;
  import org.libtorrent4j.swig.add_piece_flags_t;
  import org.libtorrent4j.swig.announce_entry_vector;
  import org.libtorrent4j.swig.byte_vector;
  import org.libtorrent4j.swig.deadline_flags_t;
  import org.libtorrent4j.swig.file_progress_flags_t;
  import org.libtorrent4j.swig.int64_vector;
  import org.libtorrent4j.swig.int_vector;
  import org.libtorrent4j.swig.libtorrent;
  import org.libtorrent4j.swig.partial_piece_info_vector;
  import org.libtorrent4j.swig.peer_info_vector;
  import org.libtorrent4j.swig.reannounce_flags_t;
  import org.libtorrent4j.swig.resume_data_flags_t;
  import org.libtorrent4j.swig.status_flags_t;
  import org.libtorrent4j.swig.torrent_flags_t;
  import org.libtorrent4j.swig.torrent_handle;
  import org.libtorrent4j.swig.torrent_info;
  import org.libtorrent4j.swig.torrent_status;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  
  /**
   * You will usually have to store your torrent handles somewhere, since it's
   * the object through which you retrieve information about the torrent and
   * aborts the torrent.
   * <p>
   * .. warning::
   * Any member function that returns a value or fills in a value has to be
   * made synchronously. This means it has to wait for the main thread to
   * complete the query before it can return. This might potentially be
   * expensive if done from within a GUI thread that needs to stay
   * responsive. Try to avoid querying for information you don't need, and
   * try to do it in as few calls as possible. You can get most of the
   * interesting information about a torrent from the
   * torrent_handle::status() call.
   * <p>
   * The default constructor will initialize the handle to an invalid state.
   * Which means you cannot perform any operation on it, unless you first
   * assign it a valid handle. If you try to perform any operation on an
   * uninitialized handle, it will throw ``invalid_handle``.
   * <p>
   * .. warning::
   * All operations on a torrent_handle may throw libtorrent_exception
   * exception, in case the handle is no longer referring to a torrent.
   * There is one exception is_valid() will never throw. Since the torrents
   * are processed by a background thread, there is no guarantee that a
   * handle will remain valid between two calls.
   *
   * @author gubatron
   * @author aldenml
   */
  public final class TorrentHandle
      extends SwigObject<torrent_handle> {
  
      private static final long REQUEST_STATUS_RESOLUTION_MILLIS = 500;
      // cache this zero flag for performance reasons
      private static final status_flags_t STATUS_FLAGS_ZERO = new status_flags_t();
  
      private long lastStatusRequestTime;
      private TorrentStatus lastStatus;
  
      /**
       * @param th the native object
       */
      public TorrentHandle(torrent_handle th) {
          super(th);
      }
  
      /**
       * Instruct libtorrent to overwrite any data that may already have been
       * downloaded with the data of the new piece being added.
       */
      public static final add_piece_flags_t OVERWRITE_EXISTING = torrent_handle.overwrite_existing;
  
      /**
       * This function will write {@code data} to the storage as piece {@code piece},
       * as if it had been downloaded from a peer. {@code data} is expected to
       * point to a buffer of as many bytes as the size of the specified piece.
       * The data in the buffer is copied and passed on to the disk IO thread
       * to be written at a later point.
       * <p>
       * By default, data that's already been downloaded is not overwritten by
       * this buffer. If you trust this data to be correct (and pass the piece
       * hash check) you may pass the overwrite_existing flag. This will
       * instruct libtorrent to overwrite any data that may already have been
       * downloaded with this data.
       * <p>
       * Since the data is written asynchronously, you may know that is passed
       * or failed the hash check by waiting for
       * {@link PieceFinishedAlert} or
       * {@link HashFailedAlert}.
       *
       * @param piece the piece index
       * @param data  the piece data
       * @param flags flags
       */
      public void addPiece(int piece, byte[] data, add_piece_flags_t flags) {
          h.add_piece_bytes(piece, Vectors.bytes2byte_vector(data), flags);
      }
  
      /**
       * Same as calling {@link #addPiece(int, byte[], add_piece_flags_t)} with
       * {@code flags} with value 0.
       *
       * @param piece the piece index
       * @param data  the piece data
       */
      public void addPiece(int piece, byte[] data) {
          h.add_piece_bytes(piece, Vectors.bytes2byte_vector(data));
      }
  
      /**
       * This function starts an asynchronous read operation of the specified
       * piece from this torrent. You must have completed the download of the
       * specified piece before calling this function.
       * <p>
       * When the read operation is completed, it is passed back through an
       * alert, {@link ReadPieceAlert}.
       * Since this alert is a response to an explicit
       * call, it will always be posted, regardless of the alert mask.
       * <p>
       * Note that if you read multiple pieces, the read operations are not
       * guaranteed to finish in the same order as you initiated them.
       *
       * @param piece the piece index
       */
      public void readPiece(int piece) {
          h.read_piece(piece);
      }
  
      /**
       * Sets the first and last piece of the range for the piece picker to start
       * downloading in sequential mode.
       *
       * If the torrent metadata has not been downloaded yet
       * then the functions do nothing.
       *
       * @param firstPiece piece index for the rage
       */
      public void setSequentialRange(int firstPiece, int lastPiece) {
          h.set_sequential_range(firstPiece, lastPiece);
      }
  
      /**
       * Sets the first piece of the range for the piece picker to start
       * downloading in sequential mode.
       * <p>
       * If the torrent metadata has not been downloaded yet
       * then the functions do nothing.
       *
       * @param firstPiece piece index for the rage
       */
      public void setSequentialRange(int firstPiece) {
          h.set_sequential_range(firstPiece);
      }
  
      /**
       * Returns true if this piece has been completely downloaded, and false
       * otherwise.
       *
       * @param piece the piece index
       * @return if piece has been completely downloaded
       */
      public boolean havePiece(int piece) {
          return h.have_piece(piece);
      }
  
      /**
       * Returns a list filled with one entry for each peer connected to this
       * torrent, given the handle is valid. If the handle is invalid, it will
       * return an empty list.
       * <p>
       * Each entry in the vector contains information about that particular peer.
       *
       * @return the list with the peers information
       * @see PeerInfo
       */
      public List<PeerInfo> peerInfo() {
          if (!h.is_valid()) {
              return new ArrayList<>();
          }
  
          peer_info_vector v = new peer_info_vector();
          h.get_peer_info(v);
  
          int size = v.size();
          ArrayList<PeerInfo> l = new ArrayList<>(size);
          for (int i = 0; i < size; i++) {
              l.add(new PeerInfo(v.get(i)));
          }
  
          return l;
      }
  
      /**
       * Returns a pointer to the torrent_info object associated with this
       * torrent. The {@link TorrentInfo} object may be a copy of the internal
       * object. If the torrent doesn't have metadata, the pointer will not be
       * initialized (i.e. a null). The torrent may be in a state without
       * metadata only if it was started without a .torrent file, e.g. by being
       * added by magnet link.
       * <p>
       * Note that the {@link TorrentInfo} object returned here may be a different
       * instance than the one added to the session, with different attributes
       * like piece layers, dht nodes and trackers. A {@link TorrentInfo} object does
       * not round-trip cleanly when added to a session.
       * <p>
       * If you want to save a .torrent file from the torrent_handle, instead
       * call save_resume_data() and write_torrent_file() the
       * add_torrent_params object passed back in the alert.
       * <p>
       * Note that a torrent added from a magnet link may not have the full
       * merkle trees for all files, and hence not have the complete piece
       * layers. In that state, you cannot create a .torrent file from the
       * torrent_info returned. Once the torrent completes downloading all
       * files, becoming a seed, you can make a .torrent file from it.
       *
       * @return the internal torrent info
       */
      public TorrentInfo torrentFile() {
          if (!h.is_valid()) {
              return null;
          }
          torrent_info ti = h.torrent_file_ptr();
          return ti != null ? new TorrentInfo(ti) : null;
      }
  
      /**
       * `status()`` will return a structure with information about the status
       * of this torrent. If the torrent_handle is invalid, it will throw
       * libtorrent_exception exception. See torrent_status. The ``flags``
       * argument filters what information is returned in the torrent_status.
       * Some information in there is relatively expensive to calculate, and if
       * you're not interested in it (and see performance issues), you can
       * filter them out.
       * <p>
       * By default everything is included. The flags you can use to decide
       * what to *include* are defined in the status_flags_t enum.
       * <p>
       * It is important not to call this method for each field in the status
       * for performance reasons.
       *
       * @return the status
       */
      public TorrentStatus status(boolean force) {
          long now = System.currentTimeMillis();
          if (force || (now - lastStatusRequestTime) >= REQUEST_STATUS_RESOLUTION_MILLIS) {
              lastStatusRequestTime = now;
              lastStatus = new TorrentStatus(h.status(STATUS_FLAGS_ZERO));
          }
  
          return lastStatus;
      }
  
      /**
       * Returns a structure with information about the status
       * of this torrent. If the handle is invalid, it will throw
       * libtorrent_exception exception. See torrent_status. The ``flags``
       * argument filters what information is returned in the torrent_status.
       * Some information in there is relatively expensive to calculate, and if
       * you're not interested in it (and see performance issues), you can
       * filter them out.
       *
       * @return the status
       */
      public TorrentStatus status() {
          return status(false);
      }
  
      /**
       * calculates ``distributed_copies``, ``distributed_full_copies`` and
       * ``distributed_fraction``.
       */
      public static final status_flags_t QUERY_DISTRIBUTED_COPIES = torrent_handle.query_distributed_copies;
  
      /**
       * includes partial downloaded blocks in ``total_done`` and
       * ``total_wanted_done``.
       */
      public static final status_flags_t QUERY_ACCURATE_DOWNLOAD_COUNTERS = torrent_handle.query_accurate_download_counters;
  
      /**
       * includes ``last_seen_complete``.
       */
      public static final status_flags_t QUERY_LAST_SEEN_COMPLETE = torrent_handle.query_last_seen_complete;
  
      /**
       * includes ``pieces``.
       */
      public static final status_flags_t QUERY_PIECES = torrent_handle.query_pieces;
  
      /**
       * includes ``verified_pieces`` (only applies to torrents in *seed mode*).
       */
      public static final status_flags_t QUERY_VERIFIED_PIECES = torrent_handle.query_verified_pieces;
  
      /**
       * includes ``torrent_file``, which is all the static information from the .torrent file.
       */
      public static final status_flags_t QUERY_TORRENT_FILE = torrent_handle.query_torrent_file;
  
      /**
       * includes {@code name}, the name of the torrent. This is either derived
       * from the .torrent file, or from the {@code &dn=} magnet link argument
       * or possibly some other source. If the name of the torrent is not
       * known, this is an empty string.
       */
      public static final status_flags_t QUERY_NAME = torrent_handle.query_name;
  
      /**
       * includes ``save_path``, the path to the directory the files of the
       * torrent are saved to.
       */
      public static final status_flags_t QUERY_SAVE_PATH = torrent_handle.query_save_path;
  
      /**
       * This method returns an up to date torrent status, the {@code flags} parameters
       * is an or-combination of the {@link status_flags_t} native values, in case you want
       * advanced (and expensive) fields filled. We recommend the use of the simple call
       * to {@link #status()} that internally keep a cache with a small time resolution.
       *
       * @param flags the flags
       * @return the status
       */
      public TorrentStatus status(status_flags_t flags) {
          return new TorrentStatus(h.status(flags));
      }
  
      /**
       * Returns an array (list) with information about pieces that are partially
       * downloaded or not downloaded at all but partially requested. See
       * {@link PartialPieceInfo} for the fields in the returned vector.
       *
       * @return a list with partial piece info
       */
      public ArrayList<PartialPieceInfo> getDownloadQueue() {
          partial_piece_info_vector v = new partial_piece_info_vector();
          h.get_download_queue(v);
          int size = (int) v.size();
          ArrayList<PartialPieceInfo> l = new ArrayList<>(size);
  
          for (int i = 0; i < size; i++) {
              l.add(new PartialPieceInfo(v.get(i)));
          }
  
          return l;
      }
  
      /**
       * Returns the info-hash for the torrent.
       * <p>
       * If this handle is to a torrent that hasn't loaded yet (for instance by being added)
       * by a URL, the returned value is undefined.
       *
       * @return the torrent info hash
       */
      public Sha1Hash infoHash() {
          return new Sha1Hash(h.info_hash());
      }
  
      /**
       * This method will disconnect all peers.
       * <p>
       * When a torrent is paused, it will however
       * remember all share ratios to all peers and remember all potential (not
       * connected) peers. Torrents may be paused automatically if there is a
       * file error (e.g. disk full) or something similar. See
       * {@link FileErrorAlert}.
       * <p>
       * To know if a torrent is paused or not, call
       * {@link #status()} and inspect TorrentStatus#isPaused() .
       * <p>
       * The ``flags`` argument to pause can be set to
       * ``torrent_handle::graceful_pause`` which will delay the disconnect of
       * peers that we're still downloading outstanding requests from. The
       * torrent will not accept any more requests and will disconnect all idle
       * peers. As soon as a peer is done transferring the blocks that were
       * requested from it, it is disconnected. This is a graceful shut down of
       * the torrent in the sense that no downloaded bytes are wasted.
       * <p>
       * torrents that are auto-managed may be automatically resumed again. It
       * does not make sense to pause an auto-managed torrent without making it
       * not automanaged first.
       */
      public void pause() {
          h.pause();
      }
  
      /**
       * Will reconnect all peers.
       * <p>
       * Torrents that are auto-managed may be automatically resumed again.
       */
      public void resume() {
          h.resume();
      }
  
      public torrent_flags_t getFlags() {
          return h.flags();
      }
  
      public void setFlags(torrent_flags_t flags, torrent_flags_t mask) {
          h.set_flags(flags, mask);
      }
  
      public void setFlags(torrent_flags_t flags) {
          h.set_flags(flags);
      }
  
      public void unsetFlags(torrent_flags_t flags) {
          h.unset_flags(flags);
      }
  
      /**
       * Instructs libtorrent to flush all the disk caches for this torrent and
       * close all file handles. This is done asynchronously and you will be
       * notified that it's complete through {@link CacheFlushedAlert}.
       * <p>
       * Note that by the time you get the alert, libtorrent may have cached
       * more data for the torrent, but you are guaranteed that whatever cached
       * data libtorrent had by the time you called
       * {@code flushCache()} has been written to disk.
       */
      public void flushCache() {
          h.flush_cache();
      }
  
      /**
       * Returns true if any whole chunk has been downloaded
       * since the torrent was first loaded or since the last time the resume
       * data was saved. When saving resume data periodically, it makes sense
       * to skip any torrent which hasn't downloaded anything since the last
       * time.
       * <p>
       * A torrent's resume data is considered saved as soon as the alert is
       * posted. It is important to make sure this alert is received and
       * handled in order for this function to be meaningful.
       *
       * @return true if data has been downloaded since the last time the resume
       * data was saved.
       */
      public boolean needSaveResumeData() {
          return h.need_save_resume_data();
      }
  
      /**
       * Every torrent that is added is assigned a queue position exactly one
       * greater than the greatest queue position of all existing torrents.
       * Torrents that are being seeded have -1 as their queue position, since
       * they're no longer in line to be downloaded.
       * <p>
       * When a torrent is removed or turns into a seed, all torrents with
       * greater queue positions have their positions decreased to fill in the
       * space in the sequence.
       * <p>
       * This function returns the torrent's position in the download
       * queue. The torrents with the smallest numbers are the ones that are
       * being downloaded. The smaller number, the closer the torrent is to the
       * front of the line to be started.
       * <p>
       * The queue position is also available in the torrent_status.
       *
       * @return the queue position
       */
      public int queuePosition() {
          return h.queue_position_ex();
      }
  
      /**
       * The {@code queue_position_*()} functions adjust the torrents position in
       * the queue. Up means closer to the front and down means closer to the
       * back of the queue. Top and bottom refers to the front and the back of
       * the queue respectively.
       */
      public void queuePositionUp() {
          h.queue_position_up();
      }
  
      /**
       * The {@code queue_position_*()} functions adjust the torrents position in
       * the queue. Up means closer to the front and down means closer to the
       * back of the queue. Top and bottom refers to the front and the back of
       * the queue respectively.
       */
      public void queuePositionDown() {
          h.queue_position_down();
      }
  
      /**
       * The {@code queue_position_*()} functions adjust the torrents position in
       * the queue. Up means closer to the front and down means closer to the
       * back of the queue. Top and bottom refers to the front and the back of
       * the queue respectively.
       */
      public void queuePositionTop() {
          h.queue_position_top();
      }
  
      /**
       * The {@code queue_position_*()} functions adjust the torrents position in
       * the queue. Up means closer to the front and down means closer to the
       * back of the queue. Top and bottom refers to the front and the back of
       * the queue respectively.
       */
      public void queuePositionBottom() {
          h.queue_position_bottom();
      }
  
      /**
       * Updates the position in the queue for this torrent. The relative order
       * of all other torrents remain intact but their numerical queue position
       * shifts to make space for this torrent's new position
       *
       * @param position the new position
       */
      public void queuePositionSet(int position) {
          h.queue_position_set_ex(position);
      }
  
      /**
       * For SSL torrents, use this to specify a path to a .pem file to use as
       * this client's certificate. The certificate must be signed by the
       * certificate in the .torrent file to be valid.
       * <p>
       * Note that when a torrent first starts up, and it needs a certificate,
       * it will suspend connecting to any peers until it has one. It's
       * typically desirable to resume the torrent after setting the SSL
       * certificate.
       * <p>
       * If you receive a {@link TorrentNeedCertAlert},
       * you need to call this to provide a valid cert. If you don't have a cert
       * you won't be allowed to connect to any peers.
       *
       * @param certificate is a path to the (signed) certificate in .pem format
       *                    corresponding to this torrent.
       * @param privateKey  is a path to the private key for the specified
       *                    certificate. This must be in .pem format.
       * @param dhParams    is a path to the Diffie-Hellman parameter file, which
       *                    needs to be in .pem format. You can generate this file using the
       *                    openssl command like this: ``openssl dhparam -outform PEM -out
       *                    dhparams.pem 512``.
       */
      public void setSslCertificate(String certificate, String privateKey,
                                    String dhParams) {
          h.set_ssl_certificate(certificate, privateKey, dhParams);
      }
  
      /**
       * For SSL torrents, use this to specify a path to a .pem file to use as
       * this client's certificate. The certificate must be signed by the
       * certificate in the .torrent file to be valid.
       * <p>
       * Note that when a torrent first starts up, and it needs a certificate,
       * it will suspend connecting to any peers until it has one. It's
       * typically desirable to resume the torrent after setting the SSL
       * certificate.
       * <p>
       * If you receive a {@link TorrentNeedCertAlert},
       * you need to call this to provide a valid cert. If you don't have a cert
       * you won't be allowed to connect to any peers.
       *
       * @param certificate is a path to the (signed) certificate in .pem format
       *                    corresponding to this torrent.
       * @param privateKey  is a path to the private key for the specified
       *                    certificate. This must be in .pem format.
       * @param dhParams    is a path to the Diffie-Hellman parameter file, which
       *                    needs to be in .pem format. You can generate this file using the
       *                    openssl command like this: ``openssl dhparam -outform PEM -out
       *                    dhparams.pem 512``.
       * @param passphrase  may be specified if the private key is encrypted and
       *                    requires a passphrase to be decrypted.
       */
      public void setSslCertificate(String certificate, String privateKey,
                                    String dhParams, String passphrase) {
          h.set_ssl_certificate(certificate, privateKey, dhParams, passphrase);
      }
  
      /**
       * This method is like {@link #setSslCertificate} but takes the actual
       * certificate, private key and DH params as byte arrays, rather than paths
       * to files.
       *
       * @param certificate buffer of the (signed) certificate in .pem format
       *                    corresponding to this torrent.
       * @param privateKey  buffer of the private key for the specified
       *                    certificate. This must be in .pem format.
       * @param dhParams    buffer of the Diffie-Hellman parameter file, which
       *                    needs to be in .pem format.
       */
      void setSslCertificateBuffer(byte[] certificate, byte[] privateKey,
                                   byte[] dhParams) {
          byte_vector cert = Vectors.bytes2byte_vector(certificate);
          byte_vector pk = Vectors.bytes2byte_vector(privateKey);
          byte_vector dh = Vectors.bytes2byte_vector(dhParams);
          h.set_ssl_certificate_buffer_ex(cert, pk, dh);
      }
  
      /**
       * The disk cache will be flushed before creating the resume data.
       * This avoids a problem with file timestamps in the resume data in
       * case the cache hasn't been flushed yet.
       */
      public static final resume_data_flags_t FLUSH_DISK_CACHE = torrent_handle.flush_disk_cache;
  
      /**
       * The resume data will contain the metadata from the torrent file as
       * well. This is default for any torrent that's added without a
       * torrent file (such as a magnet link or a URL).
       */
      public static final resume_data_flags_t SAVE_INFO_DICT = torrent_handle.save_info_dict;
  
      /**
       * If nothing significant has changed in the torrent since the last
       * time resume data was saved, fail this attempt. Significant changes
       * primarily include more data having been downloaded, file or piece
       * priorities having changed etc. If the resume data doesn't need
       * saving, a save_resume_data_failed_alert is posted with the error
       * resume_data_not_modified.
       */
      public static final resume_data_flags_t ONLY_IF_MODIFIED = torrent_handle.only_if_modified;
  
      /**
       * ``save_resume_data()`` generates fast-resume data and returns it as an
       * entry. This entry is suitable for being bencoded. For more information
       * about how fast-resume works, see fast-resume_.
       * <p>
       * The ``flags`` argument is a bitmask of flags ORed together. see
       * save_resume_flags_t
       * <p>
       * This operation is asynchronous, ``save_resume_data`` will return
       * immediately. The resume data is delivered when it's done through an
       * save_resume_data_alert.
       * <p>
       * The fast resume data will be empty in the following cases:
       * <p>
       * 1. The torrent handle is invalid.
       * 2. The torrent is checking (or is queued for checking) its storage, it
       * will obviously not be ready to write resume data.
       * 3. The torrent hasn't received valid metadata and was started without
       * metadata (see libtorrent's metadata-from-peers_ extension)
       * <p>
       * Note that by the time you receive the fast resume data, it may already
       * be invalid if the torrent is still downloading! The recommended
       * practice is to first pause the session, then generate the fast resume
       * data, and then close it down. Make sure to not remove_torrent() before
       * you receive the save_resume_data_alert though. There's no need to
       * pause when saving intermittent resume data.
       * <p>
       * .. warning::
       * If you pause every torrent individually instead of pausing the
       * session, every torrent will have its paused state saved in the
       * resume data!
       * <p>
       * .. warning::
       * The resume data contains the modification timestamps for all files.
       * If one file has been modified when the torrent is added again, the
       * will be rechecked. When shutting down, make sure to flush the disk
       * cache before saving the resume data. This will make sure that the
       * file timestamps are up to date and won't be modified after saving
       * the resume data. The recommended way to do this is to pause the
       * torrent, which will flush the cache and disconnect all peers.
       * <p>
       * .. note::
       * It is typically a good idea to save resume data whenever a torrent
       * is completed or paused. In those cases you don't need to pause the
       * torrent or the session, since the torrent will do no more writing to
       * its files. If you save resume data for torrents when they are
       * paused, you can accelerate the shutdown process by not saving resume
       * data again for paused torrents. Completed torrents should have their
       * resume data saved when they complete and on exit, since their
       * statistics might be updated.
       * <p>
       * In full allocation mode the reume data is never invalidated by
       * subsequent writes to the files, since pieces won't move around. This
       * means that you don't need to pause before writing resume data in full
       * or sparse mode. If you don't, however, any data written to disk after
       * you saved resume data and before the session closed is lost.
       * <p>
       * It also means that if the resume data is out dated, libtorrent will
       * not re-check the files, but assume that it is fairly recent. The
       * assumption is that it's better to loose a little bit than to re-check
       * the entire file.
       * <p>
       * It is still a good idea to save resume data periodically during
       * download as well as when closing down.
       * <p>
       * Example code to pause and save resume data for all torrents and wait
       * for the alerts:
       * <p>
       * .. code:: c++
       * <pre>
       * {@code
       * extern int outstanding_resume_data; // global counter of outstanding resume data
       * std::vector<torrent_handle> handles = ses.get_torrents();
       * ses.pause();
       * for (std::vector<torrent_handle>::iterator i = handles.begin();
       * i != handles.end(); ++i)
       * {
       * torrent_handle& h = *i;
       * if (!h.is_valid()) continue;
       * torrent_status s = h.status();
       * if (!s.has_metadata) continue;
       * if (!s.need_save_resume_data()) continue;
       *
       * h.save_resume_data();
       * ++outstanding_resume_data;
       * }
       *
       * while (outstanding_resume_data > 0)
       * {
       * alert const* a = ses.wait_for_alert(seconds(10));
       *
       * // if we don't get an alert within 10 seconds, abort
       * if (a == 0) break;
       *
       * std::auto_ptr<alert> holder = ses.pop_alert();
       *
       * if (alert_cast<save_resume_data_failed_alert>(a))
       * {
       * process_alert(a);
       * --outstanding_resume_data;
       * continue;
       * }
       *
       * save_resume_data_alert const* rd = alert_cast<save_resume_data_alert>(a);
       * if (rd == 0)
       * {
       * process_alert(a);
       * continue;
       * }
       *
       * torrent_handle h = rd->handle;
       * torrent_status st = h.status(torrent_handle::query_save_path | torrent_handle::query_name);
       * std::ofstream out((st.save_path
       * + "/" + st.name + ".fastresume").c_str()
       * , std::ios_base::binary);
       * out.unsetf(std::ios_base::skipws);
       * bencode(std::ostream_iterator<char>(out), *rd->resume_data);
       * --outstanding_resume_data;
       * }
       * }
       * </pre>
       * .. note::
       * Note how ``outstanding_resume_data`` is a global counter in this
       * example. This is deliberate, otherwise there is a race condition for
       * torrents that was just asked to save their resume data, they posted
       * the alert, but it has not been received yet. Those torrents would
       * report that they don't need to save resume data again, and skipped by
       * the initial loop, and thwart the counter otherwise.
       */
      public void saveResumeData(resume_data_flags_t flags) {
          h.save_resume_data(flags);
      }
  
      /**
       * Similar to calling {@link #saveResumeData(resume_data_flags_t)} with
       * empty flags.
       */
      public void saveResumeData() {
          h.save_resume_data();
      }
  
      /**
       * Returns true if this handle refers to a valid torrent and false if it
       * hasn't been initialized or if the torrent it refers to has been
       * aborted. Note that a handle may become invalid after it has been added
       * to the session. Usually this is because the storage for the torrent is
       * somehow invalid or if the filenames are not allowed (and hence cannot
       * be opened/created) on your filesystem. If such an error occurs, a
       * file_error_alert is generated and all handles that refers to that
       * torrent will become invalid.
       */
      public boolean isValid() {
          return h.is_valid();
      }
  
      /**
       * Generates a magnet URI from the specified torrent. If the torrent
       * handle is invalid, null is returned.
       */
      public String makeMagnetUri() {
          return h.is_valid() ? libtorrent.make_magnet_uri_from_torrent_handle(h) : null;
      }
  
      /**
       * Will limit the upload bandwidth used by this
       * particular torrent to the limit you set. It is given as the number of
       * bytes per second the torrent is allowed to upload.
       */
      public int getUploadLimit() {
          return h.upload_limit();
      }
  
      /**
       * Will limit the upload bandwidth used by this
       * particular torrent to the limit you set. It is given as the number of
       * bytes per second the torrent is allowed to upload.
       */
      public void setUploadLimit(int limit) {
          h.set_upload_limit(limit);
      }
  
      // ``set_upload_limit`` will limit the upload bandwidth used by this
      // particular torrent to the limit you set. It is given as the number of
      // bytes per second the torrent is allowed to upload.
      // ``set_download_limit`` works the same way but for download bandwidth
      // instead of upload bandwidth. Note that setting a higher limit on a
      // torrent then the global limit
      // (``session_settings::upload_rate_limit``) will not override the global
      // rate limit. The torrent can never upload more than the global rate
      // limit.
      //
      // ``upload_limit`` and ``download_limit`` will return the current limit
      // setting, for upload and download, respectively.
      public int getDownloadLimit() {
          return h.download_limit();
      }
  
      // ``set_upload_limit`` will limit the upload bandwidth used by this
      // particular torrent to the limit you set. It is given as the number of
      // bytes per second the torrent is allowed to upload.
      // ``set_download_limit`` works the same way but for download bandwidth
      // instead of upload bandwidth. Note that setting a higher limit on a
      // torrent then the global limit
      // (``session_settings::upload_rate_limit``) will not override the global
      // rate limit. The torrent can never upload more than the global rate
      // limit.
      //
      // ``upload_limit`` and ``download_limit`` will return the current limit
      // setting, for upload and download, respectively.
      public void setDownloadLimit(int limit) {
          h.set_download_limit(limit);
      }
  
      /**
       * This method puts the torrent back in a state where it assumes to
       * have no resume data. All peers will be disconnected and the torrent
       * will stop announcing to the tracker. The torrent will be added to the
       * checking queue, and will be checked (all the files will be read and
       * compared to the piece hashes). Once the check is complete, the torrent
       * will start connecting to peers again, as normal.
       */
      public void forceRecheck() {
          h.force_recheck();
      }
  
      /**
       * By default, force-reannounce will still honor the min-interval
       * published by the tracker. If this flag is set, it will be ignored
       * and the tracker is announced immediately.
       */
      public static final reannounce_flags_t IGNORE_MIN_INTERVAL = torrent_handle.ignore_min_interval;
  
      // ``force_reannounce()`` will force this torrent to do another tracker
      // request, to receive new peers. The ``seconds`` argument specifies how
      // many seconds from now to issue the tracker announces.
      //
      // If the tracker's ``min_interval`` has not passed since the last
      // announce, the forced announce will be scheduled to happen immediately
      // as the ``min_interval`` expires. This is to honor trackers minimum
      // re-announce interval settings.
      //
      // The ``tracker_index`` argument specifies which tracker to re-announce.
      // If set to -1 (which is the default), all trackers are re-announce.
      //
      public void forceReannounce(int seconds, int tracker_index, reannounce_flags_t flags) {
          h.force_reannounce(seconds, tracker_index, flags);
      }
  
      // ``force_reannounce()`` will force this torrent to do another tracker
      // request, to receive new peers. The ``seconds`` argument specifies how
      // many seconds from now to issue the tracker announces.
      //
      // If the tracker's ``min_interval`` has not passed since the last
      // announce, the forced announce will be scheduled to happen immediately
      // as the ``min_interval`` expires. This is to honor trackers minimum
      // re-announce interval settings.
      //
      // The ``tracker_index`` argument specifies which tracker to re-announce.
      // If set to -1 (which is the default), all trackers are re-announce.
      //
      public void forceReannounce(int seconds, int tracker_index) {
          h.force_reannounce(seconds, tracker_index);
      }
  
      // ``force_reannounce()`` will force this torrent to do another tracker
      // request, to receive new peers. The ``seconds`` argument specifies how
      // many seconds from now to issue the tracker announces.
      //
      // If the tracker's ``min_interval`` has not passed since the last
      // announce, the forced announce will be scheduled to happen immediately
      // as the ``min_interval`` expires. This is to honor trackers minimum
      // re-announce interval settings.
      //
      // The ``url`` argument specifies which tracker to re-announce.
      //
      public void forceReannounce(int seconds, String url, reannounce_flags_t flags) {
          h.force_reannounce(seconds, url, flags);
      }
  
      // ``force_reannounce()`` will force this torrent to do another tracker
      // request, to receive new peers. The ``seconds`` argument specifies how
      // many seconds from now to issue the tracker announces.
      //
      // If the tracker's ``min_interval`` has not passed since the last
      // announce, the forced announce will be scheduled to happen immediately
      // as the ``min_interval`` expires. This is to honor trackers minimum
      // re-announce interval settings.
      //
      // The ``url`` argument specifies which tracker to re-announce.
      //
      public void forceReannounce(int seconds, String url) {
          h.force_reannounce(seconds, url);
      }
  
      // ``force_reannounce()`` will force this torrent to do another tracker
      // request, to receive new peers. The ``seconds`` argument specifies how
      // many seconds from now to issue the tracker announces.
      //
      // If the tracker's ``min_interval`` has not passed since the last
      // announce, the forced announce will be scheduled to happen immediately
      // as the ``min_interval`` expires. This is to honor trackers minimum
      // re-announce interval settings.
      //
      // The ``tracker_index`` argument specifies which tracker to re-announce.
      // If set to -1 (which is the default), all trackers are re-announce.
      //
      public void forceReannounce(int seconds) {
          h.force_reannounce(seconds);
      }
  
      /**
       * Force this torrent to do another tracker
       * request, to receive new peers. The ``seconds`` argument specifies how
       * many seconds from now to issue the tracker announces.
       * <p>
       * If the tracker's ``min_interval`` has not passed since the last
       * announce, the forced announce will be scheduled to happen immediately
       * as the ``min_interval`` expires. This is to honor trackers minimum
       * re-announce interval settings.
       * <p>
       * The ``tracker_index`` argument specifies which tracker to re-announce.
       * If set to -1 (which is the default), all trackers are re-announce.
       */
      public void forceReannounce() {
          h.force_reannounce();
      }
  
      /**
       * Announce the torrent to the DHT immediately.
       */
      public void forceDHTAnnounce() {
          h.force_dht_announce();
      }
  
      /**
       * Announce the torrent on LSD immediately.
       */
      public void forceLSDAnnounce() {
          h.force_lsd_announce();
      }
  
      // ``scrape_tracker()`` will send a scrape request to a tracker. By
      // default (``idx`` = -1) it will scrape the last working tracker. If
      // ``idx`` is >= 0, the tracker with the specified index will scraped.
      //
      // A scrape request queries the tracker for statistics such as total
      // number of incomplete peers, complete peers, number of downloads etc.
      //
      // This request will specifically update the ``num_complete`` and
      // ``num_incomplete`` fields in the torrent_status struct once it
      // completes. When it completes, it will generate a scrape_reply_alert.
      // If it fails, it will generate a scrape_failed_alert.
      public void scrapeTracker(int idx) {
          h.scrape_tracker(idx);
      }
  
      // ``scrape_tracker()`` will send a scrape request to a tracker. By
      // default (``idx`` = -1) it will scrape the last working tracker. If
      // ``idx`` is >= 0, the tracker with the specified index will scraped.
      //
      // A scrape request queries the tracker for statistics such as total
      // number of incomplete peers, complete peers, number of downloads etc.
      //
      // This request will specifically update the ``num_complete`` and
      // ``num_incomplete`` fields in the torrent_status struct once it
      // completes. When it completes, it will generate a scrape_reply_alert.
      // If it fails, it will generate a scrape_failed_alert.
      public void scrapeTracker(String url) {
          h.scrape_tracker(url);
      }
  
      /**
       * Will return a sorted list with the trackers of this torrent.
       * <p>
       * The announce entry contains both a string {@code url} which specify the
       * announce url for the tracker as well as an int {@code tier}, which
       * specifies the order in which this tracker is tried.
       *
       * @return the list of trackers
       */
      public List<AnnounceEntry> trackers() {
          if (!h.is_valid()) {
              return Collections.emptyList();
          }
          return trackers(h.trackers());
      }
  
      /**
       * Will send a scrape request to the tracker. A
       * scrape request queries the tracker for statistics such as total number
       * of incomplete peers, complete peers, number of downloads etc.
       * <p>
       * This request will specifically update the ``num_complete`` and
       * ``num_incomplete`` fields in the torrent_status struct once it
       * completes. When it completes, it will generate a scrape_reply_alert.
       * If it fails, it will generate a scrape_failed_alert.
       */
      public void scrapeTracker() {
          h.scrape_tracker();
      }
  
      /**
       * If you want libtorrent to use another list of trackers for this torrent,
       * you can use {@link #replaceTrackers(List)} which takes a list of the same
       * form as the one returned from {@link #trackers()} and will replace it.
       * If you want an immediate effect, you have to call {@link #forceReannounce()}.
       * <p>
       * The updated set of trackers will be saved in the resume data, and when
       * a torrent is started with resume data, the trackers from the resume
       * data will replace the original ones.
       *
       * @param trackers the list of trackers
       * @see AnnounceEntry
       */
      public void replaceTrackers(List<AnnounceEntry> trackers) {
          announce_entry_vector v = new announce_entry_vector();
  
          for (AnnounceEntry t : trackers) {
              v.add(t.swig());
          }
  
          h.replace_trackers(v);
      }
  
      /**
       * This method will look if the specified tracker is already in the
       * set. If it is, it doesn't do anything. If it's not in the current set
       * of trackers, it will insert it in the tier specified in the
       * {@link AnnounceEntry}.
       * <p>
       * The updated set of trackers will be saved in the resume data, and when
       * a torrent is started with resume data, the trackers from the resume
       * data will replace the original ones.
       */
      public void addTracker(AnnounceEntry tracker) {
          h.add_tracker(tracker.swig());
      }
  
      /**
       * Adds another url to the torrent's list of url
       * seeds. If the given url already exists in that list, the call has no
       * effect. The torrent will connect to the server and try to download
       * pieces from it, unless it's paused, queued, checking or seeding.
       */
      public void addUrlSeed(String url) {
          h.add_url_seed(url);
      }
  
      /**
       * Removes the given url if it exists already.
       */
      public void removeUrlSeed(String url) {
          h.remove_url_seed(url);
      }
  
      /**
       * Return a set of the url seeds currently in this
       * torrent. This list is based on BEP 19.
       *
       * @return the url seed list
       */
      public List<String> urlSeeds() {
          return Vectors.string_vector2list(h.get_url_seeds());
      }
  
      /**
       * Returns an array with the availability for each piece in this torrent.
       * libtorrent does not keep track of availability for seeds, so if the
       * torrent is seeding the availability for all pieces is reported as 0.
       * <p>
       * The piece availability is the number of peers that we are connected
       * that has advertised having a particular piece. This is the information
       * that libtorrent uses in order to prefer picking rare pieces.
       *
       * @return the array with piece availability
       */
      public int[] pieceAvailability() {
          int_vector v = new int_vector();
          h.piece_availability(v);
          return Vectors.int_vector2ints(v);
      }
  
      // These functions are used to set and get the priority of individual
      // pieces. By default all pieces have priority 1. That means that the
      // random rarest first algorithm is effectively active for all pieces.
      // You may however change the priority of individual pieces. There are 8
      // different priority levels:
      //
      //  0. piece is not downloaded at all
      //  1. normal priority. Download order is dependent on availability
      //  2. higher than normal priority. Pieces are preferred over pieces with
      //     the same availability, but not over pieces with lower availability
      //  3. pieces are as likely to be picked as partial pieces.
      //  4. pieces are preferred over partial pieces, but not over pieces with
      //     lower availability
      //  5. *currently the same as 4*
      //  6. piece is as likely to be picked as any piece with availability 1
      //  7. maximum priority, availability is disregarded, the piece is
      //     preferred over any other piece with lower priority
      //
      // The exact definitions of these priorities are implementation details,
      // and subject to change. The interface guarantees that higher number
      // means higher priority, and that 0 means do not download.
      //
      // ``piece_priority`` sets or gets the priority for an individual piece,
      // specified by ``index``.
      //
      // ``prioritize_pieces`` takes a vector of integers, one integer per
      // piece in the torrent. All the piece priorities will be updated with
      // the priorities in the vector.
      //
      // ``piece_priorities`` returns a vector with one element for each piece
      // in the torrent. Each element is the current priority of that piece.
      public void piecePriority(int index, Priority priority) {
          h.piece_priority_ex(index, priority.swig());
      }
  
      public Priority piecePriority(int index) {
          return Priority.fromSwig(h.piece_priority_ex(index));
      }
  
      public void prioritizePieces(Priority[] priorities) {
          h.prioritize_pieces_ex(Priority.array2vector(priorities));
      }
  
      public Priority[] piecePriorities() {
          byte_vector v = h.get_piece_priorities_ex();
          return Priority.vector2array(v);
      }
  
      /**
       * index must be in the range [0, number_of_files).
       * <p>
       * The priority values are the same as for piece_priority().
       * <p>
       * Whenever a file priority is changed, all other piece priorities are
       * reset to match the file priorities. In order to maintain sepcial
       * priorities for particular pieces, piece_priority() has to be called
       * again for those pieces.
       * <p>
       * You cannot set the file priorities on a torrent that does not yet have
       * metadata or a torrent that is a seed. ``file_priority(int, int)`` and
       * prioritize_files() are both no-ops for such torrents.
       *
       * @param index
       * @param priority
       */
      public void filePriority(int index, Priority priority) {
          h.file_priority_ex(index, priority.swig());
      }
  
      /**
       * index must be in the range [0, number_of_files).
       * <p>
       * queries or sets the priority of file index.
       *
       * @param index
       *
       */
      public Priority filePriority(int index) {
          return Priority.fromSwig(h.file_priority_ex(index));
      }
  
      /**
       * Takes a vector that has at as many elements as
       * there are files in the torrent. Each entry is the priority of that
       * file. The function sets the priorities of all the pieces in the
       * torrent based on the vector.
       *
       * @param priorities the array of priorities
       */
      public void prioritizeFiles(Priority[] priorities) {
          h.prioritize_files_ex(Priority.array2vector(priorities));
      }
  
      /**
       * Returns a vector with the priorities of all files.
       *
       * @return the array of priorities.
       */
      public Priority[] filePriorities() {
          byte_vector v = h.get_file_priorities_ex();
          return Priority.vector2array(v);
      }
  
      /**
       * This function sets or resets the deadline associated with a specific
       * piece index (``index``). libtorrent will attempt to download this
       * entire piece before the deadline expires. This is not necessarily
       * possible, but pieces with a more recent deadline will always be
       * prioritized over pieces with a deadline further ahead in time. The
       * deadline (and flags) of a piece can be changed by calling this
       * function again.
       * <p>
       * If the piece is already downloaded when this call is made, nothing
       * happens, unless the alert_when_available flag is set, in which case it
       * will do the same thing as calling read_piece() for ``index``.
       *
       * @param index
       * @param deadline
       */
      public void setPieceDeadline(int index, int deadline) {
          h.set_piece_deadline(index, deadline);
      }
  
      /**
       *
       */
      public static final deadline_flags_t ALERT_WHEN_AVAILABLE = torrent_handle.alert_when_available;
  
      /**
       * This function sets or resets the deadline associated with a specific
       * piece index (``index``). libtorrent will attempt to download this
       * entire piece before the deadline expires. This is not necessarily
       * possible, but pieces with a more recent deadline will always be
       * prioritized over pieces with a deadline further ahead in time. The
       * deadline (and flags) of a piece can be changed by calling this
       * function again.
       * <p>
       * The ``flags`` parameter can be used to ask libtorrent to send an alert
       * once the piece has been downloaded, by passing alert_when_available.
       * When set, the read_piece_alert alert will be delivered, with the piece
       * data, when it's downloaded.
       * <p>
       * If the piece is already downloaded when this call is made, nothing
       * happens, unless the alert_when_available flag is set, in which case it
       * will do the same thing as calling read_piece() for ``index``.
       *
       * @param index
       * @param deadline
       * @param flags
       */
      public void setPieceDeadline(int index, int deadline, deadline_flags_t flags) {
          h.set_piece_deadline(index, deadline, flags);
      }
  
      /**
       * Removes the deadline from the piece. If it
       * hasn't already been downloaded, it will no longer be considered a
       * priority.
       *
       * @param index
       */
      public void resetPieceDeadline(int index) {
          h.reset_piece_deadline(index);
      }
  
      /**
       * Removes deadlines on all pieces in the torrent.
       * As if {@link #resetPieceDeadline(int)} was called on all pieces.
       */
      public void clearPieceDeadlines() {
          h.clear_piece_deadlines();
      }
  
      /**
       * Only calculate file progress at piece granularity. This makes
       * the `fileProgress()` call cheaper and also only takes bytes that
       * have passed the hash check into account, so progress cannot
       * regress in this mode.
       */
      public static final file_progress_flags_t PIECE_GRANULARITY = torrent_handle.piece_granularity;
  
      /**
       * This function fills in the supplied vector with the number of
       * bytes downloaded of each file in this torrent. The progress values are
       * ordered the same as the files in the torrent_info. This operation is
       * not very cheap. Its complexity is *O(n + mj)*. Where *n* is the number
       * of files, *m* is the number of downloading pieces and *j* is the
       * number of blocks in a piece.
       * <p>
       * The ``flags`` parameter can be used to specify the granularity of the
       * file progress. If left at the default value of 0, the progress will be
       * as accurate as possible, but also more expensive to calculate. If
       * ``torrent_handle::piece_granularity`` is specified, the progress will
       * be specified in piece granularity. i.e. only pieces that have been
       * fully downloaded and passed the hash check count. When specifying
       * piece granularity, the operation is a lot cheaper, since libtorrent
       * already keeps track of this internally and no calculation is required.
       *
       * @return the file progress
       */
      public long[] fileProgress(file_progress_flags_t flags) {
          int64_vector v = new int64_vector();
          h.file_progress(v, flags);
          return Vectors.int64_vector2longs(v);
      }
  
      /**
       * This function fills in the supplied vector with the number of
       * bytes downloaded of each file in this torrent. The progress values are
       * ordered the same as the files in the torrent_info. This operation is
       * not very cheap. Its complexity is *O(n + mj)*. Where *n* is the number
       * of files, *m* is the number of downloading pieces and *j* is the
       * number of blocks in a piece.
       *
       * @return the file progress
       */
      public long[] fileProgress() {
          int64_vector v = new int64_vector();
          h.file_progress(v);
          return Vectors.int64_vector2longs(v);
      }
  
      /**
       * The path to the directory where this torrent's files are stored.
       * It's typically the path as was given to async_add_torrent() or
       * add_torrent() when this torrent was started.
       */
      public String savePath() {
          torrent_status ts = h.status(torrent_handle.query_save_path);
          return ts.getSave_path();
      }
  
      /**
       * The name of the torrent. Typically this is derived from the
       * .torrent file. In case the torrent was started without metadata,
       * and hasn't completely received it yet, it returns the name given
       * to it when added to the session.
       *
       * @return the name
       */
      public String getName() {
          torrent_status ts = h.status(torrent_handle.query_name);
          return ts.getName();
      }
  
      /**
       * Moves the file(s) that this torrent are currently seeding from or
       * downloading to. If the given {@code savePath} is not located on the same
       * drive as the original save path, the files will be copied to the new
       * drive and removed from their original location. This will block all
       * other disk IO, and other torrents download and upload rates may drop
       * while copying the file.
       * <p>
       * Since disk IO is performed in a separate thread, this operation is
       * also asynchronous. Once the operation completes, the
       * {@link StorageMovedAlert} is generated,
       * with the new path as the message. If the move fails for some reason,
       * {@link StorageMovedFailedAlert}
       * generated instead, containing the error message.
       * <p>
       * The {@code flags} argument determines the behavior of the copying/moving
       * of the files in the torrent.
       * <p>
       * Files that have been renamed to have absolute paths are not moved by
       * this function. Keep in mind that files that don't belong to the
       * torrent but are stored in the torrent's directory may be moved as
       * well. This goes for files that have been renamed to absolute paths
       * that still end up inside the save path.
       *
       * @param savePath the new save path
       * @param flags    the move behavior flags
       */
      public void moveStorage(String savePath, MoveFlags flags) {
          h.move_storage(savePath, flags.swig());
      }
  
      /**
       * Sames as calling {@link #moveStorage(String, MoveFlags)} with empty flags.
       *
       * @param savePath the new path
       * @see #moveStorage(String, MoveFlags)
       */
      public void moveStorage(String savePath) {
          h.move_storage(savePath);
      }
  
      /**
       * Renames the file with the given index asynchronously. The rename
       * operation is complete when either a {@link FileRenamedAlert}  or
       * {@link FileRenameFailedAlert} is posted.
       *
       * @param index
       * @param newName
       */
      public void renameFile(int index, String newName) {
          h.rename_file(index, newName);
      }
  
      /**
       * Returns true if the torrent is in the session. It returns true before
       * session::remove_torrent() is called, and false afterward.
       * <p>
       * Note that this is a blocking function, unlike {@link #isValid()}
       * which returns immediately.
       *
       * @return true if the torrent is in the session
       */
      public boolean inSession() {
          return h.in_session();
      }
  
      @Override
      public boolean equals(Object obj) {
          if (!(obj instanceof TorrentHandle)) {
              return false;
          }
  
          return h.eq(((TorrentHandle) obj).h);
      }
  
      @Override
      public int hashCode() {
          return Long.hashCode(h.id());
      }
  
      // helper function
      private static ArrayList<AnnounceEntry> trackers(announce_entry_vector v) {
          int size = v.size();
          ArrayList<AnnounceEntry> l = new ArrayList<>(size);
  
          for (int i = 0; i < size; i++) {
              l.add(new AnnounceEntry(v.get(i)));
          }
  
          return l;
      }
  }
  