using UnityEngine;
using System.Collections;
/* MUDANÇA GAIA */
using System;
using mgaia.tk2d;
using mgaia.Utils;
using System.Collections.Generic;
/* FIM MUDANÇA */

[System.Serializable]
public class tk2dTextMeshData
{
    public int version = 0;

    public tk2dFontData font;
    public string text = "";
    public Color color = Color.white;
    public Color color2 = Color.white;
    public bool useGradient = false;
    public int textureGradient = 0;
    public TextAnchor anchor = TextAnchor.LowerLeft;
    public int renderLayer = 0;
    public Vector3 scale = Vector3.one;
    public bool kerning = false;
    public int maxChars = 16;
    public bool inlineStyling = false;

    public bool formatting = false;
    public int wordWrapWidth = 0;

    public float spacing = 0.0f;
    public float lineSpacing = 0.0f;
}

[ExecuteInEditMode]
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
[AddComponentMenu("2D Toolkit/Text/tk2dTextMesh")]
/// <summary>
/// Text mesh
/// </summary>
public class tk2dTextMesh : MonoBehaviour, tk2dRuntime.ISpriteCollectionForceBuild
{
    tk2dFontData _fontInst;
    string _formattedText = "";

    /* MUDANCA GAIA */
    public Color SerializedColor
    {
        get { return _color; }
    }

    MulticoloredTextMesh mMulticoloredTextMesh;
    /* FIM MUDANCA GAIA */

    // This stuff now kept in tk2dTextMeshData. Remove in future version.
    [SerializeField]
    tk2dFontData _font = null;
    [SerializeField]
    string _text = "";
    [SerializeField]
    Color _color = Color.white;
    [SerializeField]
    Color _color2 = Color.white;
    [SerializeField]
    bool _useGradient = false;
    [SerializeField]
    int _textureGradient = 0;
    [SerializeField]
    TextAnchor _anchor = TextAnchor.LowerLeft;
    [SerializeField]
    Vector3 _scale = new Vector3(1.0f, 1.0f, 1.0f);
    [SerializeField]
    bool _kerning = false;
    [SerializeField]
    int _maxChars = 16;
    [SerializeField]
    bool _inlineStyling = false;

    [SerializeField]
    bool _formatting = false;
    [SerializeField]
    int _wordWrapWidth = 0;

    [SerializeField]
    float spacing = 0.0f;
    [SerializeField]
    float lineSpacing = 0.0f;

    // Holding the data in this struct for the next version
    [SerializeField]
    tk2dTextMeshData data = new tk2dTextMeshData();

    /* MUDANÇA GAIA */

    struct ColorRange
    {
        public int StartIndex;
        public int Length;
        public int ColorIndex;
    }

    List<ColorRange> colorsRanges = new List<ColorRange>();
    /* FIM MUDANÇA */

    // Batcher needs to grab this
    public string FormattedText
    {
        get { return _formattedText; }
    }

    /// <summary>
    /// Método para sinalizar que esse código foi personalizado pela m.gaia
    /// </summary>
    internal static void MgaiaEdit()
    {
    }

    void UpgradeData()
    {
        if (data.version != 1)
        {
            data.font = _font;
            data.text = _text;
            data.color = _color;
            data.color2 = _color2;
            data.useGradient = _useGradient;
            data.textureGradient = _textureGradient;
            data.anchor = _anchor;
            data.scale = _scale;
            data.kerning = _kerning;
            data.maxChars = _maxChars;
            data.inlineStyling = _inlineStyling;
            data.formatting = _formatting;
            data.wordWrapWidth = _wordWrapWidth;
            data.spacing = spacing;
            data.lineSpacing = lineSpacing;
        }
        data.version = 1;
    }

    Vector3[] vertices;
    Vector2[] uvs;
    Vector2[] uv2;
    Color32[] colors;
    Color32[] untintedColors;

    static int GetInlineStyleCommandLength(int cmdSymbol)
    {
        int val = 0;
        switch (cmdSymbol)
        {
            case 'c': val = 5; break; // cRGBA
            case 'C': val = 9; break; // CRRGGBBAA
            case 'g': val = 9; break; // gRGBARGBA
            case 'G': val = 17; break; // GRRGGBBAARRGGBBAA
        }
        return val;
    }

    /// <summary>
    /// Formats the string using the current settings, and returns the formatted string.
    /// You can use this if you need to calculate how many lines your string is going to be wrapped to.
    /// </summary>
    public string FormatText(string unformattedString)
    {
        string returnValue = "";
        FormatText(ref returnValue, unformattedString);
        return returnValue;
    }

    void FormatText()
    {
        FormatText(ref _formattedText, data.text);
    }

    void FormatText(ref string _targetString, string _source)
    {
        InitInstance();
        if (formatting == false || wordWrapWidth == 0 || _fontInst.texelSize == Vector2.zero)
        {
            _targetString = _source;
            return;
        }

        float lineWidth = _fontInst.texelSize.x * wordWrapWidth;

        System.Text.StringBuilder target = new System.Text.StringBuilder(_source.Length);
        float widthSoFar = 0.0f;
        float wordStart = 0.0f;
        int targetWordStartIndex = -1;
        int fmtWordStartIndex = -1;
        bool ignoreNextCharacter = false;
        for (int i = 0; i < _source.Length; ++i)
        {
            char idx = _source[i];
            tk2dFontChar chr;

            bool inlineHatChar = (idx == '^');

            if (_fontInst.useDictionary)
            {
                if (!_fontInst.charDict.ContainsKey(idx)) idx = (char)0;
                chr = _fontInst.charDict[idx];
            }
            else
            {
                if (idx >= _fontInst.chars.Length) idx = (char)0; // should be space
                chr = _fontInst.chars[idx];
            }

            if (inlineHatChar) idx = '^';

            if (ignoreNextCharacter)
            {
                ignoreNextCharacter = false;
                continue;
            }

            if (data.inlineStyling && idx == '^' && i + 1 < _source.Length)
            {
                if (_source[i + 1] == '^')
                {
                    ignoreNextCharacter = true;
                    target.Append('^'); // add the second hat that we'll skip
                }
                else
                {
                    int cmdLength = GetInlineStyleCommandLength(_source[i + 1]);
                    int skipLength = 1 + cmdLength; // The ^ plus the command
                    for (int j = 0; j < skipLength; ++j)
                    {
                        if (i + j < _source.Length)
                        {
                            target.Append(_source[i + j]);
                        }
                    }
                    i += skipLength - 1;
                    continue;
                }
            }

            if (idx == '\n')
            {
                widthSoFar = 0.0f;
                wordStart = 0.0f;
                targetWordStartIndex = target.Length;
                fmtWordStartIndex = i;
            }
            else if (idx == ' '/* || idx == '.' || idx == ',' || idx == ':' || idx == ';' || idx == '!'*/)
            {
                /*if ((widthSoFar + chr.p1.x * data.scale.x) > lineWidth)
                {
                    target.Append('\n');
                    widthSoFar = chr.advance * data.scale.x;
                }
                else
                {*/
                widthSoFar += (chr.advance + data.spacing) * data.scale.x;
                //}

                wordStart = widthSoFar;
                targetWordStartIndex = target.Length;
                fmtWordStartIndex = i;
            }
            else
            {
                if ((widthSoFar + chr.p1.x * data.scale.x) > lineWidth)
                {
                    // If the last word started after the start of the line
                    if (wordStart > 0.0f)
                    {
                        wordStart = 0.0f;
                        widthSoFar = 0.0f;
                        // rewind
                        target.Remove(targetWordStartIndex + 1, target.Length - targetWordStartIndex - 1);
                        target.Append('\n');
                        i = fmtWordStartIndex;
                        continue; // don't add this character
                    }
                    else
                    {
                        target.Append('\n');
                        widthSoFar = (chr.advance + data.spacing) * data.scale.x;
                    }
                }
                else
                {
                    widthSoFar += (chr.advance + data.spacing) * data.scale.x;
                }
            }

            target.Append(idx);
        }
        _targetString = target.ToString();
    }

    [System.FlagsAttribute]
    enum UpdateFlags
    {
        UpdateNone = 0,
        UpdateText = 1,	// update text vertices & uvs
        UpdateColors = 2,	// only colors have changed
        UpdateBuffers = 4,	// update buffers (maxchars has changed)
    };
    UpdateFlags updateFlags = UpdateFlags.UpdateBuffers;

    Mesh mesh;
    MeshFilter meshFilter;

    void SetNeedUpdate(UpdateFlags uf)
    {
        if (updateFlags == UpdateFlags.UpdateNone)
        {
            updateFlags |= uf;
            tk2dUpdateManager.QueueCommit(this);
        }
        else
        {
            // Already queued
            updateFlags |= uf;
        }
    }

    // accessors
    /// <summary>Gets or sets the font. Call <see cref="Commit"/> to commit changes.</summary>
    public tk2dFontData font
    {
        get { UpgradeData(); return data.font; }
        set
        {
            UpgradeData();
            data.font = value;
            _fontInst = data.font.inst;
            SetNeedUpdate(UpdateFlags.UpdateText);

            UpdateMaterial();
        }
    }

    /// <summary>Enables or disables formatting. Call <see cref="Commit"/> to commit changes.</summary>
    public bool formatting
    {
        get { UpgradeData(); return data.formatting; }
        set
        {
            UpgradeData();
            if (data.formatting != value)
            {
                data.formatting = value;
                SetNeedUpdate(UpdateFlags.UpdateText);
            }
        }
    }

    /// <summary>Change word wrap width. This only works when formatting is enabled. 
    /// Call <see cref="Commit"/> to commit changes.</summary>
    public int wordWrapWidth
    {
        get { UpgradeData(); return data.wordWrapWidth; }
        set { UpgradeData(); if (data.wordWrapWidth != value) { data.wordWrapWidth = value; SetNeedUpdate(UpdateFlags.UpdateText); } }
    }

    /// <summary>Gets or sets the text. Call <see cref="Commit"/> to commit changes.</summary>
    public string text
    {
        get { UpgradeData(); return data.text; }
        set
        {
            UpgradeData();
            data.text = value;
            SetNeedUpdate(UpdateFlags.UpdateText);
        }
    }

    /// <summary>Gets or sets the color. Call <see cref="Commit"/> to commit changes.</summary>
    public Color color { get { UpgradeData(); return data.color; } set { UpgradeData(); data.color = value; SetNeedUpdate(UpdateFlags.UpdateColors); } }
    /// <summary>Gets or sets the secondary color (used in the gradient). Call <see cref="Commit"/> to commit changes.</summary>
    public Color color2 { get { UpgradeData(); return data.color2; } set { UpgradeData(); data.color2 = value; SetNeedUpdate(UpdateFlags.UpdateColors); } }
    /// <summary>Use vertex vertical gradient. Call <see cref="Commit"/> to commit changes.</summary>
    public bool useGradient { get { UpgradeData(); return data.useGradient; } set { UpgradeData(); data.useGradient = value; SetNeedUpdate(UpdateFlags.UpdateColors); } }
    /// <summary>Gets or sets the text anchor. Call <see cref="Commit"/> to commit changes.</summary>
    public TextAnchor anchor { get { UpgradeData(); return data.anchor; } set { UpgradeData(); data.anchor = value; SetNeedUpdate(UpdateFlags.UpdateText); } }
    /// <summary>Gets or sets the scale. Call <see cref="Commit"/> to commit changes.</summary>
    public Vector3 scale { get { UpgradeData(); return data.scale; } set { UpgradeData(); data.scale = value; SetNeedUpdate(UpdateFlags.UpdateText); } }
    /// <summary>Gets or sets kerning state. Call <see cref="Commit"/> to commit changes.</summary>
    public bool kerning { get { UpgradeData(); return data.kerning; } set { UpgradeData(); data.kerning = value; SetNeedUpdate(UpdateFlags.UpdateText); } }
    /// <summary>Gets or sets maxChars. Call <see cref="Commit"/> to commit changes.
    /// NOTE: This will free & allocate memory, avoid using at runtime.
    /// </summary>
    public int maxChars { get { UpgradeData(); return data.maxChars; } set { UpgradeData(); data.maxChars = value; SetNeedUpdate(UpdateFlags.UpdateBuffers); } }
    /// <summary>Gets or sets the default texture gradient. 
    /// You can also change texture gradient inline by using ^1 - ^9 sequences within your text.
    /// Call <see cref="Commit"/> to commit changes.</summary>
    public int textureGradient { get { UpgradeData(); return data.textureGradient; } set { UpgradeData(); data.textureGradient = value % font.gradientCount; SetNeedUpdate(UpdateFlags.UpdateText); } }
    /// <summary>Enables or disables inline styling (texture gradient). Call <see cref="Commit"/> to commit changes.</summary>
    public bool inlineStyling { get { UpgradeData(); return data.inlineStyling; } set { UpgradeData(); data.inlineStyling = value; SetNeedUpdate(UpdateFlags.UpdateText); } }
    /// <summary>Additional spacing between characters. 
    /// This can be negative to bring characters closer together.
    /// Call <see cref="Commit"/> to commit changes.</summary>
    public float Spacing { get { UpgradeData(); return data.spacing; } set { UpgradeData(); if (data.spacing != value) { data.spacing = value; SetNeedUpdate(UpdateFlags.UpdateText); } } }
    /// <summary>Additional line spacing for multieline text. 
    /// This can be negative to bring lines closer together.
    /// Call <see cref="Commit"/> to commit changes.</summary>
    public float LineSpacing { get { UpgradeData(); return data.lineSpacing; } set { UpgradeData(); if (data.lineSpacing != value) { data.lineSpacing = value; SetNeedUpdate(UpdateFlags.UpdateText); } } }

    /// <summary>
    /// Gets or sets the sorting order
    /// The sorting order lets you override draw order for sprites which are at the same z position
    /// It is similar to offsetting in z - the sprite stays at the original position
    /// This corresponds to the renderer.sortingOrder property in Unity 4.3
    /// </summary>
    public int SortingOrder
    {
        get
        {
#if (UNITY_3_5 || UNITY_4_0 || UNITY_4_0_1 || UNITY_4_1 || UNITY_4_2)
			return data.renderLayer; 
#else
            return CachedRenderer.sortingOrder;
#endif
        }
        set
        {
#if (UNITY_3_5 || UNITY_4_0 || UNITY_4_0_1 || UNITY_4_1 || UNITY_4_2)
			if (data.renderLayer != value) { 
				data.renderLayer = value; SetNeedUpdate(UpdateFlags.UpdateText);
			} 
#else
            if (CachedRenderer.sortingOrder != value)
            {
                data.renderLayer = value; // for awake
                CachedRenderer.sortingOrder = value;
#if UNITY_EDITOR
                UnityEditor.EditorUtility.SetDirty(CachedRenderer);
#endif
            }
#endif
        }
    }

    void InitInstance()
    {
        if (data != null && data.font != null)
        {
            _fontInst = data.font.inst;
            _fontInst.InitDictionary();
        }
    }

    Renderer _cachedRenderer = null;
    Renderer CachedRenderer
    {
        get
        {
            if (_cachedRenderer == null)
            {
                _cachedRenderer = renderer;
            }
            return _cachedRenderer;
        }
    }

    // Use this for initialization
    void Awake()
    {
        /* MUDANCA GAIA */
        mMulticoloredTextMesh = GetComponent<MulticoloredTextMesh>();
        /* FIM MUDANCA GAIA*/

        UpgradeData();
        if (data.font != null)
            _fontInst = data.font.inst;

        // force rebuild when awakened, for when the object has been pooled, etc
        // this is probably not the best way to do it
        updateFlags = UpdateFlags.UpdateBuffers;

        if (data.font != null)
        {
            Init();
            UpdateMaterial();
        }

        // Sensibly reset, so tk2dUpdateManager can deal with this properly
        updateFlags = UpdateFlags.UpdateNone;
    }

#if UNITY_EDITOR
    private void OnEnable()
    {
        if (renderer != null && data != null && data.font != null && data.font.inst != null && renderer.sharedMaterial == null && data.font.inst.needMaterialInstance)
        {
            ForceBuild();
        }
    }
#endif

    protected void OnDestroy()
    {
        if (meshFilter == null)
        {
            meshFilter = GetComponent<MeshFilter>();
        }
        if (meshFilter != null)
        {
            mesh = meshFilter.sharedMesh;
        }

        if (mesh)
        {
            DestroyImmediate(mesh, true);
            meshFilter.mesh = null;
        }
    }

    bool useInlineStyling { get { return inlineStyling && _fontInst.textureGradients; } }

    /// <summary>
    /// Returns the number of characters drawn for the currently active string.
    /// This may be less than string.Length - some characters are used as escape codes for switching texture gradient ^0-^9
    /// Also, there might be more characters in the string than have been allocated for the textmesh, in which case
    /// the string will be truncated.
    /// </summary>
    public int NumDrawnCharacters()
    {
        int charsDrawn = NumTotalCharacters();
        if (charsDrawn > data.maxChars) charsDrawn = data.maxChars;
        return charsDrawn;
    }

    /// <summary>
    /// Returns the number of characters excluding texture gradient escape codes.
    /// </summary>
    public int NumTotalCharacters()
    {
        InitInstance();

        if ((updateFlags & (UpdateFlags.UpdateText | UpdateFlags.UpdateBuffers)) != 0)
            FormatText();

        int numChars = 0;
        for (int i = 0; i < _formattedText.Length; ++i)
        {
            int idx = _formattedText[i];

            bool inlineHatChar = (idx == '^');

            if (_fontInst.useDictionary)
            {
                if (!_fontInst.charDict.ContainsKey(idx)) idx = 0;

            }
            else
            {
                if (idx >= _fontInst.chars.Length) idx = 0; // should be space
            }

            if (inlineHatChar) idx = '^';

            if (idx == '\n')
            {
                continue;
            }
            else if (data.inlineStyling)
            {
                if (idx == '^' && i + 1 < _formattedText.Length)
                {
                    if (_formattedText[i + 1] == '^')
                    {
                        ++i;
                    }
                    else
                    {
                        i += GetInlineStyleCommandLength(_formattedText[i + 1]);
                        continue;
                    }
                }
            }

            ++numChars;
        }
        return numChars;
    }

    [System.Obsolete("Use GetEstimatedMeshBoundsForString().size instead")]
    public Vector2 GetMeshDimensionsForString(string str)
    {
        return tk2dTextGeomGen.GetMeshDimensionsForString(str, tk2dTextGeomGen.Data(data, _fontInst, _formattedText));
    }

    /// <summary>
    /// Calculates an estimated bounds for the given string if it were rendered
    /// using the current settings.
    /// This expects an unformatted string and will wrap the string if required.
    /// </summary>
    public Bounds GetEstimatedMeshBoundsForString(string str)
    {
        InitInstance();
        tk2dTextGeomGen.GeomData geomData = tk2dTextGeomGen.Data(data, _fontInst, _formattedText);
        Vector2 dims = tk2dTextGeomGen.GetMeshDimensionsForString(FormatText(str), geomData);
        float offsetY = tk2dTextGeomGen.GetYAnchorForHeight(dims.y, geomData);
        float offsetX = tk2dTextGeomGen.GetXAnchorForWidth(dims.x, geomData);
        float lineHeight = (_fontInst.lineHeight + data.lineSpacing) * data.scale.y;
        return new Bounds(new Vector3(offsetX + dims.x * 0.5f, offsetY + dims.y * 0.5f + lineHeight, 0), Vector3.Scale(dims, new Vector3(1, -1, 1)));
    }

    public void Init(bool force)
    {
        if (force)
        {
            SetNeedUpdate(UpdateFlags.UpdateBuffers);
        }
        Init();
    }

    public void Init()
    {
        if (_fontInst && ((updateFlags & UpdateFlags.UpdateBuffers) != 0 || mesh == null))
        {
            _fontInst.InitDictionary();
            FormatText();

            var geomData = tk2dTextGeomGen.Data(data, _fontInst, _formattedText);

            // volatile data
            int numVertices;
            int numIndices;
            tk2dTextGeomGen.GetTextMeshGeomDesc(out numVertices, out numIndices, geomData);
            vertices = new Vector3[numVertices];
            uvs = new Vector2[numVertices];
            colors = new Color32[numVertices];
            untintedColors = new Color32[numVertices];
            if (_fontInst.textureGradients)
            {
                uv2 = new Vector2[numVertices];
            }
            int[] triangles = new int[numIndices];


            int target = tk2dTextGeomGen.SetTextMeshGeom(vertices, uvs, uv2, untintedColors, 0, geomData);

            if (!_fontInst.isPacked)
            {
                Color32 topColor = data.color;
                Color32 bottomColor = data.useGradient ? data.color2 : data.color;
                for (int i = 0; i < numVertices; ++i)
                {
                    /* MUDANÇA GAIA */
                    colors[i] = CreateColor(topColor, bottomColor, untintedColors[i], i);
                    /* FIM MUDANÇA GAIA */
                }
            }
            else
            {
                colors = untintedColors;
            }

            tk2dTextGeomGen.SetTextMeshIndices(triangles, 0, 0, geomData, target);



            if (mesh == null)
            {
                if (meshFilter == null)
                    meshFilter = GetComponent<MeshFilter>();

                mesh = new Mesh();
                mesh.hideFlags = HideFlags.DontSave;
                meshFilter.mesh = mesh;
            }
            else
            {
                mesh.Clear();
            }
            mesh.vertices = vertices;
            mesh.uv = uvs;
            if (font.textureGradients)
            {
                mesh.uv2 = uv2;
            }
            mesh.triangles = triangles;
            mesh.colors32 = colors;
            mesh.RecalculateBounds();
            mesh.bounds = tk2dBaseSprite.AdjustedMeshBounds(mesh.bounds, data.renderLayer);

            updateFlags = UpdateFlags.UpdateNone;
        }
    }

    /* MUDANCA GAIA */
    private Color32 CreateColor(Color32 topColor, Color32 bottomColor, Color32 untintedColor, int idx)
    {
        Color32 c = default(Color32);

        if (mMulticoloredTextMesh == null)
        {
            c = ((idx % 4) < 2) ? topColor : bottomColor;
        }
        else
        {
            int charIndex = idx / 4;

            foreach (ColorRange colorRange in colorsRanges)
            {
                if (charIndex >= colorRange.StartIndex && charIndex < colorRange.StartIndex + colorRange.Length)
                {
                    c = mMulticoloredTextMesh.colorsIndexes[colorRange.ColorIndex];
                    break;
                }
            }
        }

        byte red = (byte)(((int)untintedColor.r * (int)c.r) / 255);
        byte green = (byte)(((int)untintedColor.g * (int)c.g) / 255);
        byte blue = (byte)(((int)untintedColor.b * (int)c.b) / 255);
        byte alpha = (byte)(((int)untintedColor.a * (int)c.a) / 255);
        if (_fontInst.premultipliedAlpha)
        {
            red = (byte)(((int)red * (int)alpha) / 255);
            green = (byte)(((int)green * (int)alpha) / 255);
            blue = (byte)(((int)blue * (int)alpha) / 255);
        }

        return new Color32(red, green, blue, alpha);
    }
    /* FIM MUDANCA */

    /// <summary>
    /// Calling commit is no longer required on text meshes.
    /// You can still call commit to manually commit all changes so far in the frame.
    /// </summary>
    public void Commit()
    {
        tk2dUpdateManager.FlushQueues();
    }

    // Do not call this, its meant fo internal use
    public void DoNotUse__CommitInternal()
    {
        // Make sure instance is set up, might not be when calling from Awake.
        InitInstance();

        // make sure fonts dictionary is initialized properly before proceeding
        if (_fontInst == null)
        {
            return;
        }
        _fontInst.InitDictionary();

        // Can come in here without anything initalized when
        // instantiated in code
        if ((updateFlags & UpdateFlags.UpdateBuffers) != 0 || mesh == null)
        {
            Init();
        }
        else
        {
            if ((updateFlags & UpdateFlags.UpdateText) != 0)
            {
                FormatText();

                /* MUDANÇA GAIA */
                if (mMulticoloredTextMesh != null && _formattedText != String.Empty)
                {
                    char newLineCharacter;
                    char.TryParse("\n", out newLineCharacter);

                    colorsRanges.Clear();

                    string actualColorIndexText = String.Empty;
                    bool onDelimitter = false;
                    bool isFirstDelimitter = true;
                    int delimitterIndex = default(int);
                    int idx = 0;
                    foreach (char charAtIndex in _formattedText)
                    {
                        if (onDelimitter)
                        {
                            if (charAtIndex == '}')
                            {
                                onDelimitter = false;
                            }
                            else
                            {
                                actualColorIndexText += charAtIndex;
                            }
                        }
                        else if (charAtIndex == '{')
                        {
                            onDelimitter = true;

                            if (isFirstDelimitter)
                            {
                                isFirstDelimitter = false;
                            }
                            else
                            {
                                colorsRanges.Add(new ColorRange { ColorIndex = Int32.Parse(actualColorIndexText), StartIndex = delimitterIndex, Length = idx - delimitterIndex });

                                actualColorIndexText = String.Empty;
                            }

                            delimitterIndex = idx;
                        }
                        else if (charAtIndex != newLineCharacter)
                        {
                            idx++;
                        }
                    }

                    colorsRanges.Add(new ColorRange { ColorIndex = Int32.Parse(actualColorIndexText), StartIndex = delimitterIndex, Length = _formattedText.Length - delimitterIndex });

                    foreach (ColorRange colorRange in colorsRanges)
                    {
                        string colorIdentifier = String.Format("{{{0}}}", colorRange.ColorIndex);
                        while (_formattedText.Contains(colorIdentifier))
                        {
                            _formattedText = _formattedText.Remove(_formattedText.IndexOf(colorIdentifier), colorIdentifier.Length);
                        }
                    }
                }
                /* FIM MUDANÇA GAIA */

                var geomData = tk2dTextGeomGen.Data(data, _fontInst, _formattedText);
                int target = tk2dTextGeomGen.SetTextMeshGeom(vertices, uvs, uv2, untintedColors, 0, geomData);

                for (int i = target; i < data.maxChars; ++i)
                {
                    // was/is unnecessary to fill anything else
                    vertices[i * 4 + 0] = vertices[i * 4 + 1] = vertices[i * 4 + 2] = vertices[i * 4 + 3] = Vector3.zero;
                }

                mesh.vertices = vertices;
                mesh.uv = uvs;
                if (_fontInst.textureGradients)
                {
                    mesh.uv2 = uv2;
                }
                if (_fontInst.isPacked)
                {
                    colors = untintedColors;
                    mesh.colors32 = colors;
                }
                if (data.inlineStyling)
                {
                    SetNeedUpdate(UpdateFlags.UpdateColors);
                }

                mesh.RecalculateBounds();
                mesh.bounds = tk2dBaseSprite.AdjustedMeshBounds(mesh.bounds, data.renderLayer);
            }

            if (!font.isPacked && (updateFlags & UpdateFlags.UpdateColors) != 0) // packed fonts don't support tinting
            {
                Color32 topColor = data.color;
                Color32 bottomColor = data.useGradient ? data.color2 : data.color;
                for (int i = 0; i < colors.Length; ++i)
                {
                    /* MUDANÇA GAIA */
                    colors[i] = CreateColor(topColor, bottomColor, untintedColors[i], i);
                    /* FIM MUDANÇA GAIA */
                }

                mesh.colors32 = colors;
            }
        }

        updateFlags = UpdateFlags.UpdateNone;
    }

    /// <summary>
    /// Makes the text mesh pixel perfect to the active camera.
    /// Automatically detects <see cref="tk2dCamera"/> if present
    /// Otherwise uses Camera.main
    /// </summary>
    public void MakePixelPerfect()
    {
        float s = 1.0f;
        tk2dCamera cam = tk2dCamera.CameraForLayer(gameObject.layer);
        if (cam != null)
        {
            if (_fontInst.version < 1)
            {
                Debug.LogError("Need to rebuild font.");
            }

            float zdist = (transform.position.z - cam.transform.position.z);
            float textMeshSize = (_fontInst.invOrthoSize * _fontInst.halfTargetHeight);
            s = cam.GetSizeAtDistance(zdist) * textMeshSize;
        }
        else if (Camera.main)
        {
            if (Camera.main.isOrthoGraphic)
            {
                s = Camera.main.orthographicSize;
            }
            else
            {
                float zdist = (transform.position.z - Camera.main.transform.position.z);
                s = tk2dPixelPerfectHelper.CalculateScaleForPerspectiveCamera(Camera.main.fieldOfView, zdist);
            }
            s *= _fontInst.invOrthoSize;
        }
        scale = new Vector3(Mathf.Sign(scale.x) * s, Mathf.Sign(scale.y) * s, Mathf.Sign(scale.z) * s);
    }

    // tk2dRuntime.ISpriteCollectionEditor
    public bool UsesSpriteCollection(tk2dSpriteCollectionData spriteCollection)
    {
        if (data.font != null && data.font.spriteCollection != null)
            return data.font.spriteCollection == spriteCollection;

        // No easy way to identify this at this stage
        return true;
    }

    void UpdateMaterial()
    {
        if (renderer.sharedMaterial != _fontInst.materialInst)
            renderer.material = _fontInst.materialInst;
    }

    public void ForceBuild()
    {
        if (data.font != null)
        {
            _fontInst = data.font.inst;
            UpdateMaterial();
        }
        Init(true);
    }

#if UNITY_EDITOR
    void OnDrawGizmos()
    {
        if (mesh != null)
        {
            Bounds b = mesh.bounds;
            Gizmos.color = Color.clear;
            Gizmos.matrix = transform.localToWorldMatrix;
            Gizmos.DrawCube(b.center, b.extents * 2);
            Gizmos.matrix = Matrix4x4.identity;
            Gizmos.color = Color.white;
        }
    }
#endif
}