#include "TextField.h"

#include <math.h>

#include "AlphaField.h"
#include "Border.h"
#include "Font.h"
#include "Globals.h"
#include "Image.h"
#include "KeyboardEvent.h"
#include "MouseEvent.h"
#include "Scroll.h"
#include "Text.h"
#include "ToolTip.h"
#include "Window.h"

using namespace Framework;

#ifndef test
#    define test

bool TextStyle::equals(const TextStyle& rhs)
{
    return fontSize == rhs.fontSize && fontColor == rhs.fontColor
        && selectedColor == rhs.selectedColor
        && selectedBackcroundColor == rhs.selectedBackcroundColor
        && underlined == rhs.underlined && selected == rhs.selected
        && interactParam == rhs.interactParam
        && rendererIndex == rhs.rendererIndex;
}
#endif

TextField::TextStyleManager::TextStyleManager()
    : ReferenceCounter(),
      renderer(new RCArray<TextRenderer>()),
      index(0),
      styleIndex(0),
      text(0)
{
    current.beginIndex = 0;
    current.fontColor = 0xFFFFFFFF;
    current.fontSize = 12;
    current.selected = 0;
    current.selectedColor = 0xFFFFFFFF;
    current.selectedBackcroundColor = 0xFF0000FF;
    current.underlined = 0;
    current.interactParam = 0;
    current.rendererIndex = 0;
    textStyle.add(current);
}

TextField::TextStyleManager::~TextStyleManager()
{
    if (renderer) renderer->release();
    if (text) text->release();
}

// Sets the style of a text section
//  begin: the start position of the section
//  end: the end position of the section (not included)
void TextField::TextStyleManager::setTextStyle(
    int begin, int end, TextStyle style)
{
    if (begin < 0 || begin > end || begin > text->getLength()) return;
    int sc = textStyle.getEntryCount();
    int index = -1;
    TextStyle s = textStyle.get(0);
    // search to the correct position in the style array
    for (int i = 0; i < sc; i++)
    {
        if (textStyle.get(i).beginIndex >= begin)
        {
            index = i;
            if (textStyle.get(i).beginIndex > begin)
                s = textStyle.get(i - 1);
            else
            {
                s = textStyle.get(i);
                textStyle.remove(i);
                sc--;
            }
            break;
        }
    }
    style.beginIndex = begin;
    s.beginIndex = end;
    if (index < 0)
    { // append styles at the end
        textStyle.add(style);
        textStyle.add(s);
    }
    else
    { // insert into the middle of the style array
        textStyle.add(style, index);
        for (int i = index + 1; i < sc + 1; i++)
        { // remove styles that were overwritten
            if (textStyle.get(i).beginIndex <= end
                && textStyle.get(i).beginIndex > begin)
            {
                s = textStyle.get(i);
                textStyle.remove(i);
                i--;
                sc--;
            }
        }
        s.beginIndex = end;
        textStyle.add(s, index + 1);
    }
    cleanupStyles();
}

// Removes a text section
//  begin: the index of the first affected character
//  end: the index of the first character after the section
void TextField::TextStyleManager::removeText(int begin, int end)
{
    int sc = textStyle.getEntryCount();
    for (int i = 1; i < sc; i++)
    {
        TextStyle s = textStyle.get(i);
        if (s.beginIndex >= begin && s.beginIndex < end)
        {
            textStyle.remove(i);
            i--;
            sc--;
        }
        if (s.beginIndex >= end)
        {
            s.beginIndex -= end - begin;
            textStyle.set(s, i);
        }
    }
    text->remove(begin, end);
    cleanupStyles();
}

// Inserts text at a specific position
//  pos: the position of the new text section
//  text: the new text
void TextField::TextStyleManager::insertText(int pos, const char* text)
{
    int len = textLength(text);
    this->text->insert(pos, text);
    int sc = textStyle.getEntryCount();
    for (int i = 0; i < sc; i++)
    {
        TextStyle s = textStyle.get(i);
        if (s.beginIndex > pos)
        {
            s.beginIndex += len;
            textStyle.set(s, i);
        }
    }
    cleanupStyles();
}

// Removes unnecessary duplicate styles
void TextField::TextStyleManager::cleanupStyles()
{
    int sc = textStyle.getEntryCount();
    TextStyle last = textStyle.get(0);
    for (int i = 1; i < sc; i++)
    {
        if (textStyle.get(i).beginIndex == last.beginIndex)
        {
            last = textStyle.get(i);
            textStyle.remove(i - 1);
            i--;
            sc--;
            continue;
        }
        if (textStyle.get(i).equals(last)
            || (text && textStyle.get(i).beginIndex > text->getLength()))
        {
            textStyle.remove(i);
            i--;
            sc--;
        }
        else
            last = textStyle.get(i);
    }
}

// returns a reference to the style object
TextStyle& TextField::TextStyleManager::currentStyle()
{
    return current;
}

// returns the current text renderer
TextRenderer* TextField::TextStyleManager::zCurrentRenderer()
{
    if (renderer->getEntryCount() == 0) return 0;
    TextRenderer* tr = renderer->z(current.rendererIndex);
    if (!tr) tr = renderer->z(0);
    if (tr) tr->setFontSize(current.fontSize);
    return tr;
}

// changes the content of the style object to the style of the next character
bool TextField::TextStyleManager::nextStyle()
{
    index++;
    if (textStyle.getEntryCount() > styleIndex + 1
        && index >= textStyle.get(styleIndex + 1).beginIndex)
        current = textStyle.get(styleIndex++ + 1);
    return text && index < text->getLength();
}

// changes the content of the style object to the style of the specified
// character
//  index: the index of the character to jump to
//  returns 0 if the character does not exist
bool TextField::TextStyleManager::stepTo(int index)
{
    resetIteration();
    while (this->index < index && nextStyle())
        ;
    return text && this->index == index;
}

// changes the content of the style object to the style of the first character
void TextField::TextStyleManager::resetIteration()
{
    index = 0;
    styleIndex = 0;
    current = textStyle.get(0);
}

// Returns the style of a specific character\n//  index: The index of the
// character
TextStyle TextField::TextStyleManager::getTextStyle(int index) const
{
    TextStyle last = textStyle.get(0);
    int ind = 0;
    for (auto i = textStyle.begin(); i && ind <= index; ind++)
    {
        if (i._.beginIndex <= ind)
        {
            last = i;
            i++;
        }
    }
    return last;
}

// Contents of the TextField class from TextField.h
// Constructor
TextField::TextField()
    : DrawableBackground(),
      tm(new TextStyleManager()),
      autoLineBreakSpacing(0),
      showChar(0),
      cpos(0),
      tickVal(0),
      mouseKlick(0)
{
    charEvent = 0;
    horizontalScrollBar = new HScrollBar();
    vertikalScrollBar = new VScrollBar();
    this->setMouseEvent(_ret1ME);
    this->setKeyboardEvent(_ret1TE);
}

// Destructor
TextField::~TextField()
{
    tm->release();
    if (autoLineBreakSpacing) autoLineBreakSpacing->release();
}

void TextField::doMouseEvent(MouseEvent& me, bool userRet) // Mouse event
{
    if (!userRet) return;
    if (hasStyleNot(Style::Editable))
    {
        int rbr = 0;
        if (border) rbr = border->getRWidth();
        if (((vertikalScrollBar && hasStyle(Style::VScroll))
                || (horizontalScrollBar && hasStyle(Style::HScroll)))
            && me.mx > rbr && me.mx < gr.x - rbr && me.my > rbr
            && me.my < gr.y - rbr)
        {
            me.processed |= vertikalScrollBar->doMouseMessage(
                gr.x - rbr - 15, rbr, 15, gr.y - rbr * 2, me);
            me.processed |= horizontalScrollBar->doMouseMessage(rbr,
                gr.y - rbr * 2 - 15,
                gr.x - rbr * 2
                    - ((vertikalScrollBar && hasStyle(Style::VScroll)) ? 15
                                                                       : 0),
                15,
                me);
        }
        if (me.mx >= 0 && me.mx <= gr.x && me.my >= 0 && me.my <= gr.y
            && !me.processed && hasStyle(Style::Visible))
        {
            int scrollHi = (vertikalScrollBar && hasStyle(Style::VScroll))
                             ? vertikalScrollBar->getScroll()
                             : 0;
            int scrollBr = (horizontalScrollBar && hasStyle(Style::HScroll))
                             ? horizontalScrollBar->getScroll()
                             : 0;
            int xxx = me.mx - rbr + scrollBr;
            int yyy = me.my - rbr + scrollHi;
            int mouseChar = getTextIndexAt(xxx, yyy);
            if (mouseChar >= 0)
            {
                TextStyle s = tm->getTextStyle(mouseChar);
                if (charEvent && s.interactParam)
                    charEvent(mouseChar, s.interactParam, me);
            }
            if (charEvent) me.processed = 1;
        }
        mouseKlick = 0;
        return;
    }
    if (!me.processed)
    {
        if (hasStyleNot(Style::Focus))
        {
            mouseKlick = 0;
            if (me.id == Framework::ME_PLeft) addStyle(Style::Focus);
        }
        int rbr = 0;
        if (border) rbr = border->getRWidth();
        if (vertikalScrollBar && hasStyle(Style::VScroll))
        {
            if (vertikalScrollBar->doMouseMessage(
                    gr.x - rbr - 15, rbr, 15, gr.y - rbr * 2, me))
            {
                me.processed = 1;
                return;
            }
        }
        if (horizontalScrollBar && hasStyle(Style::HScroll))
        {
            if (horizontalScrollBar->doMouseMessage(rbr,
                    gr.y - rbr - 15,
                    gr.x - rbr * 2
                        - ((vertikalScrollBar && hasStyle(Style::VScroll)) ? 15
                                                                           : 0),
                    15,
                    me))
            {
                me.processed = 1;
                return;
            }
        }
        bool shift = getKeyState(T_Shift);
        bool strg = getKeyState(T_Strg);
        int tbr = getTextWidth();
        int thi = getTextHeight();
        int scrollHi = (vertikalScrollBar && hasStyle(Style::VScroll))
                         ? vertikalScrollBar->getScroll()
                         : 0;
        int scrollBr = (horizontalScrollBar && hasStyle(Style::HScroll))
                         ? horizontalScrollBar->getScroll()
                         : 0;
        int xxx = me.mx - rbr + scrollBr;
        int yyy = me.my - rbr + scrollHi;
        int mouseChar = getTextIndexAt(xxx, yyy);
        int scrollWidth = (vertikalScrollBar && hasStyle(Style::VScroll)) * 15;
        int scrollHeight
            = (horizontalScrollBar && hasStyle(Style::HScroll)) * 15;
        if (hasStyle(Style::HCenter))
            xxx -= (((gr.x - scrollWidth) / 2) - tbr / 2) - rbr;
        if (hasStyle(Style::VCenter) && hasStyleNot(Style::VScroll))
            yyy -= (((gr.y - scrollHeight) / 2) - thi / 2) - rbr;
        if (mouseChar >= 0)
        {
            TextStyle s = tm->getTextStyle(mouseChar);
            if (charEvent) charEvent(mouseChar, s.interactParam, me);
        }
        if (me.mx < gr.x - rbr - 15)
        {
            if (tm->renderer)
            {
                int ncpos = getCurserPosAt(xxx, yyy);
                if (me.id == Framework::ME_PLeft)
                {
                    if (ncpos != -1)
                    {
                        if (shift && cpos != ncpos)
                            addSelahl(MIN(cpos, ncpos), MAX(cpos, ncpos));
                        else if (!shift && !mouseKlick && !strg)
                            deselectSelahl();
                        cpos = ncpos;
                        rend = 1;
                        if (vertikalScrollBar && hasStyle(Style::VScroll))
                            updateVScroll();
                        if (horizontalScrollBar && hasStyle(Style::HScroll))
                            updateHScroll();
                    }
                    mouseKlick = 1;
                }
                if (me.id == ME_Move && mouseKlick)
                {
                    if (ncpos != -1)
                    {
                        rend = 1;
                        if (cpos != ncpos)
                            invertSelection(MIN(cpos, ncpos), MAX(cpos, ncpos));
                        cpos = ncpos;
                        if (vertikalScrollBar && hasStyle(Style::VScroll))
                            updateVScroll(cpos);
                        if (horizontalScrollBar && hasStyle(Style::HScroll))
                            updateHScroll(cpos);
                    }
                }
                if (me.id == ME_RLeft)
                {
                    if (ncpos != -1)
                    {
                        rend = 1;
                        if (cpos != ncpos)
                            invertSelection(MIN(cpos, ncpos), MAX(cpos, ncpos));
                        cpos = ncpos;
                        if (vertikalScrollBar && hasStyle(Style::VScroll))
                            updateVScroll(cpos);
                        if (horizontalScrollBar && hasStyle(Style::HScroll))
                            updateHScroll(cpos);
                    }
                    mouseKlick = 0;
                }
            }
        }
    }
    me.processed = 1;
}

int TextField::getTextHeight() const
{
    if (!tm || !tm->text) return 0;
    tm->resetIteration();
    int th = 0;
    int len = tm->text->getLength();
    const char* text = tm->text->getText();
    Text txtWithLineBreaks;
    if (hasStyle(Style::AutoLineBreak))
    {
        txtWithLineBreaks = addLineBreaksToText(tm->text->getText(),
            autoLineBreakSpacing ? autoLineBreakSpacing->getText() : "");
        text = txtWithLineBreaks;
        len = txtWithLineBreaks.getLength();
    }
    int max = 0;
    int distance = 0;
    for (int i = 0; i < len; i++)
    {
        if (text[i] == '\n')
        {
            th += max + distance;
            distance = 0;
            max = 0;
            tm->nextStyle();
            continue;
        }
        TextRenderer* r = tm->zCurrentRenderer();
        if (r)
        {
            int tmp = r->getRowHeight();
            max = max >= tmp ? max : tmp;
            if (max == tmp) distance = r->getLineSpacing();
        }
        tm->nextStyle();
    }
    if (max > 0) th += max;
    return th;
}

int TextField::getTextWidth() const
{
    if (!tm || !tm->text) return 0;
    tm->resetIteration();
    int maxBr = 0;
    int len = tm->text->getLength();
    const char* text = tm->text->getText();
    Text txtWithLineBreaks;
    if (hasStyle(Style::AutoLineBreak))
    {
        txtWithLineBreaks = addLineBreaksToText(tm->text->getText(),
            autoLineBreakSpacing ? autoLineBreakSpacing->getText() : "");
        text = txtWithLineBreaks;
        len = txtWithLineBreaks.getLength();
    }
    int lineBr = 0;
    char buff[] = {0, 0};
    for (int i = 0; i < len; i++)
    {
        buff[0] = text[i];
        if (text[i] == '\n')
        {
            maxBr = maxBr >= lineBr ? maxBr : lineBr;
            lineBr = 0;
            tm->nextStyle();
            continue;
        }
        TextRenderer* r = tm->zCurrentRenderer();
        if (r) lineBr += r->getTextWidth(buff);
        tm->nextStyle();
    }
    if (lineBr > 0) maxBr = maxBr >= lineBr ? maxBr : lineBr;
    return maxBr;
}

// charEvent: a function that is called when the mouse is on a
// specific character and the interactParam in the style != 0
//  call: charEvent( charIndex, interactParam, mouseEreignis );
void TextField::setCharEvent(
    std::function<void(int, int, MouseEvent me)> charEvent)
{
    this->charEvent = charEvent;
}

void TextField::setText(Text* txt) // sets the displayed text
{
    rwLock.lockWrite();
    if (!tm->text) tm->text = new Text();
    tm->text->setText(*txt);
    if (hasStyle(Style::VScroll)) updateVScroll();
    if (hasStyle(Style::HScroll)) updateHScroll();
    rwLock.unlockWrite();
    rend = 1;
    txt->release();
}

void TextField::setTextZ(Text* txt) // sets a pointer to the displayed text
{
    rwLock.lockWrite();
    if (tm->text) tm->text->release();
    tm->text = txt;
    if (hasStyle(Style::VScroll)) updateVScroll();
    if (hasStyle(Style::HScroll)) updateHScroll();
    rend = 1;
    rwLock.unlockWrite();
}

void TextField::setText(const char* txt) // sets the displayed text
{
    rwLock.lockWrite();
    if (!tm->text) tm->text = new Text();
    tm->text->setText(txt);
    if (hasStyle(Style::VScroll)) updateVScroll();
    if (hasStyle(Style::HScroll)) updateHScroll();
    rend = 1;
    rwLock.unlockWrite();
}

// sets the text with styles
//  txt: the text
//  format: \x1: enables underline
//          \x2FF: sets the font size for the following text.
//              FF is a two-digit hex value
//          \x3AARRGGBB: sets the font color.
//              AARRGGBB is an 8-digit hex value with two characters each
//              for alpha, red, green and blue
//          \x4AARRGGBB: sets the color of the selected text.
//              AARRGGBB is an 8-digit hex value with two characters each
//              for alpha, red, green and blue
//          \x5AARRGGBB: sets the background color of the selected text.
//              AARRGGBB is an 8-digit hex value with two characters each
//              for alpha, red, green and blue
//          \x6FF: sets text renderer index.
//              FF is a two-digit hex value
//          \x7: deactivates underline
//          \x8FFFFFFFF: set interact param.
//              FFFFFFFF is an 8-digit hex value
void TextField::setFormattedText(const char* txt)
{
    rwLock.lockWrite();
    if (!tm->text) tm->text = new Text();
    tm->textStyle.clear();
    TextStyle current;
    current.beginIndex = 0;
    current.fontColor = 0xFFFFFFFF;
    current.fontSize = 12;
    current.selected = 0;
    current.selectedColor = 0xFFFFFFFF;
    current.selectedBackcroundColor = 0xFF0000FF;
    current.underlined = 0;
    current.interactParam = 0;
    current.rendererIndex = 0;
    tm->textStyle.add(current);
    Text result = "";
    for (int i = 0; 1; i++)
    {
        bool br = 0;
        current.beginIndex = result.getLength();
        switch (txt[i])
        {
        case 0:
            br = 1;
            break;
        case 1:
            current.underlined = 1;
            tm->textStyle.add(current);
            break;
        case 2:
            {
                Text p = "0x";
                p.append(txt + i + 1, 2);
                current.fontSize = (unsigned char)(int)p;
                tm->textStyle.add(current);
                i += 2;
                break;
            }
        case 3:
            {
                Text p = "0x";
                p.append(txt + i + 1, 8);
                current.fontColor = (int)p;
                tm->textStyle.add(current);
                i += 8;
            }
            break;
        case 4:
            {
                Text p = "0x";
                p.append(txt + i + 1, 8);
                current.selectedColor = (int)p;
                tm->textStyle.add(current);
                i += 8;
                break;
            }
        case 5:
            {
                Text p = "0x";
                p.append(txt + i + 1, 8);
                current.selectedBackcroundColor = (int)p;
                tm->textStyle.add(current);
                i += 8;
                break;
            }
        case 6:
            {
                Text p = "0x";
                p.append(txt + i + 1, 2);
                current.rendererIndex = (unsigned char)(int)p;
                tm->textStyle.add(current);
                i += 2;
            }
            break;
        case 7:
            current.underlined = 0;
            tm->textStyle.add(current);
            break;
        case 8:
            {
                Text p = "0x";
                p.append(txt + i + 1, 8);
                current.interactParam = (int)p;
                tm->textStyle.add(current);
                i += 8;
            }
            break;
        default:
            result.append(txt[i]);
        }
        if (br) break;
    }
    tm->text->setText(result);
    tm->cleanupStyles();
    if (hasStyle(Style::VScroll)) updateVScroll();
    if (hasStyle(Style::HScroll)) updateHScroll();
    rend = 1;
    rwLock.unlockWrite();
}

// inserts line breaks so that the text does not exceed the width of the text
// field
void TextField::addLineBreaks(const char* spacing)
{
    if (!tm->text) return;
    setFormattedText(addLineBreaksToText(tm->text->getText(), spacing));
}

Text TextField::addLineBreaksToText(
    const char* txt, const char* spacing, bool includeFormat) const
{
    int lastPos = -1;
    int lastPos2 = -1;
    int x = 0;
    Text result = "";
    int len = (int)strlen(txt);
    int maxBr = getWidth();
    if (hasStyle(Style::VScroll) && vertikalScrollBar) maxBr -= 15;
    tm->resetIteration();
    TextStyle last;
    last.beginIndex = 0;
    last.fontColor = 0xFFFFFFFF;
    last.fontSize = 12;
    last.selected = 0;
    last.selectedColor = 0xFFFFFFFF;
    last.selectedBackcroundColor = 0xFF0000FF;
    last.underlined = 0;
    last.interactParam = 0;
    last.rendererIndex = 0;
    for (int i = 0; i < len; ++i)
    {
        if (last.fontSize != tm->current.fontSize && includeFormat)
        {
            Text param("\x2");
            param.appendHex((char)tm->current.fontSize);
            result += param;
            last.fontSize = tm->current.fontSize;
        }
        if (last.fontColor != tm->current.fontColor && includeFormat)
        {
            Text param("\x3");
            param.appendHex(tm->current.fontColor);
            result += param;
            last.fontColor = tm->current.fontColor;
        }
        if (last.selectedColor != tm->current.selectedColor && includeFormat)
        {
            Text param("\x4");
            param.appendHex(tm->current.selectedColor);
            result += param;
            last.selectedColor = tm->current.selectedColor;
        }
        if (last.selectedBackcroundColor != tm->current.selectedBackcroundColor
            && includeFormat)
        {
            Text param("\x5");
            param.appendHex(tm->current.selectedBackcroundColor);
            result += param;
            last.selectedBackcroundColor = tm->current.selectedBackcroundColor;
        }
        if (last.underlined != tm->current.underlined && includeFormat)
        {
            char tmp[2] = {tm->current.underlined ? (char)1 : (char)7, 0};
            result += tmp;
            last.underlined = tm->current.underlined;
        }
        if (last.interactParam != tm->current.interactParam && includeFormat)
        {
            Text param("\x8");
            param.appendHex(tm->current.interactParam);
            result += param;
            last.interactParam = tm->current.interactParam;
        }
        if (last.rendererIndex != tm->current.rendererIndex && includeFormat)
        {
            Text param("\x6");
            param.appendHex((char)tm->current.rendererIndex);
            result += param;
            last.rendererIndex = tm->current.rendererIndex;
        }
        if (txt[i] == ' ')
        {
            lastPos = i;
            lastPos2 = result.getLength();
            x += tm->zCurrentRenderer()->getTextWidth(" ");
            result += " ";
            tm->nextStyle();
            continue;
        }
        if (txt[i] == '\t')
        {
            lastPos = i;
            lastPos2 = result.getLength();
            x += tm->zCurrentRenderer()->getTextWidth("\t");
            result += "\t";
            tm->nextStyle();
            continue;
        }
        if (txt[i] == '\n')
        {
            x = 0;
            lastPos = -1;
            lastPos2 = -1;
            result += "\n";
            tm->nextStyle();
            continue;
        }
        char buff[2] = {txt[i], 0};
        x += tm->zCurrentRenderer()->getTextWidth(buff);
        result += buff;
        if (x > maxBr && lastPos > -1)
        {
            result.remove(lastPos2, result.getLength());
            result += "\n";
            result += spacing;
            x = tm->zCurrentRenderer()->getTextWidth(spacing);
            i = lastPos;
            tm->stepTo(lastPos);
            lastPos = -1;
            lastPos2 = -1;
            last = tm->currentStyle();
        }
        tm->nextStyle();
    }
    return result;
}

void TextField::setAutoLineBreakSpacing(const char* spacing)
{
    if (!autoLineBreakSpacing) autoLineBreakSpacing = new Text();
    autoLineBreakSpacing->setText(spacing);
}

// Sets the style of a text section
//  begin: the start position of the section
//  end: the end position of the section (not included)
void TextField::setTextStyle(int begin, int end, TextStyle style)
{
    tm->setTextStyle(begin, end, style);
}

void TextField::addRow(const char* zeile) // appends a line
{
    if (tm->text)
    {
        Text* txt = new Text(zeile);
        if (zeile[txt->getLength() - 1] != '\n') txt->append("\n");
        TextRenderer* r = tm->renderer->z(0);
        if (tm->textStyle.get(tm->textStyle.getEntryCount() - 1).rendererIndex
            < tm->renderer->getEntryCount())
            r = tm->renderer->z(
                tm->textStyle.get(tm->textStyle.getEntryCount() - 1)
                    .rendererIndex);
        if (r)
        {
            bool vs = vertikalScrollBar && hasStyle(Style::VScroll);
            int rbr
                = (border && hasStyle(Style::Border)) ? border->getRWidth() : 0;
            r->setFontSize(
                tm->textStyle.get(tm->textStyle.getEntryCount() - 1).fontSize);
            r->formatText(txt, gr.x - ((int)vs * 15) - rbr * 2);
        }
        rwLock.lockWrite();
        tm->text->append(txt->getText());
        rwLock.unlockWrite();
        txt->release();
        if (hasStyle(Style::VScroll)) updateVScroll();
        if (hasStyle(Style::HScroll)) updateHScroll();
        rend = 1;
    }
}

// Appends a line to the text
//  zeile: The new line
//  color: The color of the line
void TextField::addRow(const char* zeile, int color)
{
    if (tm->text)
    {
        Text* txt = new Text(zeile);
        if (zeile[txt->getLength() - 1] != '\n') txt->append("\n");
        TextRenderer* r = tm->renderer->z(0);
        if (tm->textStyle.get(tm->textStyle.getEntryCount() - 1).rendererIndex
            < tm->renderer->getEntryCount())
            r = tm->renderer->z(
                tm->textStyle.get(tm->textStyle.getEntryCount() - 1)
                    .rendererIndex);
        if (r)
        {
            bool vs = vertikalScrollBar && hasStyle(Style::VScroll);
            int rbr
                = (border && hasStyle(Style::Border)) ? border->getRWidth() : 0;
            r->setFontSize(
                tm->textStyle.get(tm->textStyle.getEntryCount() - 1).fontSize);
            r->formatText(txt, gr.x - ((int)vs * 15) - rbr * 2);
        }
        rwLock.lockWrite();
        tm->text->append(txt->getText());
        setFontColor(tm->text->getLength() - txt->getLength(),
            tm->text->getLength(),
            color);
        rwLock.unlockWrite();
        txt->release();
        if (hasStyle(Style::VScroll)) updateVScroll();
        if (hasStyle(Style::HScroll)) updateHScroll();
        rend = 1;
    }
}

// Deselects all text sections
void TextField::deselectSelahl()
{
    for (int i = 0; i < tm->textStyle.getEntryCount(); i++)
    {
        TextStyle s = tm->textStyle.get(i);
        if (s.selected)
        {
            s.selected = 0;
            tm->textStyle.set(s, i);
        }
    }
    tm->cleanupStyles();
}

void TextField::setSelection(int pos1, int pos2) // sets the selected text
{
    deselectSelahl();
    TextStyle s = tm->getTextStyle(pos1);
    s.selected = 1;
    tm->setTextStyle(pos1, pos2, s);
}

void TextField::setSelection(Point& selection)
{
    deselectSelahl();
    TextStyle s = tm->getTextStyle(selection.x);
    s.selected = 1;
    tm->setTextStyle(selection.x, selection.y, s);
    rend = 1;
}

void TextField::addSelahl(int pos1, int pos2)
{
    TextStyle s = tm->getTextStyle(pos1);
    s.selected = 1;
    tm->setTextStyle(pos1, pos2, s);
}

void TextField::addSelahl(Point& selection)
{
    TextStyle s = tm->getTextStyle(selection.x);
    s.selected = 1;
    tm->setTextStyle(selection.x, selection.y, s);
    rend = 1;
}

// Sets the selected text section
//  begin: The cursor position in the text
//  end: The position in the text up to which the text should be colored
void TextField::invertSelection(int begin, int end)
{
    for (int i = begin; i < end; i++)
    {
        TextStyle s = tm->getTextStyle(i);
        s.selected = !s.selected;
        tm->setTextStyle(i, i + 1, s);
        rend = 1;
    }
}

// replaces all selected text sections with a text
//  text: the new text
void TextField::replaceSelection(const char* text)
{
    tm->cleanupStyles();
    int sa = tm->textStyle.getEntryCount();
    int last = tm->text->getLength();
    int si = 0;
    for (int i = sa - 1; i >= 0; i--)
    {
        TextStyle s = tm->textStyle.get(i);
        si = i;
        if (s.selected)
        {
            if ((i > 0 && !tm->textStyle.get(i - 1).selected) || i == 0)
            {
                s.selected = false;
                tm->textStyle.set(s, si);
                tm->removeText(s.beginIndex, last);
                tm->insertText(s.beginIndex, text);
            }
        }
        else
            last = s.beginIndex;
    }
}

void TextField::setTextRendererZ(TextRenderer* textRd)
{
    if (tm->renderer)
        tm->renderer->clear();
    else
        tm->renderer = new RCArray<TextRenderer>();
    tm->renderer->add(textRd);
    rend = 1;
}

// Adds a TextRenderer
//  textRd: The text renderer
void TextField::addTextRendererZ(TextRenderer* textRd)
{
    if (!tm->renderer) tm->renderer = new RCArray<TextRenderer>();
    tm->renderer->add(textRd);
}

// Sets the used TextRenderers
//  textRd: The text renderers
void TextField::setTextRendererZ(RCArray<TextRenderer>* textRd)
{
    if (tm->renderer) tm->renderer->release();
    tm->renderer = textRd;
}

void TextField::setFontZ(Font* font) // sets a pointer to the font
{
    if (!tm->renderer) tm->renderer = new RCArray<TextRenderer>();
    if (!tm->renderer->getEntryCount())
        tm->renderer->add(new TextRenderer(font));
    else
        tm->renderer->z(0)->setFontZ(font);
    rend = 1;
}

// Sets a pointer to the font
//  rendererIndex: The index of the renderer whose font should be set
//  font: The font to be used for text drawing.
void TextField::setFontZ(int rendererIndex, Font* font)
{
    if (!tm->renderer) tm->renderer = new RCArray<TextRenderer>();
    if (tm->renderer->getEntryCount() <= rendererIndex)
        tm->renderer->add(new TextRenderer(font), rendererIndex);
    else
        tm->renderer->z(rendererIndex)->setFontZ(font);
}

void TextField::setFontSize(unsigned char gr) // sets the font size
{
    TextStyle s = tm->textStyle.get(0);
    s.fontSize = gr;
    tm->textStyle.set(s, 0);
    rend = 1;
}

// Sets the font size (default: 12)
//  begin: The index of the first affected character
//  end: The index of the first unaffected character
//  gr: The font size to be used for text drawing
void TextField::setFontSize(int begin, int end, unsigned char gr)
{
    TextStyle s = tm->getTextStyle(begin);
    s.fontSize = gr;
    tm->setTextStyle(begin, end, s);
    rend = 1;
}

void TextField::setFontColor(int fc) // sets the font color
{
    TextStyle s = tm->textStyle.get(0);
    s.fontColor = fc;
    tm->textStyle.set(s, 0);
    rend = 1;
}

// Sets the font color
//  begin: The index of the first affected character
//  end: The index of the first unaffected character
//  fc: The color to be used for text drawing
void TextField::setFontColor(int begin, int end, int fc)
{
    TextStyle s = tm->getTextStyle(begin);
    s.fontColor = fc;
    tm->setTextStyle(begin, end, s);
    rend = 1;
}

void TextField::setSchowChar(unsigned char c) // for password field *
{
    showChar = c;
    rend = 1;
}

void TextField::setVScrollToRow(int zeile) // scrolls to line
{
    if (vertikalScrollBar && tm->renderer && tm->renderer->getEntryCount()
        && tm->text && hasStyle(Style::Multiline))
    {
        rwLock.lockWrite();
        tm->resetIteration();
        int len = tm->text->getLength();
        int y = 0;
        int lnum = 0;
        const char* text = tm->text->getText();
        Text txtWithLineBreaks;
        if (hasStyle(Style::AutoLineBreak))
        {
            txtWithLineBreaks = addLineBreaksToText(tm->text->getText(),
                autoLineBreakSpacing ? autoLineBreakSpacing->getText() : "");
            text = txtWithLineBreaks;
            len = txtWithLineBreaks.getLength();
        }
        int max = 0;
        for (int i = 0; i < len && lnum < zeile; i++)
        {
            if (text[i] == '\n')
            {
                lnum++;
                y += max;
                max = 0;
                tm->nextStyle();
                continue;
            }
            TextRenderer* r = tm->zCurrentRenderer();
            if (r)
            {
                int tmp = r->getLineSpacing() + r->getRowHeight();
                max = max >= tmp ? max : tmp;
            }
            tm->nextStyle();
        }
        rwLock.unlockWrite();
        vertikalScrollBar->scroll(y);
        rend = 1;
    }
}

void TextField::updateVScroll(int pos) // scrolls down
{
    if (pos == -1) pos = cpos;
    if (vertikalScrollBar)
    {
        int sPos = 0;
        int hi = 0;
        int sPosZH = 0;
        if (tm->text && tm->renderer)
        {
            if (hasStyleNot(Style::Multiline)) tm->text->remove('\n');
            hi = gr.y;
            if (hasStyle(Style::Border) && border)
                hi -= border->getRWidth() * 2;
            if (hasStyle(Style::HScroll) && horizontalScrollBar) hi -= 15;
            int th = 0;
            rwLock.lockWrite();
            tm->resetIteration();
            int len = tm->text->getLength();
            const char* text = tm->text->getText();
            Text txtWithLineBreaks;
            if (hasStyle(Style::AutoLineBreak))
            {
                txtWithLineBreaks = addLineBreaksToText(tm->text->getText(),
                    autoLineBreakSpacing ? autoLineBreakSpacing->getText()
                                         : "");
                text = txtWithLineBreaks;
                len = txtWithLineBreaks.getLength();
            }
            int max = 0;
            int lastMax = 0;
            for (int i = 0; i < len; i++)
            {
                if (text[i] == '\n')
                {
                    if (i <= pos)
                    {
                        sPos += max;
                        sPosZH = max;
                    }
                    th += max;
                    lastMax = max;
                    max = 0;
                    tm->nextStyle();
                    continue;
                }
                TextRenderer* r = tm->zCurrentRenderer();
                if (r)
                {
                    int tmp = r->getLineSpacing() + r->getRowHeight();
                    max = max >= tmp ? max : tmp;
                }
                tm->nextStyle();
            }
            if (max != lastMax && max > 0)
            {
                th += max;
                lastMax = max;
            }
            th += lastMax;
            rwLock.unlockWrite();
            vertikalScrollBar->update(th, hi);
        }
        if (sPos - sPosZH < vertikalScrollBar->getScroll())
            vertikalScrollBar->scroll(sPos - sPosZH);
        if (sPos + sPosZH > vertikalScrollBar->getScroll()
                                + vertikalScrollBar->getScrollData()->anzeige)
            vertikalScrollBar->scroll(sPos + sPosZH * 2 - hi);
        rend = 1;
    }
}

void TextField::updateHScroll(int pos) // scrolls to cursor position
{
    if (pos == -1) pos = cpos;
    rwLock.lockWrite();
    if (horizontalScrollBar && tm->text && tm->renderer)
    {
        if (hasStyleNot(Style::Multiline)) tm->text->remove('\n');
        int br = gr.x;
        if (hasStyle(Style::Border) && border) br -= border->getRWidth() * 2;
        if (hasStyle(Style::VScroll) && vertikalScrollBar) br -= 15;
        tm->resetIteration();
        int maxBr = 0;
        int len = tm->text->getLength();
        const char* text = tm->text->getText();
        int lineBr = 0;
        char buff[] = {0, 0};
        int cbr = 0;
        for (int i = 0; i < len; i++)
        {
            buff[0] = text[i];
            if (text[i] == '\n')
            {
                maxBr = maxBr >= lineBr ? maxBr : lineBr;
                lineBr = 0;
                tm->nextStyle();
                continue;
            }
            TextRenderer* r = tm->zCurrentRenderer();
            if (r)
            {
                lineBr += r->getTextWidth(buff);
                if (i <= pos) cbr = lineBr;
            }
            tm->nextStyle();
        }
        maxBr = maxBr >= lineBr ? maxBr : lineBr;
        horizontalScrollBar->update(maxBr, br);
        if (cbr > horizontalScrollBar->getScroll()
                      + horizontalScrollBar->getScrollData()->anzeige)
            horizontalScrollBar->scroll(cbr - br);
        if (cbr < horizontalScrollBar->getScroll())
            horizontalScrollBar->scroll(cbr);
    }
    rwLock.unlockWrite();
}

// Returns the width in pixels needed to fully display the current text with
// the current styles
int TextField::getNeededWidth()
{
    int maxBr = 0;
    rwLock.lockRead();
    if (tm->text && tm->renderer) maxBr = getTextWidth();
    rwLock.unlockRead();
    bool vs = vertikalScrollBar && hasStyle(Style::VScroll);
    bool r = border && hasStyle(Style::Border);
    return maxBr + (r ? border->getRWidth() * 2 : 0) + (vs ? 15 : 0);
}

// Returns the height in pixels needed to fully display the current text with
// the current styles
int TextField::getNeededHeight()
{
    int th = 0;
    rwLock.lockRead();
    if (tm->text && tm->renderer) th = getTextHeight();
    rwLock.unlockRead();
    bool hs = horizontalScrollBar && hasStyle(Style::HScroll);
    bool r = border && hasStyle(Style::Border);
    return th + (r ? border->getRWidth() * 2 : 0) + (hs ? 15 : 0);
}

bool TextField::tick(double tickval) // tick
{
    if (hasStyle(Style::Focus))
    {
        if (tickVal < 0.5 && tickVal + tickval >= 0.5) rend = 1;
        if (tickVal >= 0.5 && tickVal + tickval >= 1) rend = 1;
        tickVal += tickval;
        if (tickVal >= 1) tickVal -= 1;
    }
    return DrawableBackground::tick(tickval);
}

void TextField::doKeyboardEvent(KeyboardEvent& te)
{
    bool ntakc = !te.processed;
    if (te.processed || hasStyleNot(Style::Focus)) return;
    if (!tak) return;
    getThis();
    if (tak(takParam, this, te))
    {
        if (hasStyleNot(Style::Editable))
        {
            release();
            return;
        }
        if (te.id == TE_Press)
        {
            bool shift = getKeyState(T_Shift);
            bool strg = getKeyState(T_Strg);
            switch (te.virtualKey)
            {
            case T_Entf:
                if (!tm->getTextStyle(cpos).selected)
                    tm->removeText(cpos, cpos + 1);
                else
                {
                    cpos = tm->getTextStyle(cpos).beginIndex;
                    while (cpos > 0 && tm->getTextStyle(cpos - 1).selected)
                        cpos = tm->getTextStyle(cpos - 1).beginIndex;
                }
                replaceSelection("");
                deselectSelahl();
                rend = 1;
                break;
            case T_BackSpace:
                if (!tm->getTextStyle(cpos).selected)
                {
                    tm->removeText(cpos - 1, cpos);
                    cpos--;
                }
                else
                {
                    cpos = tm->getTextStyle(cpos).beginIndex;
                    while (cpos > 0 && tm->getTextStyle(cpos - 1).selected)
                        cpos = tm->getTextStyle(cpos - 1).beginIndex;
                }
                replaceSelection("");
                deselectSelahl();
                rend = 1;
                break;
            case T_Enter:
                if (hasStyle(TextField::Style::Multiline))
                {
                    if (!tm->getTextStyle(cpos).selected)
                        tm->insertText(cpos, "\n");
                    else
                    {
                        cpos = tm->getTextStyle(cpos).beginIndex;
                        while (cpos > 0 && tm->getTextStyle(cpos - 1).selected)
                            cpos = tm->getTextStyle(cpos - 1).beginIndex;
                    }
                    replaceSelection("\n");
                    ++cpos;
                    rend = 1;
                }
                break;
            case T_Links:
                if (shift)
                {
                    if (strg)
                    {
                        int tmp = tm->text->getLKick(cpos);
                        invertSelection(tmp, cpos);
                        cpos = tmp;
                    }
                    else
                    {
                        invertSelection(cpos - 1, cpos);
                        --cpos;
                    }
                }
                else
                {
                    if (strg)
                        cpos = tm->text->getLKick(cpos);
                    else
                        --cpos;
                    deselectSelahl();
                }
                rend = 1;
                break;
            case T_Oben:
                {
                    int tmp = tm->text->getOKick(cpos);
                    invertSelection(tmp, cpos);
                    cpos = tmp;
                    if (!shift) deselectSelahl();
                    rend = 1;
                    break;
                }
            case T_Rechts:
                if (shift)
                {
                    if (strg)
                    {
                        int tmp = tm->text->getRKick(cpos);
                        invertSelection(cpos, tmp);
                        cpos = tmp;
                    }
                    else
                    {
                        invertSelection(cpos, cpos + 1);
                        ++cpos;
                    }
                }
                else
                {
                    if (strg)
                        cpos = tm->text->getRKick(cpos);
                    else
                        ++cpos;
                    deselectSelahl();
                }
                rend = 1;
                break;
            case T_Unten:
                {
                    int tmp = tm->text->getUKick(cpos);
                    invertSelection(cpos, tmp);
                    cpos = tmp;
                    if (!shift) deselectSelahl();
                    rend = 1;
                    break;
                }
            default:
                if (strg && te.id == TE_Press)
                {
                    if (te.virtualKey == 'c' || te.virtualKey == 'C')
                    {
                        int sa = tm->textStyle.getEntryCount();
                        int length = 0;
                        for (int i = 0; i < sa; i++)
                        {
                            TextStyle s = tm->textStyle.get(i);
                            if (s.selected)
                            {
                                int max = tm->text->getLength();
                                if (i < sa - 1)
                                    max = tm->textStyle.get(i + 1).beginIndex;
                                length += max - s.beginIndex;
                            }
                        }
                        if (length)
                        {
                            char* txt = new char[length + 1];
                            txt[length] = 0;
                            int index = 0;
                            for (int i = 0; i < sa; i++)
                            {
                                TextStyle s = tm->textStyle.get(i);
                                if (s.selected)
                                {
                                    int max = tm->text->getLength();
                                    if (i < sa - 1)
                                        max = tm->textStyle.get(i + 1)
                                                  .beginIndex;
                                    memcpy(txt + index,
                                        tm->text->getText() + s.beginIndex,
                                        max - s.beginIndex);
                                    index += max - s.beginIndex;
                                }
                            }
                            TextKopieren(txt);
                            delete[] txt;
                        }
                        else
                            TextKopieren(tm->text->getText());
                    }
                    if (te.virtualKey == 'v' || te.virtualKey == 'V')
                    {
                        const char* txt = TextInsert();
                        if (!tm->getTextStyle(cpos).selected)
                            tm->insertText(cpos, txt);
                        else
                        {
                            cpos = tm->getTextStyle(cpos).beginIndex;
                            while (
                                cpos > 0 && tm->getTextStyle(cpos - 1).selected)
                                cpos = tm->getTextStyle(cpos - 1).beginIndex;
                        }
                        replaceSelection(txt);
                        cpos += textLength(txt);
                        rend = 1;
                    }
                    break;
                }
                if (isWritable(te.key[0]))
                {
                    if (!tm->getTextStyle(cpos).selected)
                        tm->insertText(cpos, te.key);
                    else
                    {
                        cpos = tm->getTextStyle(cpos).beginIndex;
                        while (cpos > 0 && tm->getTextStyle(cpos - 1).selected)
                            cpos = tm->getTextStyle(cpos - 1).beginIndex;
                    }
                    replaceSelection(te.key);
                    ++cpos;
                    rend = 1;
                }
                break;
            }
        }
        if (cpos < 0) cpos = 0;
        if (cpos > tm->text->getLength()) cpos = tm->text->getLength();
        if (hasStyle(Style::VScroll)) updateVScroll(cpos);
        if (hasStyle(Style::HScroll)) updateHScroll(cpos);
        te.processed = 1;
    }
    if (ntakc && te.processed && nTak) te.processed = nTak(ntakParam, this, te);
    release();
}

void TextField::render(Image& zRObj) // draws to zRObj
{
    if (hasStyleNot(Style::Visible)) return;
    DrawableBackground::render(zRObj);
    if (!tm->text || !tm->renderer) return;
    rwLock.lockRead();
    if (!zRObj.setDrawOptions(innenPosition, innenSize))
    {
        rwLock.unlockRead();
        return;
    }
    if (hasStyleNot(Style::Multiline)) tm->text->remove('\n');
    int tbr = getTextWidth();
    int thi = getTextHeight();
    int xxx = 0;
    int yyy = 0;
    int Width = innenSize.x;
    int height = innenSize.y;
    bool hs = horizontalScrollBar && hasStyle(Style::HScroll);
    bool vs = vertikalScrollBar && hasStyle(Style::VScroll);
    if (vs) yyy -= vertikalScrollBar->getScroll();
    if (hs) xxx -= horizontalScrollBar->getScroll();
    if (hasStyle(Style::HCenter) && !hs) xxx = (Width / 2) - tbr / 2;
    int x = xxx;
    int len = tm->text->getLength();
    const char* text = tm->text->getText();
    Text txtWithLineBreaks;
    if (hasStyle(Style::AutoLineBreak))
    {
        txtWithLineBreaks = addLineBreaksToText(tm->text->getText(),
            autoLineBreakSpacing ? autoLineBreakSpacing->getText() : "",
            0);
        text = txtWithLineBreaks;
        len = txtWithLineBreaks.getLength();
    }
    tm->resetIteration();
    if (thi == 0)
    {
        thi = tm->zCurrentRenderer()->getRowHeight();
    }
    if (hasStyle(Style::VCenter) && !vs) yyy = (height / 2) - thi / 2;
    int y = yyy;
    TextStyle& style = tm->currentStyle();
    int maxLH = 0;
    int corrector = 0;
    int realLen = tm->text->getLength();
    for (int i = 0; i <= len; i++)
    {
        int oldX = x;
        if (i < len && tm->zCurrentRenderer())
            tm->zCurrentRenderer()->renderChar(x,
                y,
                isWritable(showChar) ? showChar : text[i],
                zRObj,
                style.selected ? style.selectedColor : style.fontColor,
                style.underlined,
                style.selected,
                style.selectedBackcroundColor);
        if (i - corrector == cpos && tickVal <= 0.5 && hasStyle(Style::Focus)
            && hasStyle(Style::Editable) && tm->zCurrentRenderer())
            zRObj.drawLineV(
                oldX, y, tm->zCurrentRenderer()->getRowHeight(), 0xFFFF5555);
        if (i >= realLen || text[i] != tm->text->getText()[i])
        {
            corrector++;
        }
        if (tm->zCurrentRenderer())
        {
            int tmp = tm->zCurrentRenderer()->getRowHeight()
                    + tm->zCurrentRenderer()->getLineSpacing();
            maxLH = tmp > maxLH ? tmp : maxLH;
        }
        if (i < len && text[i] == '\n')
        {
            x = xxx;
            y += maxLH;
        }
        tm->nextStyle();
    }
    zRObj.releaseDrawOptions();
    rwLock.unlockRead();
}

// Constant
Text* TextField::getText() const // returns the text
{
    if (!tm->text) return 0;
    return dynamic_cast<Text*>(tm->text->getThis());
}

Text* TextField::zText() const // returns the text without reference counter
{
    return tm->text;
}

Font* TextField::getFont() const // returns the font
{
    tm->resetIteration();
    return tm->zCurrentRenderer() ? tm->zCurrentRenderer()->getFont() : 0;
}

Font* TextField::zFont() const // returns the font without reference counter
{
    tm->resetIteration();
    return tm->zCurrentRenderer() ? tm->zCurrentRenderer()->zFont() : 0;
}

// Returns the font.
//  rendererIndex: The index of the renderer whose font should be returned
//  return: 0, if the font was not set
Font* TextField::getFont(int rendererIndex) const
{
    if (tm->renderer && tm->renderer->z(rendererIndex))
        return tm->renderer->z(rendererIndex)->getFont();
    return 0;
}

// Returns the font without increased reference counter
//  rendererIndex: The index of the renderer whose font should be returned
//  return: 0, if the font was not set
Font* TextField::zFont(int rendererIndex) const
{
    if (tm->renderer && tm->renderer->z(rendererIndex))
        return tm->renderer->z(rendererIndex)->zFont();
    return 0;
}

TextRenderer* TextField::getTextRenderer() const
{
    tm->resetIteration();
    return dynamic_cast<TextRenderer*>(tm->zCurrentRenderer()->getThis());
}

TextRenderer* TextField::zTextRenderer() const
{
    tm->resetIteration();
    return tm->zCurrentRenderer();
}

// Returns the TextRenderer.
//  index: The index of the renderer to be returned
//  return: 0, if the TextRenderer was not set
TextRenderer* TextField::getTextRenderer(int index) const
{
    if (tm->renderer && tm->renderer->z(index)) return tm->renderer->get(index);
    return 0;
}

// Returns the TextRenderer without increased reference counter
//  index: The index of the renderer to be returned
//  return: 0, if the TextRenderer was not set
TextRenderer* TextField::zTextRenderer(int index) const
{
    if (tm->renderer && tm->renderer->z(index)) return tm->renderer->z(index);
    return 0;
}

unsigned char TextField::getFontSize() const // returns the font size
{
    tm->resetIteration();
    return tm->current.fontSize;
}

// Returns the font size
//  index: The index of the character
unsigned char TextField::getFontSize(int index) const
{
    tm->resetIteration();
    return tm->current.fontSize;
}

int TextField::getFontColor() const // returns the font color
{
    tm->resetIteration();
    return tm->current.fontColor;
}

// Returns the font color in A8R8G8B8 format
//  index: The index of the character
int TextField::getFontColor(int index) const
{
    return tm->getTextStyle(index).fontColor;
}

unsigned char TextField::getShowChar() const // returns the display character
{
    return showChar;
}

int TextField::getCursorPos() const
{
    return cpos;
}

// Returns 1 if the character is selected
//  index: The index of the character
bool TextField::isCharSelected(int index) const
{
    return tm->getTextStyle(index).selected;
}

// Returns the index of the character located under the mouse
//  mx: the x position of the mouse relative to the text field position
//  my: the y position of the mouse relative to the text field position
//  return: -1, if no character is at the position
int TextField::getTextIndexAt(int mx, int my) const
{
    if (!tm || !tm->text || !tm->zCurrentRenderer()) return -1;
    int tbr = getTextWidth();
    int thi = getTextHeight();
    int xxx = 0;
    int yyy = 0;
    int Width = innenSize.x;
    int height = innenSize.y;
    bool hs = horizontalScrollBar && hasStyle(Style::HScroll);
    bool vs = vertikalScrollBar && hasStyle(Style::VScroll);
    if (vs) yyy -= vertikalScrollBar->getScroll();
    if (hs) xxx -= horizontalScrollBar->getScroll();
    if (hasStyle(Style::HCenter) && !hs) xxx = (Width / 2) - tbr / 2;
    if (hasStyle(Style::VCenter) && !vs) yyy = (height / 2) - thi / 2;
    int x = xxx;
    int y = yyy;
    int len = tm->text->getLength();
    const char* text = tm->text->getText();
    Text txtWithLineBreaks;
    if (hasStyle(Style::AutoLineBreak))
    {
        txtWithLineBreaks = addLineBreaksToText(tm->text->getText(),
            autoLineBreakSpacing ? autoLineBreakSpacing->getText() : "");
        text = txtWithLineBreaks;
        len = txtWithLineBreaks.getLength();
    }
    tm->resetIteration();
    int corrector = 0;
    int maxLH = 0;
    int realLen = tm->text->getLength();
    for (int i = 0; i < len; i++)
    {
        if (i >= realLen || text[i] != tm->text->getText()[i])
        {
            corrector++;
        }
        char buff[2] = {isWritable(showChar) ? (char)showChar : text[i], 0};
        int tmpx = tm->zCurrentRenderer()->getTextWidth(buff);
        int tmpy = tm->zCurrentRenderer()->getRowHeight();
        if (mx >= x && mx < x + tmpx && my >= y && my < y + tmpy)
            return i - corrector;
        if (mx < x + tmpx && my < y + tmpy) return -1;
        x += tmpx;
        tmpy += tm->zCurrentRenderer()->getLineSpacing();
        maxLH = tmpy > maxLH ? tmpy : maxLH;
        if (text[i] == '\n')
        {
            x = xxx;
            y += maxLH;
        }
        tm->nextStyle();
    }
    return -1;
}

// Returns the index of the character before which the cursor is placed when
// the mouse is clicked
//  mx: the x position of the mouse relative to the text field position
//  my: the y position of the mouse relative to the text field position
int TextField::getCurserPosAt(int mx, int my) const
{
    if (!tm || !tm->text || !tm->zCurrentRenderer())
        return tm->text->getLength();
    int tbr = getTextWidth();
    int thi = getTextHeight();
    int xxx = 0;
    int yyy = 0;
    int Width = innenSize.x;
    int height = innenSize.y;
    bool hs = horizontalScrollBar && hasStyle(Style::HScroll);
    bool vs = vertikalScrollBar && hasStyle(Style::VScroll);
    if (vs) yyy -= vertikalScrollBar->getScroll();
    if (hs) xxx -= horizontalScrollBar->getScroll();
    if (hasStyle(Style::HCenter) && !hs) xxx = (Width / 2) - tbr / 2;
    if (hasStyle(Style::VCenter) && !vs) yyy = (height / 2) - thi / 2;
    int x = xxx;
    int y = yyy;
    int len = tm->text->getLength();
    const char* text = tm->text->getText();
    Text txtWithLineBreaks;
    if (hasStyle(Style::AutoLineBreak))
    {
        txtWithLineBreaks = addLineBreaksToText(tm->text->getText(),
            autoLineBreakSpacing ? autoLineBreakSpacing->getText() : "");
        text = txtWithLineBreaks;
        len = txtWithLineBreaks.getLength();
    }
    tm->resetIteration();
    int maxLH = 0;
    int realLen = tm->text->getLength();
    int corrector = 0;
    for (int i = 0; i < len; i++)
    {
        if (i >= realLen || text[i] != tm->text->getText()[i])
        {
            corrector++;
        }
        int tmpx = tm->zCurrentRenderer()->getCharWidth(
            isWritable(showChar) ? showChar : text[i]);
        int tmpy = tm->zCurrentRenderer()->getRowHeight()
                 + tm->zCurrentRenderer()->getLineSpacing();
        if (mx < x + tmpx / 2
            && my < y + tmpy - tm->zCurrentRenderer()->getLineSpacing() / 2)
            return i - corrector;
        x += tmpx + tm->zCurrentRenderer()->getCharSpacing();
        maxLH = tmpy > maxLH ? tmpy : maxLH;
        if (text[i] == '\n')
        {
            if (my >= y - tm->zCurrentRenderer()->getLineSpacing() / 2
                && my < y + maxLH
                            - tm->zCurrentRenderer()->getLineSpacing() / 2)
                return i - corrector;
            x = xxx;
            y += maxLH;
        }
        tm->nextStyle();
    }
    return tm->text->getLength();
}

// Returns the style of a specific character
//  index: The index of the character
TextStyle TextField::getTextStyle(int index) const
{
    return tm->getTextStyle(index);
}

// returns the string that is inserted after each line break when using
// the AutoLineBreak style
Text TextField::getAutoLineBreakSpacing() const
{
    return autoLineBreakSpacing ? *autoLineBreakSpacing : Text("");
}

Drawable* TextField::duplicate() const // Creates a copy of the drawing
{
    TextField* obj = new TextField();
    obj->setPosition(pos);
    obj->setSize(gr);
    obj->setMouseEventParameter(makParam);
    obj->setKeyboardEventParameter(takParam);
    obj->setMouseEvent(mak);
    obj->setKeyboardEvent(tak);
    if (toolTip) obj->setToolTipZ((ToolTip*)toolTip->duplicate());
    obj->setStyle(style);
    obj->tm->renderer->release();
    obj->tm->renderer
        = dynamic_cast<RCArray<TextRenderer>*>(tm->renderer->getThis());
    obj->tm->textStyle.clear();
    for (const auto& i : tm->textStyle)
        obj->tm->textStyle.add(i);
    obj->tm->index = tm->index;
    obj->tm->styleIndex = tm->styleIndex;
    obj->tm->current = tm->current;
    if (tm->text) obj->setText(tm->text->getText());
    obj->setBackgroundColor(backgroundColor);
    if (backgroundFeld)
        obj->setAlphaFieldZ((AlphaField*)backgroundFeld->duplicate());
    if (border) obj->setBorderZ((Border*)border->duplicate());
    if (backgroundImage)
        obj->setBackgroundImage(
            dynamic_cast<Image*>(backgroundImage->getThis()));
    if (vertikalScrollBar)
    {
        obj->setVerticalClickScroll(vertikalScrollBar->getClickScroll());
        obj->setVerticalScrollPos(vertikalScrollBar->getScroll());
        obj->setVerticalScrollColor(
            vertikalScrollBar->getColor(), vertikalScrollBar->getBgColor());
    }
    if (horizontalScrollBar)
    {
        obj->setHorizontalClickScroll(horizontalScrollBar->getClickScroll());
        obj->setHorizontalScrollPos(horizontalScrollBar->getScroll());
        obj->setHorizontalScrollColor(
            horizontalScrollBar->getColor(), horizontalScrollBar->getBgColor());
    }
    if (autoLineBreakSpacing)
    {
        obj->setAutoLineBreakSpacing(*autoLineBreakSpacing);
    }
    obj->setSchowChar(showChar);
    return obj;
}