"use strict";
exports.__esModule = true;
var ts = require("typescript");
var path = require("path");
var heading = require("mdast-util-heading-range");
var remark = require("remark");
var unist = require("../unistHelpers");
var typescript_1 = require("typescript");
function initPhase(aggData) {
}
exports.initPhase = initPhase;
function readPhase(tree, pathname, aggData) {
}
exports.readPhase = readPhase;
function aggPhase(aggData) {
}
exports.aggPhase = aggPhase;
var PropData = /** @class */ (function () {
    function PropData() {
    }
    return PropData;
}());
var ParamData = /** @class */ (function () {
    function ParamData() {
    }
    ParamData.prototype.getSignature = function () {
        var sig = this.name;
        if (this.optional)
            sig += "?";
        if (this.type)
            sig += ": " + this.type;
        if (this.initializer)
            sig += " = " + this.initializer;
        return sig;
    };
    return ParamData;
}());
var MethodData = /** @class */ (function () {
    function MethodData() {
        this.params = [];
    }
    MethodData.prototype.getSignature = function () {
        var sig = this.name + "(";
        if (this.params.length > 0) {
            sig += this.params[0].getSignature();
        }
        for (var i = 1; i < this.params.length; i++) {
            sig += ", " + this.params[i].getSignature();
        }
        sig += ")";
        if (this.returnType !== "void") {
            sig += ": " + this.returnType;
        }
        return sig;
    };
    return MethodData;
}());
var ComponentDocAutoContent = /** @class */ (function () {
    function ComponentDocAutoContent() {
        this.inputs = [];
        this.outputs = [];
    }
    ComponentDocAutoContent.prototype.extractClassInfoFromSource = function (checker, classDec) {
        var sourceFile = classDec.getSourceFile();
        for (var i = 0; i < classDec.members.length; i++) {
            var member = classDec.members[i];
            if (ts.isPropertyDeclaration(member) ||
                ts.isGetAccessorDeclaration(member) ||
                ts.isSetAccessorDeclaration(member)) {
                var prop = member;
                var mods = ts.getCombinedModifierFlags(prop);
                var nonPrivate = (mods & ts.ModifierFlags.Private) === 0;
                var memSymbol = checker.getSymbolAtLocation(prop.name);
                if (nonPrivate && memSymbol && prop.decorators) {
                    var name_1 = memSymbol.getName();
                    var initializer = "";
                    if (prop.initializer) {
                        initializer = prop.initializer.getText(sourceFile);
                    }
                    var doc = ts.displayPartsToString(memSymbol.getDocumentationComment(checker));
                    doc = doc.replace(/\r\n/g, " ");
                    var propType = checker.typeToString(checker.getTypeOfSymbolAtLocation(memSymbol, memSymbol.valueDeclaration));
                    var dec = prop.decorators[0].getText(sourceFile);
                    if (dec.match(/@Input/)) {
                        this.inputs.push({
                            "name": name_1,
                            "type": propType,
                            "initializer": initializer,
                            "docText": doc
                        });
                    }
                    else if (dec.match(/@Output/)) {
                        this.outputs.push({
                            "name": name_1,
                            "type": propType,
                            "initializer": "",
                            "docText": doc
                        });
                    }
                }
            }
        }
    };
    ComponentDocAutoContent.prototype.addContentToDoc = function (tree) {
        var inTable = buildPropsTable(this.inputs);
        var outTable = buildPropsTable(this.outputs, false);
        if (inTable) {
            heading(tree, "Properties", function (before, section, after) {
                return [before, inTable, after];
            });
        }
        if (outTable) {
            heading(tree, "Events", function (before, section, after) {
                return [before, outTable, after];
            });
        }
    };
    return ComponentDocAutoContent;
}());
var ServiceDocAutoContent = /** @class */ (function () {
    function ServiceDocAutoContent() {
        this.props = [];
        this.methods = [];
    }
    ServiceDocAutoContent.prototype.extractClassInfoFromSource = function (checker, classDec) {
        var sourceFile = classDec.getSourceFile();
        for (var i = 0; i < classDec.members.length; i++) {
            var member = classDec.members[i];
            if (ts.isMethodDeclaration(member)) {
                var method = member;
                var mods = ts.getCombinedModifierFlags(method);
                var nonPrivate = (mods & ts.ModifierFlags.Private) === 0;
                var memSymbol = checker.getSymbolAtLocation(method.name);
                if (nonPrivate && memSymbol) {
                    var methData = new MethodData();
                    methData.name = memSymbol.getName();
                    var doc = ts.displayPartsToString(memSymbol.getDocumentationComment());
                    if (!doc)
                        console.log("Warning: Method " + classDec.name.escapedText + "." + methData.name + " is not documented");
                    methData.docText = doc.replace(/\r\n/g, " ");
                    var sig = checker.getSignatureFromDeclaration(method);
                    var returnType = sig.getReturnType();
                    methData.returnType = checker.typeToString(returnType);
                    var returnSymbol = returnType.getSymbol();
                    var params = method.parameters;
                    for (var p = 0; p < params.length; p++) {
                        var pData = new ParamData();
                        pData.name = params[p].name.getText();
                        if (params[p].type)
                            pData.type = params[p].type.getText();
                        else
                            pData.type = "";
                        var paramSymbol = checker.getSymbolAtLocation(params[p].name);
                        pData.docText = ts.displayPartsToString(paramSymbol.getDocumentationComment());
                        if (!pData.docText)
                            console.log("Warning: Parameter \"" + pData.name + "\" of " + classDec.name.escapedText + "." + methData.name + " is not documented");
                        pData.optional = params[p].questionToken ? true : false;
                        if (params[p].initializer) {
                            var initText = params[p].initializer.getText();
                            if (initText !== "undefined")
                                pData.initializer = initText;
                        }
                        methData.params.push(pData);
                    }
                    this.methods.push(methData);
                }
            }
        }
    };
    ServiceDocAutoContent.prototype.addContentToDoc = function (tree) {
        var propsTable = buildPropsTable(this.props);
        var methodsList = buildMethodsList(this.methods);
        if (propsTable) {
            heading(tree, "Properties", function (before, section, after) {
                return [before, propsTable, after];
            });
        }
        if (methodsList) {
            heading(tree, "Methods", function (before, section, after) {
                return [before, methodsList, after];
            });
        }
    };
    return ServiceDocAutoContent;
}());
function updatePhase(tree, pathname, aggData) {
    var fileNameNoSuffix = path.basename(pathname, ".md");
    var itemType = fileNameNoSuffix.match(/component|directive|service/);
    if (itemType) {
        var srcData = aggData.srcData[fileNameNoSuffix];
        if (srcData) {
            var srcPath = srcData.path;
            var className = fixAngularFilename(fileNameNoSuffix);
            var classData = void 0;
            if ((itemType[0] === "component") || (itemType[0] === "directive")) {
                classData = new ComponentDocAutoContent();
            }
            else if (itemType[0] === "service") {
                classData = new ServiceDocAutoContent();
            }
            getDocSourceData(path.resolve(".", srcPath), className, classData);
            classData.addContentToDoc(tree);
        }
        return true;
    }
    else {
        return false;
    }
}
exports.updatePhase = updatePhase;
function initialCap(str) {
    return str[0].toUpperCase() + str.substr(1);
}
function fixAngularFilename(rawName) {
    var name = rawName.replace(/\]|\(|\)/g, '');
    var fileNameSections = name.split('.');
    var compNameSections = fileNameSections[0].split('-');
    var outCompName = '';
    for (var i = 0; i < compNameSections.length; i++) {
        outCompName = outCompName + initialCap(compNameSections[i]);
    }
    var itemTypeIndicator = '';
    if (fileNameSections.length > 1) {
        itemTypeIndicator = initialCap(fileNameSections[1]);
    }
    var finalName = outCompName + itemTypeIndicator;
    return finalName;
}
function getDocSourceData(srcPath, docClassName, classData) {
    var prog = ts.createProgram([srcPath], {
        target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS
    });
    var sourceFiles = prog.getSourceFiles();
    var checker = prog.getTypeChecker();
    for (var i = 0; i < sourceFiles.length; i++) {
        if (!sourceFiles[i].isDeclarationFile)
            ts.forEachChild(sourceFiles[i], visit);
    }
    function visit(node) {
        if (!isNodeExported(node))
            return;
        if (ts.isClassDeclaration(node) && node.name) {
            var classDec = node;
            var sourceFile = classDec.getSourceFile();
            if (classDec.name.escapedText === docClassName) {
                getPropDataFromClassChain(checker, classDec, classData);
            }
        }
    }
}
// Get properties/events from main class and all inherited classes.
function getPropDataFromClassChain(checker, classDec, classData) {
    // Main class
    classData.extractClassInfoFromSource(checker, classDec);
    // Inherited classes
    if (classDec.heritageClauses) {
        for (var _i = 0, _a = classDec.heritageClauses; _i < _a.length; _i++) {
            var hc = _a[_i];
            var hcType = checker.getTypeFromTypeNode(hc.types[0]);
            for (var _b = 0, _c = hcType.symbol.declarations; _b < _c.length; _b++) {
                var dec = _c[_b];
                if (typescript_1.isClassDeclaration(dec)) {
                    getPropDataFromClassChain(checker, dec, classData);
                }
            }
        }
    }
}
function buildPropsTable(props, includeInitializer) {
    if (includeInitializer === void 0) { includeInitializer = true; }
    if (props.length === 0) {
        return null;
    }
    var headerCells = [
        unist.makeTableCell([unist.makeText("Name")]),
        unist.makeTableCell([unist.makeText("Type")])
    ];
    if (includeInitializer)
        headerCells.push(unist.makeTableCell([unist.makeText("Default value")]));
    headerCells.push(unist.makeTableCell([unist.makeText("Description")]));
    var rows = [
        unist.makeTableRow(headerCells)
    ];
    for (var i = 0; i < props.length; i++) {
        var pName = props[i].name;
        var pType = props[i].type;
        var pDefault = props[i].initializer || "";
        var pDesc = props[i].docText || "";
        if (pDesc) {
            pDesc = pDesc.replace(/[\n\r]+/, " ");
        }
        var descCellContent = remark().parse(pDesc).children;
        var defaultCellContent;
        if (pDefault) {
            defaultCellContent = unist.makeInlineCode(pDefault);
        }
        else {
            defaultCellContent = unist.makeText("");
        }
        var cells = [
            unist.makeTableCell([unist.makeText(pName)]),
            unist.makeTableCell([unist.makeInlineCode(pType)])
        ];
        if (includeInitializer)
            cells.push(unist.makeTableCell([defaultCellContent]));
        cells.push(unist.makeTableCell(descCellContent));
        rows.push(unist.makeTableRow(cells));
    }
    var spacers = [null, null, null];
    if (includeInitializer)
        spacers.push(null);
    return unist.makeTable(spacers, rows);
}
function buildMethodsList(methods) {
    if (methods.length === 0)
        return null;
    var listItems = [];
    for (var _i = 0, methods_1 = methods; _i < methods_1.length; _i++) {
        var method = methods_1[_i];
        var itemLines = [];
        itemLines.push(unist.makeInlineCode(method.getSignature()));
        itemLines.push(unist.makeBreak());
        itemLines.push(unist.makeParagraph(remark().parse(method.docText).children));
        itemLines.push(unist.makeBreak());
        var paramListItems = [];
        for (var _a = 0, _b = method.params; _a < _b.length; _a++) {
            var param = _b[_a];
            var currParamSections = [];
            if (param.docText !== "") {
                currParamSections.push(unist.makeInlineCode(param.name));
                var optionalPart = param.optional ? "(Optional) " : "";
                currParamSections.push(unist.makeText(" - " + optionalPart + param.docText));
                //currParamSections.push(unist.makeBreak());
                paramListItems.push(unist.makeListItem(unist.makeParagraph(currParamSections)));
            }
        }
        itemLines.push(unist.makeListUnordered(paramListItems));
        listItems.push(unist.makeListItem(unist.makeParagraph(itemLines)));
    }
    return unist.makeListUnordered(listItems);
}
function isNodeExported(node) {
    return (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export) !== 0 || (!!node.parent && node.parent.kind === ts.SyntaxKind.SourceFile);
}
