"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.getCodeActions = getCodeActions;
exports.getAvailableCommands = getAvailableCommands;
exports.executeCommand = executeCommand;
const checker_js_1 = require("../checker.js");
const index_js_1 = require("../index.js");
const ChangeAllOtherEdgeOpsAndFixGraphCommand = __importStar(require("./command/ChangeAllOtherEdgeOpsAndFixGraphCommand.js"));
const ChangeEdgeOpCommand = __importStar(require("./command/ChangeEdgeOpCommand.js"));
const ConsolidateDescendantsCommand = __importStar(require("./command/ConsolidateDescendantsCommand.js"));
const RemoveSemicolonsCommand = __importStar(require("./command/RemoveSemicolons.js"));
const common_js_1 = require("./command/common.js");
const util_js_1 = require("./util.js");
function getCodeActions(doc, sourceFile, range, _context) {
    let actions = getActionsFromDiagnostics(doc, sourceFile, range);
    const general = getGeneralRefactorings(doc, sourceFile, range);
    if (general) {
        if (!actions)
            actions = general;
        else
            actions.push.apply(actions, general);
    }
    return actions;
}
function getActionsFromDiagnostics(doc, file, range) {
    const ds = file.diagnostics;
    if (!ds || ds.length === 0)
        return undefined;
    const rangeStartOffset = doc.offsetAt(range.start);
    const rangeEndOffset = doc.offsetAt(range.end);
    const res = [];
    for (const d of ds) {
        if (isInRange(rangeStartOffset, rangeEndOffset, d.start, d.end)) {
            const commands = getCommandsForDiagnostic(doc, file, d);
            if (commands && commands.length > 0)
                res.push.apply(res, commands);
        }
    }
    return res.length === 0 ? undefined : res;
}
function getGeneralRefactorings(doc, file, range) {
    if (!file.graph)
        return undefined;
    const g = file.graph;
    const kw = g.keyword;
    const rangeStartOffset = doc.offsetAt(range.start);
    const rangeEndOffset = doc.offsetAt(range.end);
    const keywordStart = (0, util_js_1.getStart)(file, kw);
    const res = [];
    if (isInRange(rangeStartOffset, rangeEndOffset, keywordStart, kw.end)) {
        if (!subtreeContainsErrors(g)) {
            const oppositeGraphType = (0, common_js_1.getOppositeKind)(kw.kind);
            const convertGraph = convertGraphTypeCommand(file, g, oppositeGraphType);
            res.push(convertGraph);
        }
    }
    if (rangeStartOffset === rangeEndOffset) {
        const candidates = [];
        let clickedNode = (0, checker_js_1.findNodeAtOffset)(g, rangeStartOffset);
        if (clickedNode && !!clickedNode.parent) {
            if (clickedNode.kind === index_js_1.SyntaxKind.SemicolonToken) {
                res.push(RemoveSemicolonsCommand.create());
            }
            if ((0, index_js_1.isIdentifierNode)(clickedNode)) {
                clickedNode = clickedNode.parent;
            }
            const clickedEdgeStatement = clickedNode.parent;
            if (clickedEdgeStatement && !subtreeContainsErrors(clickedEdgeStatement)) {
                if ((0, checker_js_1.isEdgeStatement)(clickedEdgeStatement) &&
                    clickedEdgeStatement.rhs.length === 1 &&
                    (0, checker_js_1.isNodeId)(clickedEdgeStatement.source) &&
                    !(0, checker_js_1.edgeStatementHasAttributes)(clickedEdgeStatement)) {
                    candidates.push(clickedEdgeStatement);
                    const source = clickedEdgeStatement.source;
                    const sourceText = (0, checker_js_1.getIdentifierText)(source.id);
                    const graphParent = clickedEdgeStatement.parent;
                    if (graphParent) {
                        let hasVisitedStatement = false;
                        let hasVisitedNodeModifier = false;
                        (0, index_js_1.forEachChild)(graphParent, statement => {
                            if (statement === clickedEdgeStatement) {
                                hasVisitedStatement = true;
                                return undefined;
                            }
                            if (hasVisitedNodeModifier) {
                                return;
                            }
                            if (hasVisitedStatement) {
                                if ((0, checker_js_1.isAttrStatement)(statement) ||
                                    subtreeContainsErrors(statement)) {
                                    hasVisitedNodeModifier = true;
                                    return true;
                                }
                            }
                            if (hasVisitedStatement) {
                                if ((0, checker_js_1.isEdgeStatement)(statement) &&
                                    statement.rhs.length === 1 &&
                                    !(0, checker_js_1.edgeStatementHasAttributes)(statement)) {
                                    const statementSource = statement.source;
                                    if ((0, checker_js_1.isNodeId)(statementSource)) {
                                        const lowerSourceText = (0, checker_js_1.getIdentifierText)(statementSource.id);
                                        if (sourceText === lowerSourceText) {
                                            candidates.push(statement);
                                        }
                                    }
                                }
                            }
                            return undefined;
                        });
                    }
                }
                if (candidates.length > 1) {
                    const action = ConsolidateDescendantsCommand.create(candidates, true);
                    res.push(action);
                }
            }
        }
    }
    return res.length === 0 ? undefined : res;
}
function getCommandsForDiagnostic(doc, file, d) {
    switch (d.code.source) {
        case 1:
            return getScannerErrorCommand(doc, file, d, d.code);
        case 2:
            return getParserErrorCommand(doc, file, d, d.code);
        case 4:
            return getCheckerErrorCommand(doc, file, d, d.code);
        default:
            return (0, util_js_1.assertNever)(d.code);
    }
}
function getScannerErrorCommand(_doc, _file, d, code) {
    console.assert(d.code.source === 1);
    console.assert(code.source === 1);
    return undefined;
}
function getParserErrorCommand(_doc, _file, d, code) {
    console.assert(d.code.source === 2);
    console.assert(code.source === 2);
    return undefined;
}
function getCheckerErrorCommand(_doc, file, d, code) {
    console.assert(d.code.source === 4);
    console.assert(code.source === 4);
    switch (code.sub) {
        case 0: {
            const graph = file.graph;
            if (!graph)
                return undefined;
            const allowedOp = (0, checker_js_1.getAllowedEdgeOperation)(graph);
            const wrongOp = (0, common_js_1.getOppositeEdgeOp)(allowedOp);
            const kwk = graph.keyword.kind;
            const fixSingleEdge = ChangeEdgeOpCommand.create(d.start, d.end, allowedOp, wrongOp);
            const fixAll = convertGraphTypeCommand(file, graph, kwk);
            const convertToThisWrongType = convertGraphTypeCommand(file, graph, (0, common_js_1.getOppositeKind)(kwk));
            return [fixSingleEdge, fixAll, convertToThisWrongType];
        }
        case 1:
            return undefined;
    }
}
function convertGraphTypeCommand(file, graph, changeToGraphType) {
    const changeToEdgeOp = (0, common_js_1.getAllowedOp)(changeToGraphType);
    const allEdges = (0, checker_js_1.findAllEdges)(graph);
    const edgeOffsets = allEdges
        .filter(e => e.operation.kind !== changeToEdgeOp)
        .map(e => ({
        start: (0, util_js_1.getStart)(file, e.operation),
        end: e.operation.end,
    }));
    const graphTypeOffset = {
        start: (0, util_js_1.getStart)(file, graph.keyword),
        end: graph.keyword.end,
    };
    return ChangeAllOtherEdgeOpsAndFixGraphCommand.create(edgeOffsets, changeToEdgeOp, graphTypeOffset, graph.keyword.kind, changeToGraphType);
}
function isInRange(rangeStartOffset, rangeEndOffset, startOffset, endOffset) {
    if (rangeStartOffset === rangeEndOffset)
        return startOffset <= rangeStartOffset && rangeEndOffset <= endOffset;
    if (rangeStartOffset === startOffset && rangeEndOffset === endOffset)
        return true;
    return false;
}
const commandHandlers = {
    ["DOT.changeEdgeOp"]: ChangeEdgeOpCommand.execute,
    ["DOT.convertGraphType"]: ChangeAllOtherEdgeOpsAndFixGraphCommand.execute,
    ["DOT.consolidateDescendants"]: ConsolidateDescendantsCommand.execute,
    ["DOT.removeSemicolons"]: RemoveSemicolonsCommand.execute,
};
function getAvailableCommands() {
    return Object.keys(commandHandlers);
}
function executeCommand(doc, sourceFile, cmd) {
    const handler = commandHandlers[cmd.command];
    return handler === undefined ? undefined : handler(doc, sourceFile, cmd);
}
function subtreeContainsErrors(node) {
    if ((0, checker_js_1.nodeContainsErrors)(node))
        return true;
    let hasError = false;
    (0, index_js_1.forEachChild)(node, child => {
        if ((0, checker_js_1.nodeContainsErrors)(child)) {
            hasError = true;
        }
        if (!hasError) {
            hasError = subtreeContainsErrors(child);
        }
    });
    return hasError;
}
//# sourceMappingURL=codeAction.js.map