package com.acgist.snail.net.torrent.dht;

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import com.acgist.snail.config.DhtConfig;
import com.acgist.snail.config.SystemConfig;
import com.acgist.snail.context.IContext;
import com.acgist.snail.context.SystemThreadContext;
import com.acgist.snail.logger.Logger;
import com.acgist.snail.logger.LoggerFactory;
import com.acgist.snail.utils.NumberUtils;

/**
 * DHT上下文
 * 
 * TODO：nodeconext合并
 * 
 * @author acgist
 */
public final class DhtContext implements IContext {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(DhtContext.class);
    
    private static final DhtContext INSTANCE = new DhtContext();
    
    public static final DhtContext getInstance() {
        return INSTANCE;
    }
    
    /**
     * Token长度：{@value}
     */
    private static final int TOKEN_LENGTH = 8;
    /**
     * Token字符：{@value}
     */
    private static final String TOKEN_CHARACTER =
        "0123456789"                 +
        "abcdefghijklmnopqrstuvwxyz" +
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    /**
     * Token
     */
    private final byte[] token;
    /**
     * 消息ID
     */
    private short requestId = Short.MIN_VALUE;
    /**
     * DHT请求列表
     */
    private final List<DhtRequest> requests;
    
    private DhtContext() {
        this.token    = this.buildToken();
        this.requests = new LinkedList<>();
        SystemThreadContext.scheduledAtFixedDelay(
            DhtConfig.DHT_REQUEST_TIMEOUT_INTERVAL,
            DhtConfig.DHT_REQUEST_TIMEOUT_INTERVAL,
            TimeUnit.MINUTES,
            this::timeout
        );
    }
    
    /**
     * @return Token
     */
    public byte[] token() {
        return this.token;
    }
    
    /**
     * @return Token
     */
    private byte[] buildToken() {
        final byte[] token  = new byte[TOKEN_LENGTH];
        final byte[] bytes  = TOKEN_CHARACTER.getBytes();
        final int length    = bytes.length;
        final Random random = NumberUtils.random();
        for (int index = 0; index < TOKEN_LENGTH; index++) {
            token[index] = bytes[random.nextInt(length)];
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("生成DHT的Token：{}", new String(token));
        }
        return token;
    }
    
    /**
     * @return 消息ID
     */
    public byte[] buildRequestId() {
        synchronized (this) {
            return NumberUtils.shortToBytes(this.requestId++);
        }
    }
    
    /**
     * 放入请求
     * 
     * @param request 请求
     */
    public void request(DhtRequest request) {
        if (request == null) {
            return;
        }
        synchronized (this.requests) {
            // 删除旧的请求
            final DhtRequest oldRequest = this.remove(request.getT());
            if (oldRequest != null) {
                LOGGER.debug("删除没有收到响应的DHT请求：{}", oldRequest);
            }
            this.requests.add(request);
        }
    }
    
    /**
     * 设置响应
     * 
     * @param response 响应
     * 
     * @return 响应对应的请求
     */
    public DhtRequest response(DhtResponse response) {
        if (response == null) {
            return null;
        }
        // 设置节点可用状态
        NodeContext.getInstance().available(response.getNodeId());
        DhtRequest request;
        synchronized (this.requests) {
            // 删除请求
            request = this.remove(response.getT());
        }
        if (request != null) {
            // 设置响应
            request.setResponse(response);
        }
        return request;
    }
    
    /**
     * 处理DHT超时请求
     */
    private void timeout() {
        final long timeout   = SystemConfig.RECEIVE_TIMEOUT_MILLIS;
        final long timestamp = System.currentTimeMillis();
        synchronized (this.requests) {
            final int oldSize = this.requests.size();
            DhtRequest request;
            final Iterator<DhtRequest> iterator = this.requests.iterator();
            while(iterator.hasNext()) {
                request = iterator.next();
                if(timestamp - request.getTimestamp() > timeout) {
                    iterator.remove();
                }
            }
            final int newSize = this.requests.size();
            LOGGER.debug("""
                处理DHT超时请求
                处理之前请求数量：{}
                处理之后请求数量：{}
                """, oldSize, newSize);
        }
    }
    
    /**
     * 删除并返回删除的请求
     * 注意线程安全
     * 
     * @param id 消息ID
     * 
     * @return 请求
     */
    private DhtRequest remove(byte[] id) {
        DhtRequest request;
        final Iterator<DhtRequest> iterator = this.requests.iterator();
        while(iterator.hasNext()) {
            request = iterator.next();
            if(Arrays.equals(id, request.getT())) {
                iterator.remove();
                return request;
            }
        }
        return null;
    }

}
