"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 () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const utils = __importStar(require("../utils"));
const nestedCounters_1 = require("../utils/nestedCounters");
const logger_1 = require("../logger");
const shardFunctions_1 = __importDefault(require("./shardFunctions"));
const NodeList_1 = require("../p2p/NodeList");
/**
 * Help provide a list of nodes that can be used to ask for data
 * Intialize the list and use the dataSourceNode
 * If you need another node to talk to call tryNextDataSourceNode
 */
class DataSourceHelper {
    constructor(stateManager) {
        this.stateManager = stateManager;
    }
    initWithList(listOfNdoes) {
        this.dataSourceNodeIndex = 0;
        this.dataSourceNode = listOfNdoes[this.dataSourceNodeIndex]; // Todo random index
        this.dataSourceNodeList = [...listOfNdoes];
    }
    initByRange(lowAddress, highAddress) {
        this.dataSourceNodeIndex = 0;
        this.dataSourceNodeList = [];
        if (this.stateManager.currentCycleShardData == null) {
            /* prettier-ignore */ if (logger_1.logFlags.error)
                this.stateManager.mainLogger.error(`getDataSourceNode: initByRange currentCycleShardData == null`);
            return;
        }
        const queryLow = lowAddress;
        const queryHigh = highAddress;
        const centerNode = shardFunctions_1.default.getCenterHomeNode(this.stateManager.currentCycleShardData.shardGlobals, this.stateManager.currentCycleShardData.parititionShardDataMap, lowAddress, highAddress);
        if (centerNode == null) {
            if (logger_1.logFlags.error)
                this.stateManager.mainLogger.error(`getDataSourceNode: centerNode not found`);
            return;
        }
        let nodes = shardFunctions_1.default.getNodesByProximity(this.stateManager.currentCycleShardData.shardGlobals, this.stateManager.currentCycleShardData.nodes, centerNode.ourNodeIndex, this.stateManager.p2p.id, 40);
        const nodesInProximity = nodes.length;
        nodes = nodes.filter(this.removePotentiallyRemovedNodes);
        const filteredNodes1 = nodes.length;
        const filteredNodes = [];
        for (const node of nodes) {
            const nodeShardData = this.stateManager.currentCycleShardData.nodeShardDataMap.get(node.id);
            if (nodeShardData != null) {
                if (shardFunctions_1.default.testAddressInRange(queryLow, nodeShardData.consensusPartitions) === false) {
                    /* prettier-ignore */ if (logger_1.logFlags.error)
                        this.stateManager.mainLogger.error(`node cant fit range: queryLow:${queryLow}  ${utils.stringifyReduce(nodeShardData.consensusPartitions)}  `);
                    continue;
                }
                if (shardFunctions_1.default.testAddressInRange(queryHigh, nodeShardData.consensusPartitions) === false) {
                    /* prettier-ignore */ if (logger_1.logFlags.error)
                        this.stateManager.mainLogger.error(`node cant fit range: queryHigh:${queryHigh}  ${utils.stringifyReduce(nodeShardData.consensusPartitions)}  `);
                    continue;
                }
                filteredNodes.push(node);
            }
        }
        nodes = filteredNodes;
        const filteredNodes2 = nodes.length;
        if (nodes.length > 0) {
            this.dataSourceNodeList = nodes;
            this.dataSourceNode = nodes[Math.floor(Math.random() * nodes.length)];
            //this next line is not an error: comment it out later
            if (logger_1.logFlags.error)
                this.stateManager.mainLogger.error(`data source nodes found: ${nodes.length}  nodesInProximity:${nodesInProximity} filteredNodes1:${filteredNodes1} filteredNodes2:${filteredNodes2} `);
        }
        else {
            /* prettier-ignore */ if (logger_1.logFlags.error)
                this.stateManager.mainLogger.error(`no data source nodes found nodesInProximity:${nodesInProximity} filteredNodes1:${filteredNodes1} filteredNodes2:${filteredNodes2} `);
        }
    }
    /**
     * tryNextDataSourceNode
     * @param debugString
     */
    tryNextDataSourceNode(debugString) {
        this.dataSourceNodeIndex++;
        /* prettier-ignore */ if (logger_1.logFlags.error)
            this.stateManager.mainLogger.error(`tryNextDataSourceNode ${debugString} try next node: ${this.dataSourceNodeIndex}`);
        if (this.dataSourceNodeIndex >= this.dataSourceNodeList.length) {
            /* prettier-ignore */ if (logger_1.logFlags.error)
                this.stateManager.mainLogger.error(`tryNextDataSourceNode ${debugString} ran out of nodes ask for data`);
            this.dataSourceNodeIndex = 0;
            /* prettier-ignore */ nestedCounters_1.nestedCountersInstance.countEvent('sync', `tryNextDataSourceNode Out of tries: ${this.dataSourceNodeIndex} of ${this.dataSourceNodeList.length} `, 1);
            return false;
        }
        // pick new data source node
        this.dataSourceNode = this.dataSourceNodeList[this.dataSourceNodeIndex];
        if (this.dataSourceNode == null) {
            /* prettier-ignore */ nestedCounters_1.nestedCountersInstance.countEvent('sync', `tryNextDataSourceNode next try: ${this.dataSourceNodeIndex} of ${this.dataSourceNodeList.length} NODE==null`, 1);
            return false;
        }
        /* prettier-ignore */ if (logger_1.logFlags.error)
            this.stateManager.mainLogger.error(`tryNextDataSourceNode ${debugString} found: ${this.dataSourceNode.externalIp} ${this.dataSourceNode.externalPort} `);
        /* prettier-ignore */ nestedCounters_1.nestedCountersInstance.countEvent('sync', `tryNextDataSourceNode next try: ${this.dataSourceNodeIndex} of ${this.dataSourceNodeList.length}`, 1);
        return true;
    }
    tryRestartList(debugString) {
        this.dataSourceNodeIndex = 0;
        const numNodes = this.dataSourceNodeList.length;
        //allow a list restart if we have a small number of nodes
        if (numNodes > 3) {
            return false;
        }
        /* prettier-ignore */ nestedCounters_1.nestedCountersInstance.countEvent('sync', `DataSourceHelper restartList ${debugString} numnodes:${numNodes}`, 1);
        if (numNodes > 0) {
            this.dataSourceNode = this.dataSourceNodeList[this.dataSourceNodeIndex];
            return true;
        }
        return false;
    }
    getNumNodes() {
        return this.dataSourceNodeList.length;
    }
    removePotentiallyRemovedNodes(node) {
        return NodeList_1.potentiallyRemoved.has(node.id) != true;
    }
}
exports.default = DataSourceHelper;
//# sourceMappingURL=DataSourceHelper.js.map