import { isNullOrUndefined } from "../util";
import { createLinkedList } from "../linkedList";
import { sendMessagePasserMessage, createAsyncGetProxy, } from "../messagePasser";
import { createExternallyResolvablePromise, } from "../externalPromise";
import { createWorkerClient } from "./createWorkerClient";
export var createWorkerFarm = function (createWorker, _a) {
    var _b = _a === void 0 ? {} : _a, _c = _b.maxWorkerCount, maxWorkerCount = _c === void 0 ? 2 : _c, _d = _b.spawnBehaviour, spawnBehaviour = _d === void 0 ? "lazy" : _d;
    if (isNullOrUndefined(maxWorkerCount) || maxWorkerCount <= 0) {
        throw new Error("Can not spawn workers due to an invalid max worker count \"" + maxWorkerCount + "\"");
    }
    if (maxWorkerCount === 1) {
        return createWorkerClient(createWorker());
    }
    var workQueue = createLinkedList();
    var workerInstanceManager = createWorkerInstanceManager(createWorker, {
        maxWorkerCount: maxWorkerCount,
        spawnBehaviour: spawnBehaviour,
    });
    var handleTaskRequest = function (propertyKey, args) {
        var worker = workerInstanceManager.getFreeWorkerInstance();
        var item = {
            method: propertyKey,
            args: args,
            promise: createExternallyResolvablePromise(),
        };
        if (worker !== undefined) {
            processItem(worker, item);
        }
        else {
            workQueue.push(item);
        }
        return item.promise.promise;
    };
    var processItem = function (instance, item) {
        instance.status = "busy";
        var workerJobPromise = sendMessagePasserMessage(instance.worker, item.method, item.args);
        item.promise.fromPromise(workerJobPromise);
        workerJobPromise.then(function () {
            var nextItem = workQueue.shift();
            if (nextItem === undefined) {
                instance.status = "idle";
                return;
            }
            processItem(instance, nextItem);
        });
    };
    return createAsyncGetProxy(handleTaskRequest);
};
var createWorkerInstanceManager = function (createWorker, _a) {
    var maxWorkerCount = _a.maxWorkerCount, spawnBehaviour = _a.spawnBehaviour;
    var workers = [];
    var createWorkerInstance = function () {
        var worker = {
            worker: createWorker(),
            status: "idle",
        };
        workers.push(worker);
        return worker;
    };
    var getFreeWorkerInstance = function () {
        for (var index = 0; index < maxWorkerCount; index++) {
            var worker = workers[index];
            if (worker === undefined) {
                return createWorkerInstance();
            }
            if (worker.status === "idle") {
                return worker;
            }
        }
        return undefined;
    };
    if (spawnBehaviour === "eager") {
        for (var index = 0; index < maxWorkerCount; index++) {
            createWorkerInstance();
        }
    }
    return {
        getFreeWorkerInstance: getFreeWorkerInstance,
    };
};
