"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cfunction_1 = require("../fundamentals/function/cfunction");
const cmodule_1 = require("../fundamentals/type/cmodule");
const cid_1 = require("../cid/cid");
const cliteral_1 = require("../fundamentals/type/cliteral");
const winston = require("winston");
class CTask {
    constructor(name, cfunctionID, _clang, target = null) {
        this.name = name;
        this.cfunctionID = cfunctionID;
        this._clang = _clang;
        this.target = target;
        this._inline = false;
        this.refresh = () => {
            var outFlowNames = this.findOutFlowTags();
            this._flowControl.forEach((ctask, outFlowName, flowControl) => {
                if (outFlowNames.indexOf(outFlowName) != -1) {
                    flowControl.delete(outFlowName);
                }
            });
            for (var flowName of outFlowNames) {
                if (!this._flowControl.has(flowName)) {
                    this._flowControl.set(flowName, new Array());
                }
            }
            this._inline = this._flowControl.size != 0;
            var fctParameters = this.getCFunction().getParameters();
            for (var declaration of fctParameters) {
                if (this._parametersAssignment.has(declaration.name)) {
                    this._parametersAssignment.delete(declaration.name);
                }
            }
        };
        this._next = new Array();
        this._flowControl = new Map();
        this._parametersAssignment = new Map();
        this._cid = cid_1.CID.GetNewCID();
        this.refresh();
    }
    get CID() {
        return this._cid;
    }
    get next() {
        return this._next;
    }
    get flowControl() {
        return this._flowControl;
    }
    get parametersAssignment() {
        return this._parametersAssignment;
    }
    get returnValueAssignment() { return this._returnValueAssignment; }
    set returnValueAssignment(returnValueAssignment) {
        var cfunction = this.getCFunction();
        if (returnValueAssignment.getTargetDeclaration().canBeAssignedWith(cfunction.returnType)) {
            this._returnValueAssignment = returnValueAssignment;
        }
    }
    get inline() {
        return this._inline;
    }
    validate(issues) {
        var cfunction = this.getCFunction();
        var parameters = cfunction.getParameters();
        this.validateAssignments(parameters, issues);
        if (this._inline) {
            if (this.target != null) {
                issues.push("ctask is invalid, cannot inline function when it should be called on a target module.");
            }
            if (cfunction.usingTemplate()) {
                issues.push("cfunction is invalid, cannot inline function when it using templates :" + cfunction.name);
            }
            if (cfunction.getIncludes().length != 0) {
                issues.push("cfunction is invalid, cannot inline CPP function when it has includes for its body to compile :" + cfunction.name);
            }
            if (cfunction.returnType != cliteral_1.CLiteral.cvoid) {
                issues.push("cfunction cannot be inlined when return type is not void" + cfunction.name);
            }
        }
        return issues.length == 0;
    }
    validateAssignment(parameterName, variableToUse, fctParameters, outMsgs) {
        var correspondingParameter = fctParameters.filter((parameter) => {
            return parameter.name == parameterName;
            ;
        });
        if (correspondingParameter.length == 0) {
            outMsgs.push("Could not find any matching function parameter for assignment.");
            return false;
        }
        else if (correspondingParameter.length > 1) {
            outMsgs.push("Found too many function parameters for assignment.");
            return false;
        }
        else if (!variableToUse.getTargetDeclaration().canAssignTo(correspondingParameter[0])) {
            outMsgs.push("Trying to assign a variable which type doesn't math the function parameter's type.");
            return false;
        }
        return true;
    }
    validateAssignments(fctParameters, outMsgs) {
        var success = true;
        this._parametersAssignment.forEach((variableToUse, parameter) => {
            success = success && this.validateAssignment(parameter, variableToUse, fctParameters, outMsgs);
        });
        if (this._parametersAssignment.size != fctParameters.length) {
            outMsgs.push("Task parameters assignment do not match the number of parameters of the function. task : " + this.name);
            success = false;
        }
        return success;
    }
    setParameterAssignment(parameterName, variableToUse) {
        var fctParameters = this.getCFunction().getParameters().filter((dcl) => { return dcl.name == parameterName; });
        var msgs = new Array();
        if (this.validateAssignment(parameterName, variableToUse, fctParameters, msgs)) {
            this._parametersAssignment.set(parameterName, variableToUse);
            return true;
        }
        else {
            return false;
        }
    }
    navigateTaskGraph(functor, visitedTasks = null) {
        let results = [];
        if (visitedTasks == null) {
            visitedTasks = new Array();
        }
        if (visitedTasks.indexOf(this) != -1) {
            return;
        }
        results.concat(functor(this));
        visitedTasks.push(this);
        this._flowControl.forEach((tasks, outFlowName, flowControl) => {
            for (var task of tasks) {
                results.concat(task.navigateTaskGraph(functor, visitedTasks));
            }
        });
        for (var task of this._next) {
            results.concat(task.navigateTaskGraph(functor, visitedTasks));
        }
        return results;
    }
    findOutFlowTags() {
        return this.getCFunction().flowControlTags;
    }
    getOutFlowTags() {
        var result = new Array();
        this._flowControl.forEach((tasks, flowTag) => {
            result.push(flowTag);
        });
        return result;
    }
    addTaskToFlowControl(flowTag, task) {
        if (this._flowControl.has(flowTag)) {
            this._flowControl.get(flowTag).push(task);
        }
    }
    addNextTask(task) {
        this._next.push(task);
    }
    getCFunction() {
        if (this.target != null) {
            var ctype = this.target.getTargetDeclaration().ctype;
            if (!(ctype instanceof (cmodule_1.CModule))) {
                winston.log('error', "Task function's target is not a module. This is invalid.");
                return null;
            }
            else {
                var cmodule = (ctype);
                return cmodule.getCFunction(this.cfunctionID);
            }
        }
        else {
            var ctype = this._clang.getCType(this.cfunctionID);
            if (!(ctype instanceof (cfunction_1.CFunction))) {
                winston.log('error', "Task function's id isn't referencing a valid function.");
                return null;
            }
            return ctype;
        }
    }
}
exports.CTask = CTask;
//# sourceMappingURL=ctask.js.map