package com.tsi.comm.server.service;

import com.tsi.comm.server.config.TsiCvimServerConfig;
import com.tsi.comm.server.vo.TsiNodeVo;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerArray;

@Slf4j
@Getter
@Setter
@Service
public class TsiQueueDistributorService {

    private final TsiCvimServerConfig serverConfig;

    private final AtomicIntegerArray packetLoads;
    private final AtomicIntegerArray dbmsLoads;
    private final AtomicIntegerArray logLoads;

    public TsiQueueDistributorService(TsiCvimServerConfig serverConfig) {
        this.serverConfig = serverConfig;
        this.packetLoads = new AtomicIntegerArray(this.serverConfig.getPacketWorkers());
        this.dbmsLoads = new AtomicIntegerArray(this.serverConfig.getDbmsWorkers());
        this.logLoads = new AtomicIntegerArray(this.serverConfig.getLoggingWorkers());
    }

    private int getLeastLoadedQueue(AtomicIntegerArray loads) {
        int length = loads.length();
        int minIndex = 0;
        int minLoad = loads.get(0);
        for (int ii = 1; ii < length; ii++) {
            int current = loads.get(ii);
            if (current < minLoad) {
                minLoad = current;
                minIndex = ii;
            }
        }
        loads.incrementAndGet(minIndex); // 새로운 장비 배정
        return minIndex;
    }

    public void setLoadedQueue(TsiNodeVo node) {
        int packetIdx = getLeastLoadedQueue(this.packetLoads);
        int dbmsIdx = getLeastLoadedQueue(this.dbmsLoads);
        int loggingIdx = getLeastLoadedQueue(this.logLoads);
        node.setPktQIdx(packetIdx);
        node.setDbmsQIdx(dbmsIdx);
        node.setLogQIdx(loggingIdx);
    }

    public void releaseQueue(TsiNodeVo node) {
        this.packetLoads.decrementAndGet(node.getPktQIdx());
        this.dbmsLoads.decrementAndGet(node.getDbmsQIdx());
        this.logLoads.decrementAndGet(node.getLogQIdx());
        // 노드정보의 인덱스는 초기화하지 않고 이전값을 갖고있도록한다.
        // 패킷을 주고받으면서 인덱스값을 가지고 있어야 한다.
        // 어차피 부하분산 해제는 완료되었으며 노드가 연결을 다시하면
        // 인덱스는 다시 할당되기 때문에 문제가 없다.
    }

    public void logQueueLoads() {
        StringBuilder packetSb = new StringBuilder();
        for (int i = 0; i < this.packetLoads.length(); i++) {
            packetSb.append("[").append(i).append(":").append(this.packetLoads.get(i)).append("] ");
        }

        StringBuilder dbmsSb = new StringBuilder();
        for (int i = 0; i < this.dbmsLoads.length(); i++) {
            dbmsSb.append("[").append(i).append(":").append(this.dbmsLoads.get(i)).append("] ");
        }

        StringBuilder logSb = new StringBuilder();
        for (int i = 0; i < this.logLoads.length(); i++) {
            logSb.append("[").append(i).append(":").append(this.logLoads.get(i)).append("] ");
        }
        log.info("PacketLoads: {} EA. {}", this.packetLoads.length(), packetSb);
        log.info("DbmsLoads: {} EA. {}", this.dbmsLoads.length(), dbmsSb);
        log.info("LogLoads: {} EA. {}", this.logLoads.length(), logSb);
    }
}
