"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;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.networkMode = void 0;
exports.init = init;
exports.reset = reset;
exports.getTxs = getTxs;
exports.dropInvalidTxs = dropInvalidTxs;
exports.updateRecord = updateRecord;
exports.validateRecordTypes = validateRecordTypes;
exports.parseRecord = parseRecord;
exports.queueRequest = queueRequest;
exports.sendRequests = sendRequests;
exports.enterRecovery = enterRecovery;
exports.enterShutdown = enterShutdown;
exports.enterSafety = enterSafety;
exports.enterProcessing = enterProcessing;
exports.isInternalTxAllowed = isInternalTxAllowed;
exports.enterRestore = enterRestore;
const Context = __importStar(require("./Context"));
const Self = __importStar(require("./Self"));
const utils_1 = require("../utils");
const CycleCreator_1 = require("./CycleCreator");
const NodeList = __importStar(require("./NodeList"));
const nestedCounters_1 = require("../utils/nestedCounters");
const logger_1 = require("../logger");
/** STATE */
let p2pLogger;
exports.networkMode = 'forming';
/** ROUTES */
/*
const gossipRoute: P2P.P2PTypes.GossipHandler = () => {
  return
}

const routes = {
  internal: {},
  gossip: {
    gossip: gossipRoute,
  },
}
*/
/** FUNCTIONS */
/** CycleCreator Functions */
/* These functions must be defined by all modules that implement a
     network action like going active, lost node detection, etc.
     These functions are called by CycleCreator
*/
function init() {
    // Init logger
    p2pLogger = Context.logger.getLogger('p2p');
    // Init state
    reset();
    // Register routes
    /*
    for (const [name, handler] of Object.entries(routes.internal)) {
      Comms.registerInternal(name, handler)
    }
    for (const [name, handler] of Object.entries(routes.gossip)) {
      Comms.registerGossipHandler(name, handler)
    }
    */
}
function reset() {
    return;
}
function getTxs() {
    return;
}
function dropInvalidTxs(txs) {
    return;
}
/*
Given the txs and prev cycle record mutate the referenced record
*/
function updateRecord(txs, record, prev) {
    const active = NodeList.activeByIdOrder.length;
    const { initShutdown, forcedMode } = Context.config.p2p;
    // Check for forcedMode and apply it directly if present
    // for forcedMode v1 just allowing safety mode
    const validModes = [
        //'forming',
        //'processing',
        'safety',
        //'recovery',
        //'restart',
        //'restore',
    ];
    if (forcedMode && forcedMode !== '') {
        if (validModes.includes(forcedMode)) {
            /* prettier-ignore */ nestedCounters_1.nestedCountersInstance.countEvent('P2P: forcedMode', forcedMode);
            /* prettier-ignore */ if (logger_1.logFlags.verbose)
                console.log('P2P: Modes: Forced mode to:', forcedMode);
            record.mode = forcedMode;
        }
        else {
            /* prettier-ignore */ nestedCounters_1.nestedCountersInstance.countEvent('P2P: forcedMode:invalid', forcedMode);
            /* prettier-ignore */ if (logger_1.logFlags.verbose)
                console.log('P2P: Modes: Invalid forced mode:', forcedMode);
        }
        return;
    }
    // If Shutdown mode in triggered from Admin/DAO tx
    if (initShutdown) {
        record.mode = 'shutdown';
        return;
    }
    if (prev) {
        //  if the modules have just been swapped last cycle
        if (prev.mode === undefined && prev.safetyMode !== undefined) {
            if (CycleCreator_1.hasAlreadyEnteredProcessing === false) {
                record.mode = 'forming';
            }
            else if (enterProcessing(active)) {
                record.mode = 'processing';
            }
            else if (enterSafety(active)) {
                record.mode = 'safety';
            }
            else if (enterRecovery(active)) {
                record.mode = 'recovery';
            }
            // for all other cases
        }
        else {
            record.mode = prev.mode;
            if (prev.mode === 'forming') {
                if (enterProcessing(active)) {
                    record.mode = 'processing';
                }
            }
            else if (prev.mode === 'processing') {
                if (enterShutdown(active)) {
                    record.mode = 'shutdown';
                }
                else if (enterRecovery(active)) {
                    record.mode = 'recovery';
                }
                else if (enterSafety(active)) {
                    record.mode = 'safety';
                }
            }
            else if (prev.mode === 'safety') {
                if (enterShutdown(active)) {
                    record.mode = 'shutdown';
                }
                else if (enterRecovery(active)) {
                    record.mode = 'recovery';
                }
                else if (enterProcessing(active)) {
                    record.mode = 'processing';
                }
            }
            else if (prev.mode === 'recovery') {
                if (enterShutdown(active)) {
                    record.mode = 'shutdown';
                }
                else if (enterRestore(active + prev.syncing)) {
                    record.mode = 'restore';
                }
            }
            else if (prev.mode === 'shutdown' && Self.isFirst) {
                record.mode = 'restart';
            }
            else if (prev.mode === 'restart') {
                // Use prev.syncing to be sure that new joined nodes in the previous cycle have synced the cycle data before we trigger the `restore` mode to start syncing the state data
                if (enterRestore(prev.syncing)) {
                    record.mode = 'restore';
                }
            }
            else if (prev.mode === 'restore') {
                if (enterProcessing(active)) {
                    record.mode = 'processing';
                }
            }
        }
    }
    else if (Self.isFirst) {
        // If you're the first node
        record.mode = 'forming';
    }
}
function validateRecordTypes(rec) {
    const err = (0, utils_1.validateTypes)(rec, { mode: 's' });
    if (err)
        return err;
    return '';
}
function parseRecord(record) {
    // If the networkMode before updating is 'restart' or 'recovery' and now it's about to change to 'restore', emit 'restore' event
    if ((exports.networkMode === 'restart' || exports.networkMode === 'recovery') && record.mode === 'restore') {
        Self.emitter.emit('restore', record.counter);
    }
    if (exports.networkMode === 'restore' && record.mode === 'processing') {
        Self.setRestartNetwork(false);
    }
    exports.networkMode = record.mode;
    if (exports.networkMode === 'restart' && !Self.isRestartNetwork)
        Self.setRestartNetwork(true);
    return {
        added: [],
        removed: [],
        updated: [],
    };
}
function queueRequest() {
    return;
}
function sendRequests() {
    return;
}
/** Helper Functions */
/* These functions make the code neater and easier to understand
 */
// use baselineNodes for the below functions since it deals with losing nodes and unexpected downsizing
// baselineNodes is the baseline number of nodes required to be in the network to be considered safe
function enterRecovery(activeCount) {
    const threshold = Context.config.p2p.networkBaselineEnabled
        ? Context.config.p2p.baselineNodes
        : Context.config.p2p.minNodes;
    return activeCount < 0.75 * threshold;
}
function enterShutdown(activeCount) {
    const threshold = Context.config.p2p.networkBaselineEnabled
        ? Context.config.p2p.baselineNodes
        : Context.config.p2p.minNodes;
    return activeCount <= 0.3 * threshold;
}
function enterSafety(activeCount) {
    const threshold = Context.config.p2p.networkBaselineEnabled
        ? Context.config.p2p.baselineNodes
        : Context.config.p2p.minNodes;
    return activeCount >= 0.75 * threshold && activeCount < 0.9 * threshold;
}
function enterProcessing(activeCount) {
    /*
    In the future the change from recovery to processing will need to be updated in the recovery project.
    per Andrew, we may want a sticky state that doesn't enter processing until something indicates the data is restored,
    and we may even want the nodes to get to minnodes count before the archivers start patching data
    */
    // use of minNodes instead of baselineNodes since dealing with going into processing mode
    return activeCount >= Context.config.p2p.minNodes;
}
/** An internal tx is allowed to be processed if the network is in one of the modes mentioned in the function */
function isInternalTxAllowed() {
    return ['processing', 'safety', 'forming'].includes(exports.networkMode);
}
function enterRestore(totalNodeCount) {
    // use of baselineNodes since dealing with going into restore mode and baselineNodes is the minimum number of nodes required to go into safety, restore, and recovery mode
    const threshold = Context.config.p2p.networkBaselineEnabled
        ? Context.config.p2p.baselineNodes
        : Context.config.p2p.minNodes;
    return totalNodeCount >= threshold + Context.config.p2p.extraNodesToAddInRestart;
}
//# sourceMappingURL=Modes.js.map