/*
 * Child process to get localised language names.
 *
 * Set the GPII_LANG_CODES environment variable to a json array containing the desired language-country codes.
 *
 * This needs to be performed in a child process in order to use the system's current display language. Loading and
 * unloading winlangdb in the main process did not appear to work. GPII-3544.
 *
 * Copyright 2018 Raising the Floor - International
 *
 * Licensed under the New BSD license. You may not use this file except in
 * compliance with this License.
 *
 * The R&D leading to these results received funding from the
 * Department of Education - Grant H421A150005 (GPII-APCP). However,
 * these results do not necessarily represent the policy of the
 * Department of Education, and you should not assume endorsement by the
 * Federal Government.
 *
 * You may obtain a copy of the License at
 * https://github.com/GPII/universal/blob/master/LICENSE.txt
 */

"use strict";

var ffi = require("ffi-napi"),
    ref = require("ref");

/**
 * winlangdb.dll is used by control panel to perform language-related things. The first parameter of these functions
 * are a pointer to a HSTRING containing the language code (en-US), the second is a pointer which will
 * contain a pointer a HSTRING to the result when it returns. (These are undocumented).
 */
var winlangdb = ffi.Library("winlangdb", {
    // Get the english name of a language code.
    // HRESULT Bcp47GetEnglishName(HSTRING, HSTRING*)
    "Bcp47GetEnglishName": [
        "int", [ "int", "int*" ]
    ],
    // Get the localised name of a language code.
    // HRESULT Bcp47GetLocalizedName(HSTRING, HSTRING*)
    "Bcp47GetLocalizedName": [
        "int", [ "int", "int*" ]
    ],
    // Get the native name of a language code.
    // HRESULT Bcp47GetNativeName(HSTRING, HSTRING*)
    "Bcp47GetNativeName": [
        "int", [ "int", "int*" ]
    ]
});

var crtString = ffi.Library("api-ms-win-core-winrt-string-l1-1-0.dll", {
    // https://docs.microsoft.com/windows/desktop/api/winstring/nf-winstring-windowscreatestring
    "WindowsCreateString": [
        "int", [ "void*", "int", "int*" ]
    ],
    // https://docs.microsoft.com/windows/desktop/api/winstring/nf-winstring-windowsgetstringrawbuffer
    "WindowsGetStringRawBuffer": [
        "char*", ["int", "int*"]
    ],
    // https://docs.microsoft.com/en-us/windows/desktop/api/winstring/nf-winstring-windowsdeletestring
    "WindowsDeleteString": [
        "int", ["int"]
    ]
});

/**
 * Gets the language names of the given language, identified by its IETF language code (`en`, `es-MX`).
 *
 * It returns an object containing the name in English, the current display language, and native language.
 *
 * If only the language identifier (first 2 characters) are passed, then the language name is returned. If the country
 * code is also given, then the country is also returned in brackets. If the country is code is unknown, or the
 * country-specific language isn't recognised, then the language code is used instead of the country.
 *
 * If the language is unknown, then an empty string is used. If the language code is invalid, null each field is null.
 *
 * Examples:
 *```
 * "es-MX" => { english: "Spanish", local: "Spanish (Mexico)", native: "Español (México)" }
 * "en" => { english: "English", local: "English", native: "English" }
 * "en-GB" => { "english": "English", "local": "English (United Kingdom)", "native": "English (United Kingdom)" }
 *```
 * When the current display language is French:
 * ```
 * "nl-NL" => { english: "Dutch", local: "Néerlandais (Pays-Bas)", native: "Nederlands (Nederland)" }
 * ```
 * @param {String} langCode The language codes, in the form of `lang[-COUNTRY]`.
 * @return {LanguageNames} The language names.
 */
function getLanguageNames(langCode) {

    // Create a HSTRING from the language code.
    var codeBuf = new Buffer(langCode + "\u0000", "ucs2");
    var codeHStr = ref.alloc("int");
    crtString.WindowsCreateString(codeBuf, langCode.length, codeHStr);

    var funcs = {
        "english": winlangdb.Bcp47GetEnglishName,
        "local": winlangdb.Bcp47GetLocalizedName,
        "native": winlangdb.Bcp47GetNativeName
    };

    var result = {};
    Object.keys(funcs).forEach(function (key) {
        var func = funcs[key];
        var name = null;

        var nameHStr = ref.alloc("int");
        var errorCode = func(codeHStr.deref(), nameHStr);

        if (errorCode) {
            console.error("Bcp47GetXXXName failed (" + key + ")", errorCode);
        } else {
            var len = ref.alloc("int");
            var nameBuffer = crtString.WindowsGetStringRawBuffer(nameHStr.deref(), len);
            if (len.deref() > 0) {
                name = ref.reinterpretUntilZeros(nameBuffer, 2, 0).toString("ucs2");
            } else {
                name = "";
            }
        }

        crtString.WindowsDeleteString(nameHStr.deref());

        result[key] = name;
    });

    crtString.WindowsDeleteString(codeHStr);

    return result;
}

var langCodes = JSON.parse(process.env.GPII_LANG_CODES);

var result = {};
langCodes.forEach(function (langCode) {
    result[langCode] = getLanguageNames(langCode);
});

process.send(result);
