namespace Graphics2DText {

    /*
        Renders a specific font face, style, size, and color.
    */
    @localized('en', 'FontRenderer')
    @localized('es', 'ESFontRenderer')
    @localized('jp', 'JPFontRenderer')
    class FontRenderer {

        field font; // a FontResource instance

        field size = 12;
        field style = FontStyle.NORMAL;
        field red = 255;
        field green = 255;
        field blue = 255;

        // These are cleared every time an attribute is updated.
        // These are repopulated when you call render(text).
        field _native = null; // NativeFont
        field _settingsKey = null; // A string key that uniquely identifies text appearance encompasing all attributes. Used as part of the texture cache
        field _glRenderer = null;

        static field _textureCache = {}; // A cache of previously rendered images so that drawing the same text in multiple frames is done quickly

        // TODO: make the texture cache a layered dictionary where the first key is the the settings key and the secondary key is the text itself
        // That will reduce memory churn of concatenating these two strings every time it's rendered.

        constructor(font) {
            if (!(font is Graphics2DText.FontResource)) throw new InvalidArgumentException("Expected a FontResource instance");
            this.font = font;
        }

        @localized('en', 'setColor')
        @localized('es', 'ESsetColor')
        @localized('jp', 'JPsetColor')
        function setColor(red, green, blue) {
            // TODO: verify args
            this.red = red;
            this.green = green;
            this.blue = blue;
            this._native = null;
            this._settingsKey = null;
            return this;
        }

        @localized('en', 'setSize')
        @localized('es', 'ESsetSize')
        @localized('jp', 'JPsetSize')
        function setSize(size) {
            // TODO: verify args
            this.size = size;
            this._native = null;
            this._settingsKey = null;
            return this;
        }

        @localized('en', 'setStyle')
        @localized('es', 'ESsetStyle')
        @localized('jp', 'JPsetStyle')
        function setStyle(style) {
            // TODO: verify args
            this.style = style;
            this._native = null;
            this._settingsKey = null;
            return this;
        }

        @localized('en', 'render')
        @localized('es', 'ESrender')
        @localized('jp', 'JPrender')
        function render(text) {
            if (this._native == null) {
                this._native = NativeFont.get(
                    this.font._sourceType, this.font._source,
                    this.size, this.style, this.red, this.green, this.blue);

                // don't recycle the native font key for this since the native key will
                // drop attributes that aren't part of an allocated font face instance.
                // We need all attributes in this key.
                this._settingsKey =
                    [
                        this.font._sourceType, this.font._source,
                        this.size,
                        this.style,
                        this.red, this.green, this.blue
                    ].join(':');

                if ($lib_graphics2dtext_isGlRenderer()) {
                    this._glRenderer = GLRenderer.get(this.font._sourceType, this.font._source, this.size, this.style, this._native);
                }
            }

            textureKey = this._settingsKey + '\n' + text;
            texture = FontRenderer._textureCache.get(textureKey);
            if (texture == null) {
                texture = new TextSurface(this, text + '');
                FontRenderer._textureCache[textureKey] = texture;
            }

            return texture;
        }
    }
}
