content: package com.acgist.snail.net.torrent.peer;
  
  import com.acgist.snail.config.PeerConfig;
  import com.acgist.snail.logger.Logger;
  import com.acgist.snail.logger.LoggerFactory;
  import com.acgist.snail.net.torrent.TorrentSession;
  import com.acgist.snail.net.torrent.utp.UtpClient;
  
  /**
   * Peer下载
   * 
   * @author acgist
   */
  public final class PeerDownloader extends PeerConnect {
      
      private static final Logger LOGGER = LoggerFactory.getLogger(PeerDownloader.class);
      
      /**
       * @param peerSession    Peer信息
       * @param torrentSession BT任务信息
       */
      private PeerDownloader(PeerSession peerSession, TorrentSession torrentSession) {
          super(peerSession, torrentSession, PeerSubMessageHandler.newInstance(peerSession, torrentSession));
      }
      
      /**
       * 新建Peer下载
       * 
       * @param peerSession    Peer信息
       * @param torrentSession BT任务信息
       * 
       * @return {@link PeerDownloader}
       */
      public static final PeerDownloader newInstance(PeerSession peerSession, TorrentSession torrentSession) {
          return new PeerDownloader(peerSession, torrentSession);
      }
      
      /**
       * 握手
       * 
       * @return 是否握手成功
       */
      public boolean handshake() {
          // 建立连接
          final boolean success = this.connect();
          if(success) {
              // 发送握手
              this.peerSubMessageHandler.initClient(this).handshake();
          } else {
              this.peerSession.incrementFailTimes();
          }
          this.available = success;
          return success;
      }
      
      /**
       * 建立连接
       * 优先使用TCP/UTP进行连接，如果连接失败使用holepunch协议重连。
       * 
       * @return 是否连接成功
       */
      private boolean connect() {
          if(this.peerSession.utp()) {
              LOGGER.debug("Peer连接（UTP）：{}", this.peerSession);
              final UtpClient utpClient = UtpClient.newInstance(this.peerSession, this.peerSubMessageHandler);
              final boolean utpOk = utpClient.connect();
              if(utpOk) {
                  return utpOk;
              } else {
                  return this.holepunchConnect(false);
              }
          } else {
              LOGGER.debug("Peer连接（TCP）：{}", this.peerSession);
              final PeerClient peerClient = PeerClient.newInstance(this.peerSession, this.peerSubMessageHandler);
              final boolean tcpOk = peerClient.connect();
              if(tcpOk) {
                  return tcpOk;
              } else {
                  return this.holepunchConnect(true);
              }
          }
      }
  
      /**
       * 使用holepunch协议连接
       * 
       * @param utpRetry 是否可以使用UTP重试
       * 
       * @return 是否连接成功
       */
      private boolean holepunchConnect(boolean utpRetry) {
          // Peer不可以直接连接：使用holepunch协议连接
          if(!this.peerSession.outgo()) {
              // 中继：PEX来源
              final PeerSession pexSource = this.peerSession.pexSource();
              if(
                  pexSource != null &&
                  // 中继已经连接
                  pexSource.connected() &&
                  // 中继支持holepunch
                  pexSource.holepunch() &&
                  // 目标支持holepunch
                  this.peerSession.holepunch()
              ) {
                  final var peerConnect = pexSource.peerConnect();
                  if(peerConnect != null) {
                      // 是否已经连接
                      if(!this.peerSession.holeunchConnect()) {
                          peerConnect.holepunchRendezvous(this.peerSession);
                      }
                      // 已经收到holepunch连接消息使用UTP协议连接
                      if(this.peerSession.holeunchConnect()) {
                          LOGGER.debug("Peer连接（holepunch）：{}", this.peerSession);
                          final UtpClient utpClient = UtpClient.newInstance(this.peerSession, this.peerSubMessageHandler);
                          return utpClient.connect();
                      } else {
                          LOGGER.debug("Peer连接失败（holepunch）：{}", this.peerSession);
                      }
                  }
              }
          }
          // holepunch协议连接失败并且可以使用UTP重试
          if(utpRetry) {
              LOGGER.debug("Peer连接重试（UTP）：{}", this.peerSession);
              final UtpClient utpClient = UtpClient.newInstance(this.peerSession, this.peerSubMessageHandler);
              final boolean utpOk = utpClient.connect();
              if(utpOk) {
                  // 支持UTP
                  this.peerSession.flags(PeerConfig.PEX_UTP);
                  // 直接连接
                  this.peerSession.flags(PeerConfig.PEX_OUTGO);
                  return utpOk;
              }
          }
          return false;
      }
      
      @Override
      public void release() {
          try {
              if(this.available) {
                  LOGGER.debug("关闭PeerDownloader：{}", this.peerSession);
                  super.release();
              }
          } catch (Exception e) {
              LOGGER.error("关闭PeerDownloader异常", e);
          } finally {
              this.peerSession.statusOff(PeerConfig.STATUS_DOWNLOAD);
              this.peerSession.peerDownloader(null);
          }
      }
  
  }
  