# Source: System.js

    /**
     * @license
     * Copyright 2020 Roberto Luiz Souza Monteiro,
     *                Renata Souza Barreto,
     *                Hernane Borges de Barros Pereira.
     *
     * Licensed under the Apache License, Version 2.0 (the 'License');
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *   http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an 'AS IS' BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */

    /**
     * MaiaScript system library.
     * @class
     */
    function System() {
        init();

        /**
         * Creates the attributes of the class.
         */
        function init() {
            // Class attributes goes here.
        }

        /**
         * Convert Unicode caracters to Latin1.
         * @param {string}   str - Unicode string.
         * @return {string}  The Unicode string converted do Latin1.
         */
        this.base64EncodeUnicode = function(str) {
            // First we escape the string using encodeURIComponent to get the UTF-8 encoding of the characters,
            // then we convert the percent encodings into raw bytes, and finally feed it to btoa() function.
            utf8Bytes = encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
                return String.fromCharCode('0x' + p1);
            });
            return btoa(utf8Bytes);
        }

        /**
         * Download a file.
         * @param {string}  fileName - File name.
         * @param {string}  fileData - Data to save.
         * @param {string}  mimeType - Mime type (default: 'text/plain').
         * @return          The file is downloaded.
         */
        this.downloadFile = function(fileName, fileData, mimeType) {
            if (typeof mimeType == 'undefined') {
                var mimeType = 'text/plain';
            }
            var uri = 'data:' + mimeType + ';charset=utf-8;base64,' + this.base64EncodeUnicode(fileData);
            var downloadLink = document.createElement('a');
            downloadLink.href = uri;
            downloadLink.download = fileName;
            document.body.appendChild(downloadLink);
            downloadLink.click();
            document.body.removeChild(downloadLink);
        }

        /**
         * Displays a message in the console.
         * @param {string}  text - Text to display.
         */
        this.log = function(text)
        {
            console.log(text);
        }

        /**
         * Displays a message in the console.
         * @param {string}  text - Text to display.
         */
        this.print = function(text)
        {
            this.log(text);
        }

       /**
         * Displays a formated string based on format specifiers passed to the function.
         * @param {string}   fmt - A string containing format specifiers.
         * @param {object}   arguments - Objects to be formatted.
         * @return {string}  A formatted string based on format specifiers passed to the function.
         */
        this.printf = function(fmt)
        {
            this.log(string.sprintFormat(string.sprintfParse(fmt), arguments));
        }

        /**
         * Displays a message on the console and advances the cursor to the next line.
         * @param {string}  text - Text to display.
         */
        this.println = function(text)
        {
            this.log(text + '\r\n');
        }

        /**
         * Reads data from browser storage.
         * @param {object}  obj - Object to store data: {'key': value, 'key': value, ...}
         * @param {object}  callBack - Callback function to call after access to storage.
         * @return          Data from storage.
         */
        this.readDataFromStorage = function(obj, callBack) {
            for (key in obj) {
                if (typeof localStorage.getItem(key) != 'undefined') {
                    obj[key] = localStorage.getItem(key);
                } else {
                    obj[key] = {};
                }
            }
            if (typeof callBack != 'undefined') {
                callBack();
            }
        }

        /**
         * Displays a message in a dialog box asking for confirmation.
         * @param {string}   text - Text to display.
         * @return {string}  User choice.
         */
        this.showConfirmDialog = function(text)
        {
            return confirm(text);
        }

        /**
         * Displays a message in a dialog box asking you to enter text.
         * @param {string}   text - Text to display.
         * @param {string}   defaultText - Default text to display in the text box.
         * @return {string}  User-typed text.
         */
        this.showInputDialog = function(text, defaultText = '')
        {
            return prompt(text, defaultText);
        }

        /**
         * Displays a message in a dialog box.
         * @param {string}  text - Text to display.
         */
        this.showMessageDialog = function(text)
        {
            alert(text);
        }

        /**
         * Load a MaiaScript module.
         * @param {string}   inputFile - Module name.
         * @return {object}  The MaiaScript module loaded.
         */
        this.source = function(inputFile)
        {
            if (typeof process !== 'undefined') {
                var fs = require('fs');
                var readTextFile = fs.readFileSync;

                function getXml(data) {
                    compiledCode.xml += data;
                }

                function read(input) {
                    if (/^{.*}$/.test(input)) {
                        return input.substring(1, input.length - 1);
                    } else {
                        var content = readTextFile(input, 'utf-8');
                        return content.length > 0 && content.charCodeAt(0) == 0xFEFF ? content.substring(1) : content;
                    }
                }

                if (typeof inputFile != 'undefined') {
                    var code = read(String(inputFile));
                    core.eval(code);
                } else {
                    throw new Error('Invalid argument for function source. Argument must be a string.');
                }
            }
        }

        /**
         * Upload a file.
         * @param {object}    fileObject - File data structure.
         * @param {function}  callBack - callback to be called when the file is loaded.
         * @return            The file is uploaded.
         */
        this.uploadFile = function(fileObject, callBack) {
            var input = document.createElement('input');
            input.type = 'file';
            input.onchange = e => {
                var file = e.target.files[0];
                fileObject.fullFileName = file.name;
                fileObject.fileName = fileObject.fullFileName.split('.').shift();
                fileObject.fileExtension = fileObject.fullFileName.split('.').pop();
                var reader = new FileReader();
                reader.readAsText(file,'UTF-8');
                reader.onload = readerEvent => {
                    fileObject.fileData = readerEvent.target.result;
                    if (typeof callBack != 'undefined') {
                        callBack(fileObject);
                    }
                }
            }
            input.click();
        }

        /**
         * Writes data to storage.
         * @param {object}  obj - Object to store data: {'key': value, 'key': value, ...}
         * @param {object}  callBack - Callback function to call after access to storage.
         * @return          Data written to storage.
         */
        this.writeDataToStorage = function(obj, callBack) {
            for (key in obj) {
                if (typeof obj[key] != 'undefined') {
                    localStorage.setItem(key, obj[key]);
                } else {
                    localStorage.setItem(key, {});
                }
            }
            if (typeof callBack != 'undefined') {
                callBack();
            }
        }
    }

    system = new System();