content: package com.acgist.snail.config;
  
  import java.util.LinkedHashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.stream.Collectors;
  
  import com.acgist.snail.logger.Logger;
  import com.acgist.snail.logger.LoggerFactory;
  import com.acgist.snail.net.torrent.TorrentAcceptHandler;
  import com.acgist.snail.net.torrent.dht.DhtContext;
  import com.acgist.snail.net.torrent.dht.NodeContext;
  import com.acgist.snail.net.torrent.dht.NodeSession;
  import com.acgist.snail.net.torrent.dht.request.AnnouncePeerRequest;
  import com.acgist.snail.net.torrent.dht.request.FindNodeRequest;
  import com.acgist.snail.net.torrent.dht.request.GetPeersRequest;
  import com.acgist.snail.net.torrent.dht.request.PingRequest;
  import com.acgist.snail.net.torrent.dht.response.AnnouncePeerResponse;
  import com.acgist.snail.net.torrent.dht.response.FindNodeResponse;
  import com.acgist.snail.net.torrent.dht.response.GetPeersResponse;
  import com.acgist.snail.net.torrent.dht.response.PingResponse;
  import com.acgist.snail.utils.StringUtils;
  
  /**
   * DHT配置
   * 
   * @author acgist
   */
  public final class DhtConfig extends PropertiesConfig {
      
      private static final Logger LOGGER = LoggerFactory.getLogger(DhtConfig.class);
      
      /**
       * 请求类型
       * 
       * @author acgist
       */
      public enum QType {
          
          /**
           * ping
           * 
           * @see PingRequest
           * @see PingResponse
           */
          PING("ping"),
          /**
           * 查找节点
           * 
           * @see FindNodeRequest
           * @see FindNodeResponse
           */
          FIND_NODE("find_node"),
          /**
           * 查找Peer
           * 
           * @see GetPeersRequest
           * @see GetPeersResponse
           */
          GET_PEERS("get_peers"),
          /**
           * 声明Peer
           * 
           * @see AnnouncePeerRequest
           * @see AnnouncePeerResponse
           */
          ANNOUNCE_PEER("announce_peer");
          
          /**
           * 类型标识
           */
          private final String value;
          
          /**
           * @param value 类型标识
           */
          private QType(String value) {
              this.value = value;
          }
          
          /**
           * @return 类型标识
           */
          public String getValue() {
              return this.value;
          }
          
          /**
           * @param value 类型标识
           * 
           * @return 请求类型
           */
          public static final QType of(String value) {
              final QType[] types = QType.values();
              for (final QType type : types) {
                  if(type.value.equals(value)) {
                      return type;
                  }
              }
              return null;
          }
          
      }
      
      /**
       * 错误编码
       * 数据类型：{@link List}
       * 数据格式：[0]=错误编码；[1]=错误描述；
       * 
       * @author acgist
       */
      public enum ErrorCode {
          
          /**
           * 一般错误
           */
          CODE_201(201),
          /**
           * 服务错误
           */
          CODE_202(202),
          /**
           * 协议错误：不规范包、无效参数、错误Token
           */
          CODE_203(203),
          /**
           * 未知方法
           */
          CODE_204(204);
          
          /**
           * 错误编码
           */
          private final int code;
          
          /**
           * @param code 错误编码
           */
          private ErrorCode(int code) {
              this.code = code;
          }
          
          /**
           * @return 错误编码
           */
          public int getCode() {
              return this.code;
          }
          
      }
      
      /**
       * DHT配置文件
       */
      public static final String DHT_CONFIG = "/config/bt.dht.properties";
      /**
       * DHT消息字符
       * 
       * @see TorrentAcceptHandler
       */
      public static final byte DHT_HEADER = 'd';
      /**
       * 消息ID（请求ID、响应ID）
       * 
       * @see DhtContext#buildRequestId()
       */
      public static final String KEY_T = "t";
      /**
       * 消息类型
       * 请求消息类型：{@link #KEY_Q}
       * 响应消息类型：{@link #KEY_R}
       */
      public static final String KEY_Y = "y";
      /**
       * 请求消息类型、请求类型
       * 请求消息类型：{@link #KEY_Y}
       * 请求类型：{@link QType}
       */
      public static final String KEY_Q = "q";
      /**
       * 响应消息类型、响应参数
       * 响应消息类型：{@link #KEY_Y}
       * 响应参数类型：{@link Map}
       */
      public static final String KEY_R = "r";
      /**
       * 请求参数
       * 请求参数类型：{@link Map}
       */
      public static final String KEY_A = "a";
      /**
       * 错误编码
       * 错误编码类型：{@link Map}
       * 
       * @see ErrorCode
       */
      public static final String KEY_E = "e";
      /**
       * 客户端版本
       */
      public static final String KEY_V = "v";
      /**
       * NodeId
       * 
       * @see NodeContext#getNodeId()
       */
      public static final String KEY_ID = "id";
      /**
       * 下载端口
       * 
       * @see QType#ANNOUNCE_PEER
       * @see SystemConfig#getTorrentPortExt()
       */
      public static final String KEY_PORT = "port";
      /**
       * Token
       * 
       * @see QType#ANNOUNCE_PEER
       */
      public static final String KEY_TOKEN = "token";
      /**
       * IPv4节点列表
       * 
       * @see QType#FIND_NODE
       * @see QType#GET_PEERS
       */
      public static final String KEY_NODES = "nodes";
      /**
       * IPv6节点列表
       * 
       * @see QType#FIND_NODE
       * @see QType#GET_PEERS
       */
      public static final String KEY_NODES6 = "nodes6";
      /**
       * IPv4节点
       * 
       * @see #KEY_WANT
       */
      public static final String KEY_WANT_N4 = "n4";
      /**
       * IPv6节点
       * 
       * @see #KEY_WANT
       */
      public static final String KEY_WANT_N6 = "n6";
      /**
       * 请求返回节点类型
       * 
       * @see #KEY_WANT_N4
       * @see #KEY_WANT_N6
       * @see QType#FIND_NODE
       * @see QType#GET_PEERS
       */
      public static final String KEY_WANT = "want";
      /**
       * Peer列表
       * 
       * @see QType#GET_PEERS
       */
      public static final String KEY_VALUES = "values";
      /**
       * 目标（NodeId、InfoHash）
       * 
       * @see QType#FIND_NODE
       */
      public static final String KEY_TARGET = "target";
      /**
       * InfoHash
       * 
       * @see QType#GET_PEERS
       * @see QType#ANNOUNCE_PEER
       */
      public static final String KEY_INFO_HASH = "info_hash";
      /**
       * 是否自动获取Peer端口
       * 
       * @see #IMPLIED_PORT_AUTO
       * @see #IMPLIED_PORT_CONFIG
       * @see QType#ANNOUNCE_PEER
       */
      public static final String KEY_IMPLIED_PORT = "implied_port";
      /**
       * 自动配置：忽略端口配置
       * 使用UDP连接端口作为Peer端口（支持UTP）
       */
      public static final Integer IMPLIED_PORT_AUTO = 1;
      /**
       * 端口配置
       * 使用消息端口配置
       * 
       * @see #KEY_PORT
       */
      public static final Integer IMPLIED_PORT_CONFIG = 0;
      /**
       * Peer列表长度
       * 
       * @see QType#GET_PEERS
       */
      public static final int GET_PEER_SIZE = 32;
      /**
       * NodeId长度
       */
      public static final int NODE_ID_LENGTH = 20;
      /**
       * Node最大保存数量
       */
      public static final int MAX_NODE_SIZE = 1024;
      /**
       * DHT超时请求清理执行周期（分钟）
       */
      public static final int DHT_REQUEST_TIMEOUT_INTERVAL = 8;
      
      /**
       * 默认DHT节点
       * NodeID=host:port
       */
      private final Map<String, String> nodes = new LinkedHashMap<>();
      
      private static final DhtConfig INSTANCE = new DhtConfig();
      
      public static final DhtConfig getInstance() {
          return INSTANCE;
      }
      
      private DhtConfig() {
          super(DHT_CONFIG);
          this.init();
          this.release();
      }
      
      @Override
      public void init() {
          this.properties.entrySet().forEach(entry -> {
              final String nodeId  = (String) entry.getKey();
              final String address = (String) entry.getValue();
              if(StringUtils.isNotEmpty(nodeId) && StringUtils.isNotEmpty(address)) {
                  this.nodes.put(nodeId, address);
              } else {
                  LOGGER.warn("默认DHT节点注册失败：{} - {}", nodeId, address);
              }
          });
          if(LOGGER.isDebugEnabled()) {
              LOGGER.debug("加载DHT节点数量：{}", this.nodes.size());
          }
      }
  
      @Override
      public void persistent() {
          final Map<String, String> data = NodeContext.getInstance().resize().stream()
              .filter(NodeSession::useable)
              .collect(Collectors.toMap(
                  node -> StringUtils.hex(node.getId()),
                  node -> SymbolConfig.Symbol.COLON.join(node.getHost(), node.getPort())
              ));
          this.persistent(data, DHT_CONFIG);
          if(LOGGER.isDebugEnabled()) {
              LOGGER.debug("保存DHT节点数量：{}", data.size());
          }
      }
      
      /**
       * @return 默认DHT节点
       */
      public Map<String, String> getNodes() {
          return this.nodes;
      }
      
  }
  