// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
//      Magicbane Emulator Project © 2013 - 2022
//                www.magicbane.com


package engine.net;

import engine.Enum.DispatchChannel;
import engine.objects.ProducedItem;
import org.pmw.tinylog.Logger;

import java.util.HashSet;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.atomic.LongAdder;

/**
 * Thread blocks until MagicBane dispatch messages are
 * enqueued then processes them in FIFO order. The collection
 * is thread safe.
 * <p>
 * Any large messages not time sensitive such as load object
 * sent to more than a single individual should be spawned
 * individually on a DispatchMessageThread.
 */


public enum ItemProductionManager implements Runnable {

    ITEMPRODUCTIONMANAGER;


    // Instance variables

    @SuppressWarnings("unchecked") // Cannot have arrays of generics in java.
    private static final DelayQueue<ItemQueue> producedQueue = new DelayQueue<>();
    public static volatile long[] messageCount = new long[DispatchChannel.values().length];

    // Class variables
    public static LongAdder[] dispatchCount = new LongAdder[DispatchChannel.values().length];


    // Performance metrics
    public static volatile long[] maxRecipients = new long[DispatchChannel.values().length];
    public static LongAdder dispatchPoolSize = new LongAdder();
    public static HashSet<ProducedItem> FailedItems = new HashSet<>();
    public Thread itemProductionThread = null;
    private ItemQueue itemQueue;
    private long nextFailedItemAudit;

    // Thread constructor

    public static void send(ItemQueue item) {

        // Don't queue up empty dispatches!

        if (item == null)
            return;

        producedQueue.add(item);

    }

    public static String getNetstatString() {

        String outString = null;
        String newLine = System.getProperty("line.separator");
        outString = "[LUA_NETSTA()]" + newLine;
        outString += "poolSize: " + dispatchPoolSize.longValue() + '\n';

        for (DispatchChannel dispatchChannel : DispatchChannel.values()) {

            outString += "Channel: " + dispatchChannel.name() + '\n';
            outString += "Dispatches: " + dispatchCount[dispatchChannel.getChannelID()].longValue() + '\n';
            outString += "Messages: " + messageCount[dispatchChannel.getChannelID()] + '\n';
            outString += "maxRecipients: " + maxRecipients[dispatchChannel.getChannelID()] + '\n';
        }
        return outString;
    }

    public void startMessagePump() {

        itemProductionThread = new Thread(this);
        itemProductionThread.setName("ItemProductionManager");

    }

    public void initialize() {
        itemProductionThread.start();
    }

    @Override
    public void run() {


        while (true) {
            try {

                this.itemQueue = producedQueue.take();

                if (this.itemQueue == null) {
                    return;
                }

                if (this.itemQueue != null) {
                    if (this.itemQueue.item == null) {
                        this.itemQueue.release();
                        return;
                    }


                    boolean created = this.itemQueue.item.finishProduction();

                    if (!created)
                        FailedItems.add(this.itemQueue.item);

                    this.itemQueue.release();


                }


            } catch (Exception e) {
                Logger.error(e);
            }

        }
    }

    // For Debugging:
    //Logger.error("MessageDispatcher", messageDispatch.msg.getOpcodeAsString() + " sent to " + messageDispatch.playerList.size() + " players");
}