/*
 This file is part of TALER
 (C) 2016 GNUnet e.V.
 TALER is free software; you can redistribute it and/or modify it under the
 terms of the GNU General Public License as published by the Free Software
 Foundation; either version 3, or (at your option) any later version.
 TALER is distributed in the hope that it will be useful, but WITHOUT ANY
 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 You should have received a copy of the GNU General Public License along with
 TALER; see the file COPYING.  If not, see 
 */
/**
 * Generate .po file from list of source files.
 *
 * Note that duplicate message IDs are NOT merged, to get the same output as
 * you would from xgettext, just run msguniq.
 *
 * @author Florian Dold
 */
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var fs_1 = require("fs");
var ts = require("typescript");
function wordwrap(str, width) {
    if (width === void 0) { width = 80; }
    var regex = '.{1,' + width + '}(\\s|$)|\\S+(\\s|$)';
    return str.match(RegExp(regex, 'g'));
}
function processFile(sourceFile) {
    processNode(sourceFile);
    var lastTokLine = 0;
    var preLastTokLine = 0;
    function getTemplate(node) {
        switch (node.kind) {
            case ts.SyntaxKind.FirstTemplateToken:
                return node.text;
            case ts.SyntaxKind.TemplateExpression:
                var te = node;
                var textFragments = [te.head.text];
                for (var _i = 0, _a = te.templateSpans; _i < _a.length; _i++) {
                    var tsp = _a[_i];
                    textFragments.push("%" + ((textFragments.length - 1) / 2 + 1) + "$s");
                    textFragments.push(tsp.literal.text.replace(/%/g, "%%"));
                }
                return textFragments.join('');
            default:
                return "(pogen.ts: unable to parse)";
        }
    }
    function getComment(node) {
        var lc = ts.getLineAndCharacterOfPosition(sourceFile, node.pos);
        var lastComments;
        for (var l = preLastTokLine; l < lastTokLine; l++) {
            var pos = ts.getPositionOfLineAndCharacter(sourceFile, l, 0);
            var comments = ts.getTrailingCommentRanges(sourceFile.text, pos);
            if (comments) {
                lastComments = comments;
            }
        }
        if (!lastComments) {
            return;
        }
        var candidate = lastComments[lastComments.length - 1];
        var candidateEndLine = ts.getLineAndCharacterOfPosition(sourceFile, candidate.end).line;
        if (candidateEndLine != lc.line - 1) {
            return;
        }
        var text = sourceFile.text.slice(candidate.pos, candidate.end);
        switch (candidate.kind) {
            case ts.SyntaxKind.SingleLineCommentTrivia:
                // Remove comment leader
                text = text.replace(/^[/][/]\s*/, "");
                break;
            case ts.SyntaxKind.MultiLineCommentTrivia:
                // Remove comment leader and trailer,
                // handling white space just like xgettext.
                text = text
                    .replace(/^[/][*](\s*?\n|\s*)?/, "")
                    .replace(/(\n[ \t]*?)?[*][/]$/, "");
                break;
        }
        return text;
    }
    function getPath(node) {
        switch (node.kind) {
            case ts.SyntaxKind.PropertyAccessExpression:
                var pae = node;
                return Array.prototype.concat(getPath(pae.expression), [pae.name.text]);
            case ts.SyntaxKind.Identifier:
                var id = node;
                return [id.text];
        }
        return ["(other)"];
    }
    function arrayEq(a1, a2) {
        if (a1.length != a2.length) {
            return false;
        }
        for (var i = 0; i < a1.length; i++) {
            if (a1[i] != a2[i]) {
                return false;
            }
        }
        return true;
    }
    function processTaggedTemplateExpression(tte) {
        var lc = ts.getLineAndCharacterOfPosition(sourceFile, tte.pos);
        if (lc.line != lastTokLine) {
            preLastTokLine = lastTokLine;
            lastTokLine = lc.line;
        }
        var path = getPath(tte.tag);
        var res = {
            path: path,
            line: lc.line,
            comment: getComment(tte),
            template: getTemplate(tte.template).replace(/"/g, '\\"'),
        };
        return res;
    }
    function formatMsgComment(line, comment) {
        if (comment) {
            for (var _i = 0, _a = comment.split('\n'); _i < _a.length; _i++) {
                var cl = _a[_i];
                console.log("#. " + cl);
            }
        }
        console.log("#: " + sourceFile.fileName + ":" + (line + 1));
        console.log("#, c-format");
    }
    function formatMsgLine(head, msg) {
        // Do escaping, wrap break at newlines
        var parts = msg
            .match(/(.*\n|.+$)/g)
            .map(function (x) { return x.replace(/\n/g, '\\n'); })
            .map(function (p) { return wordwrap(p); })
            .reduce(function (a, b) { return a.concat(b); });
        if (parts.length == 1) {
            console.log(head + " \"" + parts[0] + "\"");
        }
        else {
            console.log(head + " \"\"");
            for (var _i = 0, parts_1 = parts; _i < parts_1.length; _i++) {
                var p = parts_1[_i];
                console.log("\"" + p + "\"");
            }
        }
    }
    function getJsxElementPath(node) {
        var path;
        var process = function (childNode) {
            switch (childNode.kind) {
                case ts.SyntaxKind.JsxOpeningElement:
                    {
                        var e = childNode;
                        return path = getPath(e.tagName);
                    }
                default:
                    break;
            }
        };
        ts.forEachChild(node, process);
        return path;
    }
    function translateJsxExpression(node, h) {
        switch (node.kind) {
            case ts.SyntaxKind.StringLiteral:
                {
                    var e = node;
                    return e.text;
                }
            default:
                return "%" + h[0]++ + "$s";
        }
    }
    function trim(s) {
        return s.replace(/^[ \n\t]*/, "").replace(/[ \n\t]*$/, "");
    }
    function getJsxContent(node) {
        var fragments = [];
        var holeNum = [1];
        var process = function (childNode) {
            switch (childNode.kind) {
                case ts.SyntaxKind.JsxText:
                    {
                        var e = childNode;
                        var t = e.getText().split("\n").map(trim).join("\n");
                        fragments.push(t);
                    }
                case ts.SyntaxKind.JsxOpeningElement:
                    break;
                case ts.SyntaxKind.JsxElement:
                    fragments.push("%" + holeNum[0]++ + "$s");
                    break;
                case ts.SyntaxKind.JsxExpression:
                    {
                        var e = childNode;
                        fragments.push(translateJsxExpression(e.expression, holeNum));
                        break;
                    }
                case ts.SyntaxKind.JsxClosingElement:
                    break;
                default:
                    var lc = ts.getLineAndCharacterOfPosition(childNode.getSourceFile(), childNode.getStart());
                    console.error("unrecognized syntax in JSX Element " + ts.SyntaxKind[childNode.kind] + " (" + childNode.getSourceFile().fileName + ":" + (lc.line + 1) + ":" + (lc.character + 1));
                    break;
            }
        };
        ts.forEachChild(node, process);
        return fragments.join("");
    }
    function getJsxSingular(node) {
        var res;
        var process = function (childNode) {
            switch (childNode.kind) {
                case ts.SyntaxKind.JsxElement:
                    {
                        var path = getJsxElementPath(childNode);
                        if (arrayEq(path, ["i18n", "TranslateSingular"])) {
                            res = getJsxContent(childNode);
                        }
                    }
                default:
                    break;
            }
        };
        ts.forEachChild(node, process);
        return res;
    }
    function getJsxPlural(node) {
        var res;
        var process = function (childNode) {
            switch (childNode.kind) {
                case ts.SyntaxKind.JsxElement:
                    {
                        var path = getJsxElementPath(childNode);
                        if (arrayEq(path, ["i18n", "TranslatePlural"])) {
                            res = getJsxContent(childNode);
                        }
                    }
                default:
                    break;
            }
        };
        ts.forEachChild(node, process);
        return res;
    }
    function processNode(node) {
        switch (node.kind) {
            case ts.SyntaxKind.JsxElement:
                var path = getJsxElementPath(node);
                if (arrayEq(path, ["i18n", "Translate"])) {
                    var content = getJsxContent(node);
                    var line = ts.getLineAndCharacterOfPosition(sourceFile, node.pos).line;
                    var comment = getComment(node);
                    formatMsgComment(line, comment);
                    formatMsgLine("msgid", content);
                    console.log("msgstr \"\"");
                    console.log();
                    return;
                }
                if (arrayEq(path, ["i18n", "TranslateSwitch"])) {
                    var line = ts.getLineAndCharacterOfPosition(sourceFile, node.pos).line;
                    var comment = getComment(node);
                    formatMsgComment(line, comment);
                    var singularForm = getJsxSingular(node);
                    if (!singularForm) {
                        console.error("singular form missing");
                        process.exit(1);
                    }
                    var pluralForm = getJsxPlural(node);
                    if (!pluralForm) {
                        console.error("plural form missing");
                        process.exit(1);
                    }
                    formatMsgLine("msgid", singularForm);
                    formatMsgLine("msgid_plural", pluralForm);
                    console.log("msgstr[0] \"\"");
                    console.log("msgstr[1] \"\"");
                    console.log();
                    return;
                }
                break;
            case ts.SyntaxKind.CallExpression:
                {
                    // might be i18n.plural(i18n[.X]`...`, i18n[.X]`...`)
                    var ce = node;
                    var path_1 = getPath(ce.expression);
                    if (!arrayEq(path_1, ["i18n", "plural"])) {
                        break;
                    }
                    if (ce.arguments[0].kind != ts.SyntaxKind.TaggedTemplateExpression) {
                        break;
                    }
                    if (ce.arguments[1].kind != ts.SyntaxKind.TaggedTemplateExpression) {
                        break;
                    }
                    var line = ts.getLineAndCharacterOfPosition(sourceFile, ce.pos).line;
                    var t1 = processTaggedTemplateExpression(ce.arguments[0]);
                    var t2 = processTaggedTemplateExpression(ce.arguments[1]);
                    var comment = getComment(ce);
                    formatMsgComment(line, comment);
                    formatMsgLine("msgid", t1.template);
                    formatMsgLine("msgid_plural", t2.template);
                    console.log("msgstr[0] \"\"");
                    console.log("msgstr[1] \"\"");
                    console.log();
                    // Important: no processing for child i18n expressions here
                    return;
                }
            case ts.SyntaxKind.TaggedTemplateExpression:
                {
                    var tte = node;
                    var _a = processTaggedTemplateExpression(tte), comment = _a.comment, template = _a.template, line = _a.line, path_2 = _a.path;
                    if (path_2[0] != "i18n") {
                        break;
                    }
                    formatMsgComment(line, comment);
                    formatMsgLine("msgid", template);
                    console.log("msgstr \"\"");
                    console.log();
                    break;
                }
        }
        ts.forEachChild(node, processNode);
    }
}
exports.processFile = processFile;
var fileNames = process.argv.slice(2);
console.log("# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR , YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2016-11-23 00:00+0100\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME \\n\"\n\"Language-Team: LANGUAGE \\n\"\n\"Language: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"");
console.log();
fileNames.sort();
fileNames.forEach(function (fileName) {
    var sourceFile = ts.createSourceFile(fileName, fs_1.readFileSync(fileName).toString(), ts.ScriptTarget.ES2016, /*setParentNodes */ true);
    processFile(sourceFile);
});