namespace Core {

    @localized('en', 'CRAYON_VERSION')
    @localized('es', 'ESCRAYON_VERSION')
    @localized('jp', 'JPCRAYON_VERSION')
    const CRAYON_VERSION = "0.2.1";

    @localized('en', 'Type')
    @localized('es', 'ESType')
    @localized('jp', 'JPType')
    enum Type {
        NULL = 0,
        BOOLEAN = 1,
        INTEGER = 2,
        FLOAT = 3,
        STRING = 4,
        LIST = 5,
        DICTIONARY = 6,
        OBJECT = 7,
        FUNCTION = 8,
        CLASS = 9,
    }

    @localized('en', 'assert')
    @localized('es', 'ESassert')
    @localized('jp', 'JPassert')
    function assert(condition, message = null) {
        return $$$('assert', condition, message, true);
    }

    @localized('en', 'fail')
    @localized('es', 'ESfail')
    @localized('jp', 'JPfail')
    function fail(message) {
        return $$$('assert', false, message, false);
    }

    @localized('en', 'chr')
    @localized('es', 'ESchr')
    @localized('jp', 'JPchr')
    function chr(value) {
        return $$$('chr', value);
    }

    @localized('en', 'currentTime')
    @localized('es', 'EScurrentTime')
    @localized('jp', 'JPcurrentTime')
    function currentTime() {
        return $$$('currentTime');
    }

    @localized('en', 'ord')
    @localized('es', 'ESord')
    @localized('jp', 'JPord')
    function ord(value) {
        return $$$('ord', value);
    }

    @localized('en', 'parseFloat')
    @localized('es', 'ESparseFloat')
    @localized('jp', 'JPparseFloat')
    function parseFloat(value) {
        return $$$('parseFloat', value);
    }

    @localized('en', 'parseInt')
    @localized('es', 'ESparseInt')
    @localized('jp', 'JPparseInt')
    function parseInt(value) {
        return $$$('parseInt', value);
    }

    @localized('en', 'print')
    @localized('es', 'publicar')
    @localized('jp', 'JPprint')
    function print(value) {
        return $$$('print', value);
    }

    @localized('en', 'sleep')
    @localized('es', 'ESsleep')
    @localized('jp', 'JPsleep')
    function sleep(seconds) {
        Core.assert(
            $$$('typeis', seconds, Core.Type.INTEGER, Core.Type.FLOAT) &&
            seconds >= 0, "Invalid argument.");
        return $$$('sleep', seconds);
    }

    @localized('en', 'typeof')
    @localized('es', 'EStypeof')
    @localized('jp', 'JPtypeof')
    function typeof(value) {
        return $$$('typeof',value);
    }

    @localized('en', 'isString')
    @localized('es', 'ESisString')
    @localized('jp', 'JPisString')
    function isString(value) {
        return $$$('typeis', value, Core.Type.STRING);
    }

    @localized('en', 'isNumber')
    @localized('es', 'ESisNumber')
    @localized('jp', 'JPisNumber')
    function isNumber(value) {
        return $$$('typeis', value, Core.Type.INTEGER, Core.Type.FLOAT);
    }

    function _LIB_CORE_list_sort_by_key(fp, list) {
        if (list.length > 1) {
            $$$('sortList', list, _LIB_CORE_list_map(fp, list));
        }
    }

    function _LIB_CORE_list_map(fp, list) {
        output = [];
        for (t : list) {
            output.add(fp(t));
        }
        return output;
    }

    function _LIB_CORE_list_filter(fp, list) {
        output = [];
        for (t : list) {
            b = fp(t);
            if (!$$$('typeis', b, Core.Type.BOOLEAN)) {
                Core.fail("Filter function returned a non-boolean value.");
            }
            if (b) {
                output.add(t);
            }
        }
        return output;
    }

    @localized('en', 'getCurrentExecutionToken')
    @localized('es', 'ESgetCurrentExecutionToken')
    @localized('jp', 'JPgetCurrentExecutionToken')
    function getCurrentExecutionToken() {
        return $$$('execId');
    }

    @localized('en', 'getExecutionIterationCounter')
    @localized('es', 'ESgetExecutionIterationCounter')
    @localized('jp', 'JPgetExecutionIterationCounter')
    function getExecutionIterationCounter() {
        return $$$('execCounter');
    }

    @localized('en', 'getProjectId')
    @localized('es', 'ESgetProjectId')
    @localized('jp', 'JPgetProjectId')
    function getProjectId() {
        return $$$('projectId');
    }

    // Interanlly called by fp.invoke(args)
    function _LIB_CORE_invoke(fp, args) {
        // MAGIC ALERT: There is a special compiler hack that checks specifically for this line (based on parent
        // function name) and compiles this function invocation with a special flag to treat the args list as
        // individually passed arguments.
        return fp(args);
    }

    // A simple dummy object.
    // The VM supports attaching native data to any object instance, and so this generally serves as
    // the canonical object to use for that purpose.
    @localized('en', 'Object')
    @localized('es', 'ESObject')
    @localized('jp', 'JPObject')
    class Object { }
}
