"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.p2p = exports.P2P = void 0;
const events_1 = require("events");
const utils = __importStar(require("../utils"));
const Apoptosis_1 = require("./Apoptosis");
const Comms = __importStar(require("./Comms"));
const Context_1 = require("./Context");
const CycleChain = __importStar(require("./CycleChain"));
const CycleCreator = __importStar(require("./CycleCreator"));
const NodeList = __importStar(require("./NodeList"));
const Self = __importStar(require("./Self"));
const Utils = __importStar(require("./Utils"));
const logger_1 = require("../logger");
const Join_1 = require("./Join");
const CycleAutoScale = __importStar(require("./CycleAutoScale"));
/* p2p functions */
class P2P extends events_1.EventEmitter {
    constructor() {
        super();
        this.registerInternal = Comms.registerInternal;
        this.registerInternalBinary = Comms.registerInternalBinary;
        this.registerGossipHandler = Comms.registerGossipHandler;
        this.unregisterGossipHandler = Comms.unregisterGossipHandler;
        this.unregisterInternal = Comms.unregisterInternal;
        this.ask = Comms.ask;
        this.askBinary = Comms.askBinary;
        this.tell = Comms.tell;
        this.tellBinary = Comms.tellBinary;
        this.sendGossipIn = Comms.sendGossip;
        this.robustQuery = Utils.robustQuery;
        this.sendGossipAll = Comms.sendGossipAll; //Need this but will try sendGossipIn as a work around
    }
    // Make sure these are copying a reference instead of value
    get isFirstSeed() {
        return Self.isFirst;
    }
    get isActive() {
        return Self.isActive;
    }
    get id() {
        return Self.id;
    }
    getNodeId() {
        return Self.id;
    }
    initApoptosis(message, userFriendlyMessage) {
        // [TODO] - we need to change apoptosizeSelf
        //          currently it tell all the nodes in the network that it is leaving; not practical in large networks
        //          we should gossip this, but origninal gossip is only allowed in Q1 and the node cannot
        //          wait until then.
        //          Need to think about the best way to handle this.
        (0, Apoptosis_1.apoptosizeSelf)(message, userFriendlyMessage);
    }
    allowTransactions() {
        return NodeList.activeByIdOrder.length >= Context_1.config.p2p.minNodesToAllowTxs;
    }
    allowSet() {
        return NodeList.activeByIdOrder.length === 1;
    }
    /*
    ,function allowSet_orig () {
        return this.state.getActiveCount() === 1
    }
    */
    // Propably don't need to do anything; the join module only uses Self.isActive to accept join request
    setJoinRequestToggle(bool) { }
    /*
    ,function setJoinRequestToggle_orig (bool) {
      this.joinRequestToggle = bool
    }
    */
    // goActive() {
    //   nestedCountersInstance.countEvent('p2p', 'goActive')
    //   const activePromise = new Promise<void>((resolve, reject) => {
    //     Self.emitter.on('active', () => resolve())
    //   })
    //   //Active.requestActive()
    //   console.log('return goActive promise...')
    //   return activePromise
    // }
    getLatestCycles(amount) {
        if (CycleChain.cycles.length < amount) {
            return CycleChain.cycles;
        }
        return CycleChain.cycles.slice(0 - amount);
    }
    shutdown() {
        CycleCreator.shutdown();
    }
    configUpdated() {
        //todo, make this nicer!
        CycleAutoScale.configUpdated();
    }
}
exports.P2P = P2P;
exports.p2p = new P2P();
class State extends events_1.EventEmitter {
    getNode(id) {
        return NodeList.nodes.get(id);
    }
    getRemovedNodePubKeyFromCache(id) {
        return NodeList.getRemovedNodePubKeyFromCache(id);
    }
    getNodes() {
        return NodeList.nodes;
    }
    getNodesRequestingJoin() {
        return (0, Join_1.getNodeRequestingJoin)();
    }
    getNodeByPubKey(pubkey) {
        if (NodeList.byPubKey.has(pubkey) !== true) {
            if (logger_1.logFlags.console)
                console.log(`getNodeByPubKey cant find ${utils.makeShortHash(pubkey)} in set: ${utils.stringifyReduce(NodeList.byPubKey.keys)} `);
        }
        return NodeList.byPubKey.get(pubkey);
    }
    // looks like what the original function in p2p-state did is get the active nodes
    //   excluding yourself and returned as an array
    getActiveNodes_orig(id) {
        return getSubsetOfNodeList(NodeList.activeByIdOrder, id);
    }
    getActiveNodes(id) {
        if (id) {
            return Object.values(NodeList.activeOthersByIdOrder);
        }
        else {
            return Object.values(NodeList.activeByIdOrder);
        }
    }
    // The original function in p2p.state just returns an array with all nodes that are syncing excluding self
    //     there is no concept of neighbors
    getOrderedSyncingNeighbors(node) {
        const nodes = NodeList.othersByIdOrder.filter((e) => e.status === 'syncing'); // remove syncing nodes
        return nodes;
    }
    getLastCycle() {
        return CycleChain.newest;
    }
    //save for local debugging
    getCycleByCounter(counter) {
        const i = utils.binarySearch(CycleChain.cycles, { counter }, utils.propComparator('counter'));
        if (i > -1)
            return CycleChain.cycles[i];
        return null;
    }
    //save for local debugging
    getCycleByTimestamp(timestamp) {
        const secondsTs = Math.floor(timestamp * 0.001);
        const i = utils.binarySearch(CycleChain.cycles, secondsTs, (ts, record) => {
            if (ts > record.start + record.duration) {
                return 1;
            }
            if (ts < record.start) {
                return -1;
            }
            return 0;
        });
        if (i > -1)
            return CycleChain.cycles[i];
        return null;
    }
}
const state = new State();
// old way had lastCycle, and time.  but this seems unused now
// Self.emitter.on('cycle_q1_start', (lastCycle: Shardus.Cycle, time: number) => {
//   state.emit('cycle_q1_start', lastCycle, time)
// })
Self.emitter?.on('cycle_q1_start', () => {
    state.emit('cycle_q1_start');
});
Self.emitter?.on('cycle_q2_start', () => {
    state.emit('cycle_q2_start');
});
Self.emitter?.on('cycle_q3_start', () => {
    state.emit('cycle_q3_start');
});
exports.p2p['state'] = state;
/* Listeners */
// this._registerListener(this.p2p.state, 'cycle_q1_start', async (lastCycle: Shardus.Cycle, time:number) => {})
// this._registerListener(this.p2p.state, 'cycle_q2_start', async (lastCycle: Shardus.Cycle, time:number) => {})
// this._registerListener(this.p2p.state, 'cycle_q3_start', async (lastCycle: Shardus.Cycle, time:number) => {})
/* Internal functions */
/* all this does is returns an array of nodes with our node removed from it */
/*    it is given an obj of ids -> nodes */
function getSubsetOfNodeList(nodes, self = null) {
    if (!self)
        return Object.values(nodes);
    // Check if self in node list
    if (!nodes[self]) {
        // stack
        this.mainLogger.warn(`Invalid node ID in 'self' field. Given ID: ${self} : ${new Error().stack}`);
        return Object.values(nodes);
    }
    const nodesCopy = utils.deepCopy(nodes);
    delete nodesCopy[self];
    return Object.values(nodesCopy);
}
//# sourceMappingURL=Wrapper.js.map