//---Include---
#include "Text.h"

#include <iomanip>
#include <math.h>
#include <sstream>
#ifndef WIN32
#    include <string.h>
#endif
#include "Regex.h"

using namespace Framework;

DynamicBuffer::DynamicBuffer(std::function<int(std::stringbuf&)> onAppend)
    : std::stringbuf(),
      onAppend(onAppend)
{}

int DynamicBuffer::sync()
{
    return onAppend(*this);
}

FlushingOStream::FlushingOStream(
    DynamicBuffer* buffer, std::function<void()> onDestroy)
    : std::ostream(buffer),
      onDestroy(onDestroy)
{}

FlushingOStream::FlushingOStream(const Framework::FlushingOStream& stream)
    : std::ostream(stream.rdbuf())
{}

FlushingOStream::~FlushingOStream()
{
    flush();
    onDestroy();
}

Text::Text(char* t, int l)
    : ReferenceCounter(),
      txt(0),
      length(0),
      suchGBeg(0),
      suchGEnd(0),
      precision(-1),
      stringWriter(0)
{
    length = l;
    txt = t; // create new text
}

// Content of the Text class from Text.h
// Constructor
Text::Text()
    : ReferenceCounter(),
      txt(0),
      length(0),
      suchGBeg(0),
      suchGEnd(0),
      precision(-1),
      stringWriter(0)
{
    setText("");
}

Text::Text(const Text& txt)
    : ReferenceCounter(),
      txt(0),
      length(0),
      suchGBeg(0),
      suchGEnd(0),
      precision(-1),
      stringWriter(0)
{
    setText(txt, txt.length);
}

Text::Text(const char* t)
    : ReferenceCounter(),
      txt(0),
      length(0),
      suchGBeg(0),
      suchGEnd(0),
      precision(-1),
      stringWriter(0)
{
    setText(t); // set text
}

Framework::Text::Text(const char* txt, int offset, int length)
    : ReferenceCounter(),
      txt(0),
      length(0),
      suchGBeg(0),
      suchGEnd(0),
      precision(-1),
      stringWriter(0)
{
    this->length = length;
    this->txt = new char[length + 1];
    memcpy(this->txt, txt + offset, length);
    this->txt[length] = 0;
}

Text::Text(int zahl)
    : ReferenceCounter(),
      txt(0),
      length(0),
      suchGBeg(0),
      suchGEnd(0),
      precision(-1),
      stringWriter(0)
{
    *this = zahl;
}

// Creates a new Text object with a number as text
//  num: The number that should be in the text
Text::Text(double num)
    : Text()
{
    *this = num;
}

// Creates a new Text object with a number as text
//  num: The number that should be in the text
Text::Text(float num)
    : Text()
{
    *this = num;
}

// Destructor
Text::~Text()
{
    if (stringWriter) delete stringWriter;
    delete[] txt;
}

void Framework::Text::setTextZ(char* t, int l)
{
    delete[] txt; // delete old text
    length = l;
    txt = t; // create new text
}

void Text::toUpperCase()
{
    if (!txt) return;
    for (int i = 0; i < length; i++)
    {
        if (txt[i] >= 'a' && txt[i] <= 'z') txt[i] = (char)(txt[i] - 32);
        switch (txt[i])
        {
        case SpecialCharacters::SMALL_UE:
            txt[i] = SpecialCharacters::BIG_UE;
            break;
        case SpecialCharacters::SMALL_OE:
            txt[i] = SpecialCharacters::BIG_OE;
            break;
        case SpecialCharacters::SMALL_AE:
            txt[i] = SpecialCharacters::BIG_AE;
            break;
        }
    }
}

void Text::toLowerCase()
{
    if (!txt) return;
    for (int i = 0; i < length; i++)
    {
        if (txt[i] >= 'A' && txt[i] <= 'Z') txt[i] = (char)(txt[i] + 32);
        switch (txt[i])
        {
        case SpecialCharacters::BIG_UE:
            txt[i] = SpecialCharacters::SMALL_UE;
            break;
        case SpecialCharacters::BIG_OE:
            txt[i] = SpecialCharacters::SMALL_OE;
            break;
        case SpecialCharacters::BIG_AE:
            txt[i] = SpecialCharacters::SMALL_AE;
            break;
        }
    }
}

// non-constant
void Text::setSuchGrenzen(
    char gBeg, char gEnd) // for every search function, does not search between
                          // the characters gBeg and gEnd
{
    suchGBeg = gBeg;
    suchGEnd = gEnd;
}

void Text::setText(const char* t) // replaces the text
{
    setText(t, textLength(t)); // set text
}

// non-constant
void Text::setText(const char* t, int l) // replaces the text
{
    delete[] txt; // delete old text
    length = l;
    txt = new char[(__int64)length + 1]; // create new text
    for (int i = 0; i < length; ++i)     // fill text
        txt[i] = t[i];
    txt[length] = '\0'; // set text end
}

void Text::setText(const Text& t)
{
    setText(t, t.getLength()); // set text
}

void Text::append(char c) // appends to the text
{
    append(&c, 1);
}

void Text::append(const char* t) // appends to the text
{
    int tl = (int)textLength(t);                    // length of the argument
    char* res = new char[(__int64)tl + length + 1]; // create new text
    for (int i = 0; i < length; ++i)                // fill with current text
        res[i] = txt[i];
    for (int i = 0; i < tl; ++i) // append argument
        res[length + i] = t[i];
    res[length + tl] = '\0';    // set text end
    setTextZ(res, length + tl); // set text
}

void Text::appendHex(char num) // appends the number in hex to the text
{
    char* res = new char[(__int64)length + sizeof(char) * 2 + 1];
    for (int i = 0; i < length; ++i)
        res[i] = txt[i];
    std::stringstream stream;
    stream << std::setfill('0') << std::setw((int)sizeof(char) * 2) << std::hex
           << (int)num;
    std::string str = stream.str();
    for (int i = length; i < length + (int)sizeof(char) * 2; ++i)
        res[i] = str.c_str()[i - length];
    res[length + sizeof(char) * 2] = 0;
    setTextZ(res, length + sizeof(char) * 2);
}

void Framework::Text::appendHex(short num)
{
    char* res = new char[(__int64)length + sizeof(short) * 2 + 1];
    for (int i = 0; i < length; ++i)
        res[i] = txt[i];
    std::stringstream stream;
    stream << std::setfill('0') << std::setw((int)sizeof(short) * 2) << std::hex
           << num;
    std::string str = stream.str();
    for (int i = length; i < length + (int)sizeof(short) * 2; ++i)
        res[i] = str.c_str()[i - length];
    res[length + sizeof(short) * 2] = 0;
    setTextZ(res, length + sizeof(short) * 2);
}

void Text::appendHex(int num) // appends the number in hex to the text
{
    char* res = new char[(__int64)length + sizeof(int) * 2 + 1];
    for (int i = 0; i < length; ++i)
        res[i] = txt[i];
    std::stringstream stream;
    stream << std::setfill('0') << std::setw((int)sizeof(int) * 2) << std::hex
           << num;
    std::string str = stream.str();
    for (int i = length; i < length + (int)sizeof(int) * 2; ++i)
        res[i] = str.c_str()[i - length];
    res[length + sizeof(int) * 2] = 0;
    setTextZ(res, length + sizeof(int) * 2);
}

void Text::appendHex(__int64 num) // appends the number in hex to the text
{
    char* res = new char[(__int64)length + sizeof(__int64) * 2 + 1];
    for (int i = 0; i < length; ++i)
        res[i] = txt[i];
    std::stringstream stream;
    stream << std::setfill('0') << std::setw((int)sizeof(__int64) * 2)
           << std::hex << num;
    std::string str = stream.str();
    for (int i = length; i < length + (int)sizeof(__int64) * 2; ++i)
        res[i] = str.c_str()[i - length];
    res[length + sizeof(__int64) * 2] = 0;
    setTextZ(res, length + sizeof(__int64) * 2);
}

void Text::append(const char* t, int l)            // appends to the text
{                                                  // length of the text
    char* res = new char[(__int64)l + length + 1]; // create new text
    for (int i = 0; i < length; ++i)               // fill with current text
        res[i] = txt[i];
    for (int i = 0; i < l; ++i) // append argument
        res[length + i] = t[i];
    res[length + l] = '\0';    // set text end
    setTextZ(res, length + l); // set text
}

void Text::append(const Text& t)
{
    if (t.getLength() > 0)        // check for invalid argument
        append(t, t.getLength()); // append text
}

void Text::append(int num)
{
    std::stringstream ss;
    ss << num;
    std::string string = ss.str();
    append(string.c_str(), (int)string.length());
}

// Appends a number to the end of the text
//  num: The number to convert to text and append at the end
void Text::append(__int64 num)
{
    std::stringstream ss;
    ss << num;
    std::string string = ss.str();
    append(string.c_str(), (int)string.length());
}

void Text::append(unsigned int num)
{
    std::stringstream ss;
    ss << num;
    std::string string = ss.str();
    append(string.c_str(), (int)string.length());
}

void Text::append(double num)
{
    std::stringstream ss;
    if (precision >= 0) ss.precision(precision);
    ss << std::fixed << num;
    std::string string = ss.str();
    const char* str = string.c_str();
    int len = (int)string.length();
    for (int i = len - 1; i > 0; i--)
    {
        if (str[i] == '0')
            len--;
        else
        {
            if (str[i] == '.') len--;
            break;
        }
    }
    append(str, len);
}

void Text::append(float num)
{
    std::stringstream ss;
    if (precision >= 0) ss.precision(precision);
    ss << std::fixed << num;
    std::string string = ss.str();
    const char* str = string.c_str();
    int len = (int)string.length();
    for (int i = len - 1; i > 0; i--)
    {
        if (str[i] == '0')
            len--;
        else
        {
            if (str[i] == '.') len--;
            break;
        }
    }
    append(str, len);
}

//! Returns an ostream that appends all output to this text
FlushingOStream Text::append()
{
    if (!stringWriter)
    {
        stringWriter = new DynamicBuffer([this](std::stringbuf& buf) {
            std::string str = buf.str();
            this->append(str.c_str(), (int)str.length());
            buf.str("");
            return 0;
        });
    }
    return FlushingOStream(stringWriter);
}

void Text::insert(int p, char c) // inserts at position p
{
    if (p > length || p < 0) // check for invalid argument
        return;
    char* res = new char[(__int64)length + 2]; // create new text
    for (int i = 0; i < p; ++i)                // fill text
        res[i] = txt[i];
    res[p] = c;
    for (int i = p; i < length; ++i) // fill text
        res[i + 1] = txt[i];
    res[length + 1] = '\0';    // set text end
    setTextZ(res, length + 1); // set text
}

void Text::insert(int p, const char* t) // inserts at position p
{
    insert(p, t, textLength(t)); // insert text
}

void Framework::Text::insert(int p, const char* t, int len)
{
    if (p > length || p < 0 || len <= 0) // check for invalid argument
        return;                          // length of argument
    char* res = new char[(__int64)len + length + 1]; // create new text
    for (int i = 0; i < p; ++i)                      // fill text
        res[i] = txt[i];
    for (int i = 0; i < len; ++i) // insert text
        res[i + p] = t[i];
    for (int i = p; i < length; ++i) // fill text
        res[i + len] = txt[i];
    res[len + length] = '\0';    // set text end
    setTextZ(res, length + len); // set text
}

void Text::insert(int p, const Text& t)
{
    insert(p, t, t.getLength()); // insert text
}

void Framework::Text::regexReplace(
    const char* regex, const char* replacement, Regex::RegexConfig* config)
{
    regexReplace(
        regex, [replacement](Regex::Result&) { return replacement; }, config);
}

void Framework::Text::regexReplace(const char* regex,
    std::function<Text(Regex::Result&)> replacementFunction,
    Regex::RegexConfig* config)
{
    auto matcher
        = config == 0 ? Regex::parse(regex) : Regex::parse(regex, *config);
    if (!matcher) return;
    auto result = matcher->match(txt, length);
    if (result->getEntryCount() > 0)
    {
        int replacedLength = 0;
        int replacementLength = 0;
        Text* replacements = new Text[result->getEntryCount()];
        int i = 0;
#ifdef WIN32
#    pragma warning(push)
#    pragma warning(disable : 6385)
#endif
        for (Regex::Result* match : *result)
        {
            replacedLength += match->getEnd() - match->getStart();
            replacements[i] = replacementFunction(*match);
            replacementLength += replacements[i].getLength();
            i++;
        }
        int newLength = length - replacedLength + replacementLength;
        char* newText = new char[(__int64)newLength + 1];
        int pos = 0;
        int newPos = 0;
        int j = 0;
        for (Regex::Result* match : *result)
        {
            for (int i = 0; i < match->getStart() - pos; i++)
            {
                newText[newPos + i] = txt[pos + i];
            }
            newPos += match->getStart() - pos;
            pos = match->getEnd();
            for (int i = 0; i < replacements[j].getLength(); i++)
            {
                newText[newPos + i] = replacements[j][i];
            }
            newPos += replacements[j].getLength();
            j++;
        }
        for (int i = pos; i < getLength(); i++)
        {
            newText[newPos + i - pos] = txt[i];
        }
#ifdef WIN32
#    pragma warning(pop)
#endif
        newText[newLength] = '\0';
        setTextZ(newText, newLength);
        delete[] replacements;
    }
    result->release();
    matcher->release();
}

void Text::replace(int p1, int p2, const char* t) // replaces text from p1 to p2
{
    replace(p1, p2, t, textLength(t)); // replace text
}

void Framework::Text::replace(int p1, int p2, const char* t, int len)
{
    if (p1 > p2)
    {
        int x = p1; // swap p1 and p2
        p1 = p2;
        p2 = x;
    }
    remove(p1, p2); // delete text section from p1 to p2
    if (len > 0)
    {
        insert(p1, t, len); // insert argument at p1
    }
}

void Text::replace(int p1, int p2, const Text& t)
{
    replace(p1, p2, t, t.getLength()); // replace text
}

void Text::replace(char c1, char c2) // replaces every c1 with c2
{
    if (c1 == '\0' || c2 == '\0') // check for invalid argument
        return;
    if (!has(c1)) // check if c1 exists
        return;
    int suchGCount = 0;
    if (suchGBeg && suchGEnd)
    {
        for (int i = 0; i < length; ++i) // search text
        {
            bool b = suchGCount != 0;
            if (txt[i] == c1 && !suchGCount) txt[i] = c2; // replace text
            if (txt[i] == suchGBeg) ++suchGCount;
            if (txt[i] == suchGEnd) --suchGCount;
            if (txt[i] == c1 && !suchGCount && b) txt[i] = c2; // replace text
        }
    }
    else
    {
        for (int i = 0; i < length; ++i) // search text
        {
            if (txt[i] == c1) txt[i] = c2; // replace text
        }
    }
}

void Text::replace(const char* t1, const char* t2) // replaces every t1 with t2
{
    replace(t1, textLength(t1), t2, textLength(t2)); // replace text
}

void Framework::Text::replace(
    const char* t1, int len1, const char* t2, int len2)
{
    if (len1 > length || len1 <= 0) // check for invalid argument
        return;
    if (!has(t1, len1)) // check if t1 exists
        return;
    int anz = countOf(t1, len1); // count of t1 in text
    int* begin = new int[anz];
    int* end = new int[anz];
    int searchStart = 0;
    for (int i = 0; i < anz; ++i) // save positions of t1
    {
        begin[i] = positionOf(searchStart, t1, len1);
        end[i] = begin[i] + len1;
        searchStart = end[i];
    }
    int resl = (length - (anz * len1)) + (anz * len2) + 1; // length of result
    char* res = new char[resl];                            // create new text
    int rep = 0;              // stores which t1 we are at
    int last = 0;             // fill position in txt
    int neu = 0;              // fill position in res
    for (; neu < resl; ++neu) // fill text
    {
        if (rep < anz && last == begin[rep]) // replace text
        {
            last = end[rep];
            ++rep;
            for (int ii = 0; ii < len2; ++ii)
            {
                if (neu >= resl) break;
                res[neu] = t2[ii];
                ++neu;
            }
            if (neu >= resl) break;
            --neu;
        }
        else // copy text
        {
            res[neu] = txt[last];
            ++last;
        }
    }
    res[resl - 1] = '\0'; // set text end
    setTextZ(res, resl);  // set text
    delete[] begin;       // free memory
    delete[] end;
}

void Text::replace(const Text& t1, const char* t2)
{
    replace(t1, t1.getLength(), t2, textLength(t2)); // replace text
}

void Text::replace(const char* t1, const Text& t2)
{
    replace(t1, textLength(t1), t2, t2.getLength()); // replace text
}

void Text::replace(const Text& t1, const Text& t2)
{
    replace(t1, t1.getLength(), t2, t2.getLength()); // replace text
}

void Text::replace(int index, char c1, char c2) // replaces the i-th c1 with c2
{
    if (c1 == '\0' || c2 == '\0' || index < 0) // check for invalid argument
        return;
    if (!has(c1)) // check if c1 exists
        return;
    int anz = countOf(c1);
    if (index >= anz) // check if an i-th c1 exists
        return;
    txt[positionOf(c1, index)] = c2;
}

void Text::replace(int index,
    const char* t1,
    const char* t2) // replaces the i-th t1 with t2
{
    replace(index, t1, textLength(t1), t2, textLength(t2)); // replace text
}

void Framework::Text::replace(
    int index, const char* t1, int len1, const char* t2, int len2)
{
    if (len1 >= length || len1 <= 0 || index < 0) // check for invalid argument
        return;
    if (!has(t1)) // check if t1 exists
        return;
    int anz = countOf(t1, len1); // count of t1 in text
    if (index >= anz)            // check if an i-th t1 exists
        return;
    int begin = positionOf(index, len1, t1);
    int end = begin + len1;
    replace(begin, end, t2, len2); // replace
}

void Text::replace(int i, const Text& t1, const char* t2)
{
    replace(i, t1, t1.getLength(), t2, textLength(t2)); // replace
}

void Text::replace(int i, const char* t1, const Text& t2)
{
    replace(i, t1, textLength(t1), t2, t2.getLength()); // replace
}

void Text::replace(int i, const Text& t1, const Text& t2)
{
    replace(i, t1, t1.getLength(), t2,
        t2.getLength()); // replace
}

void Text::fillText(
    char c, int len) // sets the text to as many c as len is large
{
    char* res = new char[(__int64)len + 1];
    for (int i = 0; i < len; ++i)
        res[i] = c;
    res[len] = '\0';
    setTextZ(res, len);
}

void Text::remove(int p) // deletes p
{
    if (p < 0 || p >= length) // check for invalid argument
        return;
    char* res = new char[length];             // create new text
    for (int i = 0; i < p && i < length; ++i) // fill text
        res[i] = txt[i];
    for (int i = p + 1; i < length; ++i)
        res[i - 1] = txt[i];
    res[length - 1] = 0;
    setTextZ(res, length - 1); // set text
}

void Text::remove(int p1, int p2) // deletes from p1 to p2 (p2 is kept)
{
    if (p1 == p2) return;
    if (p1 > p2) // swap p1 and p2
    {
        int x = p1;
        p1 = p2;
        p2 = x;
    }
    if (p1 < 0) // check for invalid argument
        p1 = 0;
    if (p2 > length) p2 = length;
    int resl = length - (p2 - p1);           // length of result
    char* res = new char[(__int64)resl + 1]; // create new text
    memcpy(res, txt, p1);
    memcpy(res + p1, txt + p2, length - p2);
    res[resl] = '\0';    // set text end
    setTextZ(res, resl); // set text
}

void Text::remove(char c) // deletes every c
{
    if (!has(c)) // check if c exists
        return;
    int anz = countOf(c);                            // count of c
    char* res = new char[(__int64)length - anz + 1]; // create new text
    int anz2 = 0;
    int suchGCount = 0;
    for (int i = 0; i < length; ++i) // fill text
    {
        bool b = suchGCount != 0;
        if (txt[i] == c && !suchGCount)
            ++anz2;
        else
            res[i - anz2] = txt[i];
        if (txt[i] == suchGBeg) ++suchGCount;
        if (txt[i] == suchGEnd) --suchGCount;
        if (b)
        {
            if (txt[i] == c && !suchGCount)
                ++anz2;
            else
                res[i - anz2] = txt[i];
        }
    }
    res[length - anz] = '\0';    // set text end
    setTextZ(res, length - anz); // set text
}

void Text::remove(const char* t) // deletes every t
{
    remove(t, textLength(t)); // delete text
}

void Framework::Text::remove(const char* t, int len)
{
    if (len <= 0 || len > length) // check for invalid argument
        return;
    if (!has(t, len)) // check if text contains t
        return;
    int anz = countOf(t, len); // count of t
    int* begin = new int[anz];
    for (int i = 0; i < anz; ++i) // find start of all t's
        begin[i] = positionOf(i, len, t);
    int resl = length - (anz * len);         // length of result
    char* res = new char[(__int64)resl + 1]; // create new text
    int del = 0;
    for (int i = 0; i < length; ++i) // fill text
    {
        if (del < anz && i == begin[del]) // skip text
        {
            i += len - 1;
            ++del;
        }
        else
            res[i - (del * len)] = txt[i]; // fill text
    }
    res[resl] = '\0';    // set text end
    setTextZ(res, resl); // set text
    delete[] begin;
}

void Text::remove(const Text& t)
{
    remove(t, t.getLength()); // delete text
}

void Text::remove(int index, char c)
{
    if (index < 0 || !has(c)) // check for invalid argument
        return;
    int anz = countOf(c); // count of c's
    if (index >= anz)     // check if an i-th c exists
        return;
    int pos = positionOf(c, index); // position of the i-th c
    if (pos < 0) return;
    if (!length) return;
    char* res = new char[length];               // create new text
    for (int i = 0; i < pos && i < length; ++i) // fill text
        res[i] = txt[i];
    for (int i = pos + 1; i < length; ++i)
        res[i - 1] = txt[i];
    res[length - 1] = '\0';    // set text end
    setTextZ(res, length - 1); // set text
}

void Text::remove(int index, const char* t) // deletes the i-th t
{
    remove(index, t, textLength(t)); // delete text
}

void Framework::Text::remove(int index, const char* t, int len)
{
    if (index < 0 || !has(t, len) || len <= 0) // check for invalid argument
        return;
    int anz = countOf(t, len); // count of t's
    if (index >= anz)          // check if an i-th t exists
        return;
    int pos = positionOf(index, len, t); // position of the i-th t
    if (pos < 0) return;
    if (!length) return;
    char* res = new char[(__int64)length - len + 1];      // create new text
    for (int i = 0; i < pos && i < length - len + 1; ++i) // fill text
        res[i] = txt[i];
    for (int i = pos + len; i < length; ++i)
        res[i - len] = txt[i];
    res[length - len] = '\0';    // set text end
    setTextZ(res, length - len); // set text
}

void Text::remove(int i, const Text& t)
{
    remove(i, t, t.getLength()); // delete text
}

// Deletes all ' ', '\n', '\r', '\t' until a different character
//  pos: The position of the first character
int Text::removeWhitespaceAfter(int pos)
{
    int removedLength = 0;
    for (int i = pos; i < length; i++)
    {
        if (txt[i] == ' ' || txt[i] == '\n' || txt[i] == '\r' || txt[i] == '\t')
            removedLength++;
        else
            break;
    }
    remove(pos, pos + removedLength);
    return length;
}

// Deletes all ' ', '\n', '\r', '\t' until a different character
//  pos: The position of the first character (starts at pos-1)
int Text::removeWhitespaceBefore(int pos)
{
    int length = 0;
    for (int i = pos - 1; i >= 0; i--)
    {
        if (txt[i] == ' ' || txt[i] == '\n' || txt[i] == '\r' || txt[i] == '\t')
            length++;
        else
            break;
    }
    remove(pos - length, pos);
    return length;
}

void Text::setPrecision(int p) // sets the number of decimal places for doubles
{
    precision = p;
}

// constant
int Text::getLength() const // returns the text length
{
    return length;
}

int Text::getLKick(int pos) const
{
    if (txt[pos - 1] == ' ')
    {
        int ret = 1;
        for (; ret < pos && txt[pos - ret - 1] == ' '
               && txt[pos - ret - 1] != '\n';
            ++ret)
            ;
        return pos - ret;
    }
    else
    {
        int ret = 1;
        for (; ret < pos && txt[pos - ret - 1] != ' '
               && txt[pos - ret - 1] != '\n';
            ++ret)
            ;
        return pos - ret;
    }
}

int Text::getOKick(int pos) const
{
    if (!has('\n')) return 0;
    int lpos = 0;
    while (pos - lpos - 1 > 0 && txt[pos - lpos - 1] != '\n')
        ++lpos;
    int vllen = 1;
    while (pos - lpos - vllen - 1 >= 0 && txt[pos - lpos - vllen - 1] != '\n')
        ++vllen;
    if (vllen > lpos)
        return pos - vllen;
    else
        return pos - lpos - 1;
}

int Text::getRKick(int pos) const
{
    if (txt[pos] == ' ')
    {
        int ret = 1;
        for (; ret + pos < length && txt[pos + ret] == ' '
               && txt[pos + ret] != '\n';
            ++ret)
            ;
        return pos + ret;
    }
    else
    {
        int ret = 1;
        for (; ret + pos < length && txt[pos + ret] != ' '
               && txt[pos + ret] != '\n';
            ++ret)
            ;
        return pos + ret;
    }
}

int Text::getUKick(int pos) const
{
    if (!has('\n')) return length;
    int lpos = 0;
    while (pos - lpos > 0 && txt[pos - lpos - 1] != '\n')
        ++lpos;
    int llen = 1;
    while (pos + llen - 1 < length && txt[pos + llen - 1] != '\n')
        ++llen;
    int vllen = 1;
    while (
        pos + llen + vllen - 1 < length && txt[pos + llen + vllen - 1] != '\n')
        ++vllen;
    if (vllen == 1) return pos + llen < length ? pos + llen : length;
    if (vllen < lpos) return pos + llen + vllen - 1;
    return pos + llen + lpos;
}

bool Text::has(const Text& t) const // does the text contain t
{
    return has(t, t.getLength()); // check text
}

bool Text::has(const char* t) const
{
    return has(t, textLength(t)); // check text
}

bool Framework::Text::has(const char* t, int len) const
{
    return has(0, t, len);
}

bool Framework::Text::has(int searchStartIndex, const char* t) const
{
    return has(searchStartIndex, t, textLength(t)); // check text
}

bool Framework::Text::has(int searchStartIndex, const char* t, int len) const
{
    if (len <= 0
        || len > length - searchStartIndex) // check for invalid argument
        return 0;
    int suchGCount = 0;
    for (int i = searchStartIndex; i + len <= length; ++i) // search text
    {
        bool searched = 0;
        if (!suchGCount)
        {
            bool b = 1;
            for (int ii = 0; ii < len; ++ii) // check text
                b &= txt[i + ii] == t[ii];
            if (b) return 1;
            searched = 1;
        }
        if (txt[i] == suchGBeg) ++suchGCount;
        if (txt[i] == suchGEnd) --suchGCount;
        if (!suchGCount && !searched)
        {
            bool b = 1;
            for (int ii = 0; ii < len; ++ii) // check text
                b &= txt[i + ii] == t[ii];
            if (b) return 1;
        }
    }
    return 0;
}

// Checks whether the text contains the content of another text
//  t: The text whose content to search for
//  return: (true) if the content is found. (false) otherwise
//  pos: the position where the string should begin
bool Text::hasAt(int pos, const Text& t) const
{
    return hasAt(pos, t, t.getLength()); // check text
}

// Checks whether the text contains a specific string
//  t: The string to search for
//  return: (true) if the string is found. (false) otherwise
//  pos: the position where the string should begin
bool Text::hasAt(int pos, const char* t) const
{
    return hasAt(pos, t, textLength(t)); // check text
}

bool Framework::Text::hasAt(int pos, const char* t, int len) const
{
    if (len <= 0 || len + pos > length) // check for invalid argument
        return 0;
    bool b = 1;
    for (int i = 0; i < len; ++i) // check text
        b &= txt[pos + i] == t[i];
    return b;
}

bool Text::has(char c) const // contains c
{
    bool ret = 0;
    int suchGCount = 0;
    for (int i = 0; i < length; ++i) // search
    {
        bool b = suchGCount != 0;
        if (!suchGCount) // check
            ret |= txt[i] == c;
        if (txt[i] == suchGBeg) ++suchGCount;
        if (txt[i] == suchGEnd) --suchGCount;
        if (!suchGCount && b) // check
            ret |= txt[i] == c;
    }
    return ret;
}

bool Text::beginnsWith(const char* t) const
{
    return hasAt(0, t);
}

bool Text::endsWith(const char* t) const
{
    int len = textLength(t);
    return hasAt(length - len, t, len);
}

bool Text::isEqual(const char* t) const // checks if the text equals t
{
    return isEqual(t, textLength(t)); // check text
}

bool Text::isEqual(const char* t, int len) const // checks if the text equals t
{
    if (length != len) // check for invalid argument
        return 0;
    if (length == -1) return 1;
    bool ret = true;
    for (int i = 0; i < len; ++i) // check
        ret &= txt[i] == t[i];
    return ret;
}

bool Text::isEqual(const Text& t) const
{
    return isEqual(t, t.getLength()); // check text
}

const char* Text::getText() const // returns the text
{
    return txt;
}

int Text::countOf(char c) const // returns the count of c in the text
{
    int ret = 0;
    int suchGCount = 0;
    if (suchGBeg && suchGEnd)
    {
        for (int i = 0; i < length; ++i) // search
        {
            bool b = suchGCount != 0;
            ret += txt[i] == c && !suchGCount; // count
            if (txt[i] == suchGBeg) ++suchGCount;
            if (txt[i] == suchGEnd) --suchGCount;
            ret += txt[i] == c && !suchGCount && b; // count
        }
    }
    else
    {
        for (int i = 0; i < length; ++i) // search
        {
            ret += txt[i] == c; // count
        }
    }
    return ret;
}

int Text::countOf(const char* t) const // returns the count of t in the text
{
    return countOf(t, textLength(t)); // check text
}

int Framework::Text::countOf(const char* t, int len) const
{
    if (len <= 0 || len > length) // check for invalid argument
        return 0;
    int ret = 0;
    int suchGCount = 0;
    for (int i = 0; i + len <= length; ++i) // search
    {
        bool b = suchGCount != 0;
        if (!suchGCount)
        {
            bool b = 1;
            for (int ii = 0; ii < len; ++ii) // check
                b &= txt[i + ii] == t[ii];
            if (b) ++ret; // count
        }
        if (txt[i] == suchGBeg) ++suchGCount;
        if (txt[i] == suchGEnd) --suchGCount;
        if (!suchGCount && b)
        {
            bool b = 1;
            for (int ii = 0; ii < len; ++ii) // check
                b &= txt[i + ii] == t[ii];
            if (b) ++ret; // count
        }
    }
    return ret;
}

int Text::countOf(const Text& t) const
{
    return countOf(t, t.getLength()); // check text
}

int Text::positionOf(char c) const // returns the position of the first c
{
    int suchGCount = 0;
    for (int i = 0; i < length; ++i) // search
    {
        bool b = suchGCount != 0;
        if (txt[i] == c && !suchGCount) // check
            return i;
        if (txt[i] == suchGBeg) ++suchGCount;
        if (txt[i] == suchGEnd) --suchGCount;
        if (txt[i] == c && !suchGCount && b) // check
            return i;
    }
    return -1;
}

int Text::positionOf(const char* t) const // returns the position of the first t
{
    return positionOf(textLength(t), t);
}

int Framework::Text::positionOf(int len, const char* t) const
{
    return positionOf(0, t, len);
}

int Text::positionOf(int searchStart, const char* t, int len) const
{
    if (len <= 0 || len > length - searchStart) // check for invalid argument
        return -1;
    int suchGCount = 0;
    for (int i = searchStart; i + len <= length; ++i) // search
    {
        bool b = suchGCount != 0;
        if (!suchGCount)
        {
            bool b = 1;
            for (int ii = 0; ii < len; ++ii) // check
                b &= txt[i + ii] == t[ii];
            if (b) return i;
        }
        if (txt[i] == suchGBeg) ++suchGCount;
        if (txt[i] == suchGEnd) --suchGCount;
        if (!suchGCount && b)
        {
            bool b = 1;
            for (int ii = 0; ii < len; ++ii) // check
                b &= txt[i + ii] == t[ii];
            if (b) return i;
        }
    }
    return -1;
}

int Text::positionOf(const Text& t) const
{
    return positionOf(t.getLength(), t); // check text
}

int Text::positionOf(
    char c, int index) const // returns the position of the i-th c
{
    int ii = 0;
    int suchGCount = 0;
    for (int i = 0; i < length; ++i) // search
    {
        bool b = suchGCount != 0;
        if (txt[i] == c && !suchGCount) // check
        {
            if (ii == index)
                return i;
            else
                ++ii;
        }
        if (txt[i] == suchGBeg) ++suchGCount;
        if (txt[i] == suchGEnd) --suchGCount;
        if (txt[i] == c && !suchGCount && b) // check
        {
            if (ii == index)
                return i;
            else
                ++ii;
        }
    }
    return -1;
}

int Text::positionOf(
    const char* t, int index) const // returns the position of the i-th t
{
    return positionOf(index, textLength(t), t); // check text
}

int Framework::Text::positionOf(int index, int len, const char* t) const
{
    if (len <= 0 || len > length) // check for invalid argument
        return 0;
    int i2 = 0;
    int suchGCount = 0;
    for (int i = 0; i + len <= length; ++i) // search
    {
        bool b = suchGCount != 0;
        if (!suchGCount)
        {
            bool b = 1;
            for (int ii = 0; ii < len; ++ii) // check
                b &= txt[i + ii] == t[ii];
            if (b)
            {
                if (i2 == index)
                    return i;
                else
                    ++i2;
            }
        }
        if (txt[i] == suchGBeg) ++suchGCount;
        if (txt[i] == suchGEnd) --suchGCount;
        if (!suchGCount && b)
        {
            bool b = 1;
            for (int ii = 0; ii < len; ++ii) // check
                b &= txt[i + ii] == t[ii];
            if (b)
            {
                if (i2 == index)
                    return i;
                else
                    ++i2;
            }
        }
    }
    return -1;
}

int Text::positionOf(const Text& t, int i) const
{
    return positionOf(i, t.getLength(), t); // check text
}

Text* Text::getTeilText(int p1, int p2) const // returns the text from p1 to p2
{
    if (p1 > p2) // swap p1 and p2
    {
        int x = p1;
        p1 = p2;
        p2 = x;
    }
    if (p1 < 0 || p2 > length) // check for invalid argument
        return new Text("");
    char* cp = new char[(__int64)p2 - p1 + 1]; // create new text
    for (int i = p1; i < p2; ++i)              // fill text
    {
        cp[i - p1] = txt[i];
    }
    cp[p2 - p1] = '\0';
    return new Text(cp, p2 - p1); // return text
}

Text* Text::getTeilText(int p) const // returns the text from p to the end
{
    return getTeilText(p, getLength()); // return text
}

// calculates the hash code of the text
int Text::hashCode() const
{
    int result = 0;
    for (int i = 0; i < length; i++)
        result += (int)pow(txt[i] * 31, length - 1 - i);
    return result;
}

// Operators
Text& Text::operator+=(const int num)
{
    append(num);
    return *this;
}

// Appends a number to the end of the text
Text& Text::operator+=(const __int64 num)
{
    append(num);
    return *this;
}

Text& Text::operator+=(const double num)
{
    append(num);
    return *this;
}

Text& Text::operator+=(const float num)
{
    append(num);
    return *this;
}

Text& Text::operator+=(const char* txt)
{
    append(txt);
    return *this;
}

Text& Text::operator+=(const Text& txt)
{
    append(txt, txt.getLength());
    return *this;
}

Text& Text::operator=(const int num)
{
    setText("", 0);
    append(num);
    return *this;
}

Text& Text::operator=(const double num)
{
    setText("", 0);
    append(num);
    return *this;
}

Text& Text::operator=(const float num)
{
    setText("", 0);
    append(num);
    return *this;
}

Text& Text::operator=(const char* txt)
{
    setText(txt);
    return *this;
}

Text& Text::operator=(const Text& txt)
{
    setText(txt, txt.getLength());
    return *this;
}

Text::operator const char*() const
{
    return txt;
}

Text::operator int() const
{
    if (length > 2 && txt[0] == '0' && txt[1] == 'x')
        return TextZuInt((txt + 2), 16);
    return TextZuInt(txt, 10);
}

Text::operator __int64() const
{
    if (length > 2 && txt[0] == '0' && txt[1] == 'x')
        return TextZuInt64((txt + 2), 16);
    return TextZuInt64(txt, 10);
}

Text::operator double() const
{
    return TextZuDouble(txt);
}

Text::operator float() const
{
    return TextZuFloat(txt);
}

bool Text::operator>(Text& t) const
{
    int len2 = t.getLength();
    const char* txt2 = t;
    for (int i = 0; i < length && i < len2; ++i)
    {
        if (txt[i] > txt2[i]) return 1;
        if (txt[i] < txt2[i]) return 0;
    }
    if (length > len2) return 1;
    return 0;
}

bool Text::operator<(Text& t) const
{
    int len2 = t.getLength();
    const char* txt2 = t;
    for (int i = 0; i < length && i < len2; ++i)
    {
        if (txt[i] < txt2[i]) return 1;
        if (txt[i] > txt2[i]) return 0;
    }
    if (length < len2) return 1;
    return 0;
}

// Creates a new text consisting of this and t2
Text Text::operator+(const Text& t2) const
{
    return Text(*this) += t2;
}

// Creates a new text consisting of this and t2
Text Text::operator+(const char* t2) const
{
    return Text(*this) += t2;
}

// Creates a new text consisting of this and num
Text Text::operator+(const int num) const
{
    return Text(*this) += num;
}

// Creates a new text consisting of this and num
Text Text::operator+(const __int64 num) const
{
    return Text(*this) += num;
}

// Creates a new text consisting of this and num
Text Text::operator+(const double num) const
{
    return Text(*this) += num;
}

// Creates a new text consisting of this and num
Text Text::operator+(const float num) const
{
    return Text(*this) += num;
}

bool Text::operator==(const Text& right) const
{
    return isEqual(right, right.getLength());
}

Text Text::fromArray(char* arr, int len)
{
    return Text(arr, len);
}

const Text Text::EMPTY = "";

// Content of the TextReader class
// Constructor
//  txt: The text to be read. It is not copied but read directly.
TextReader::TextReader(Text* txt)
    : ReferenceCounter()
{
    this->txt = txt;
    lPos = 0;
}

// Destructor
TextReader::~TextReader()
{
    txt->release();
}

// Sets the position of the byte to be read next
//  pos: The index of the byte
//  ende: 1 if the index counts from the end of the text. 0 if from the
//  beginning
void TextReader::setReadPosition(__int64 pos, bool ende)
{
    int l = txt->getLength();
    lPos = ende ? l - pos : pos;
    if (lPos < 0) lPos = 0;
    if (lPos > l) lPos = l;
}

// Reads from the text
//  bytes: An array to be filled with bytes from the text
//  len: How many bytes to read from the text
void TextReader::read(char* bytes, int len)
{
    int l = txt->getLength();
    len = (int)MIN(l - lPos, len);
    for (__int64 i = lPos; i < lPos + len; i++)
        bytes[i - lPos] = txt->getText()[i];
    lPos += len;
}

// Reads the next line of the text
//  return: The read line as text with line break
Text* TextReader::readLine()
{
    if (isEnd()) return 0;
    Text* ret = new Text("");
    __int64 len = txt->getLength();
    for (char c = 0; c != '\n' && lPos < len;)
    {
        read(&c, 1);
        if (c) ret->append(&c, 1);
    }
    return ret;
}

// Checks whether the resource has been fully read
//  return 1 if the resource has been fully read. 0 otherwise
bool TextReader::isEnd() const
{
    return lPos >= txt->getLength();
}

// Returns the index of the byte from the text that would be read next
// return -1 if an error occurred. Otherwise the position of the read pointer
__int64 TextReader::getReadPosition() const
{
    return lPos;
}

//! Returns the number of bytes to read
__int64 TextReader::getSize() const
{
    return txt->getLength();
}

// char* operationen
int Framework::stringPositionOfChar(const char* string,
    char c,
    int num) // finds the position of the num-th c
             // in string, -1 if not found
{
    int gef = 0;
    int p = 0;
    for (char cc = *string; *string; ++string)
    {
        if (cc == c)
        {
            if (gef == num)
                return p;
            else
                ++gef;
        }
        ++p;
    }
    return -1;
}

int Framework::stringPositionOfString(const char* string,
    char* suche,
    int sBegPos) // finds the position of 'suche' in 'string' starting at
                 // position 'sBegPos', -1 if not found
{
    for (int i = 0; i < sBegPos; ++i)
    {
        if (!*string) return -1;
        ++string;
    }
    int tl = textLength(suche);   // length of the argument
    int txl = textLength(string); // length of the text
    if (tl <= 0 || tl > txl)      // check for invalid argument
        return -1;
    for (int i = 0; i + tl <= txl; ++i) // search
    {
        bool b = 1;
        for (int ii = 0; ii < tl; ++ii) // check
            if (b) b = string[i + ii] == suche[ii];
        if (b) return i + sBegPos;
    }
    return -1;
}

//---Other Functions---
void Framework::TextKopieren(
    const char* txt) // copies the text to the clipboard
{
#ifdef WIN32
    int laen = textLength(txt) + 1;
    if (laen == 1) return;
    HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, laen);
    if (!hMem) return;
    memcpy(GlobalLock(hMem), txt, laen);
    GlobalUnlock(hMem);
    OpenClipboard(0);
    EmptyClipboard();
    SetClipboardData(CF_TEXT, hMem);
    CloseClipboard();
#endif
}

const char* Framework::TextInsert() // returns the text from the clipboard
{
#ifdef WIN32
    if (!OpenClipboard(0)) return "";
    HANDLE hClipData = GetClipboardData(CF_TEXT);
    char* cBuffer = (char*)GlobalLock(hClipData);
    GlobalUnlock(hClipData);
    CloseClipboard();
    return cBuffer;
#else
    return 0;
#endif
}

char Framework::smallOrBig(char c, bool gr)
{
    int ret = c;
    if (gr)
    {
        if (c >= 'a' && c <= 'z')
            ret -= 32;
        else
        {
            switch (c)
            {
            case SpecialCharacters::CARET:
                return SpecialCharacters::DEGREE_SIGN;
            case '1':
                return '!';
            case '<':
                return '>';
            case '2':
                return '\"';
            case '3':
                return SpecialCharacters::SECTION_SIGN;
            case '4':
                return '$';
            case '5':
                return '%';
            case '6':
                return '&';
            case '7':
                return '/';
            case '8':
                return '(';
            case '9':
                return ')';
            case '0':
                return '=';
            case ',':
                return ';';
            case '.':
                return ':';
            case SpecialCharacters::SZ:
                return '?';
            case '-':
                return '_';
            case SpecialCharacters::ACUTE_ACCENT:
                return '`';
            case '+':
                return '*';
            case '#':
                return '\'';
            case SpecialCharacters::SMALL_UE:
                return SpecialCharacters::BIG_UE;
            case SpecialCharacters::SMALL_OE:
                return SpecialCharacters::BIG_OE;
            case SpecialCharacters::SMALL_AE:
                return SpecialCharacters::BIG_AE;
            }
        }
    }
    else
    {
        if (c >= 'A' && c <= 'Z')
            ret += 32;
        else
        {
            switch (c)
            {
            case SpecialCharacters::DEGREE_SIGN:
                return SpecialCharacters::CARET;
            case '!':
                return '1';
            case '>':
                return '<';
            case '\"':
                return '2';
            case SpecialCharacters::SECTION_SIGN:
                return '3';
            case '$':
                return '4';
            case '%':
                return '5';
            case '&':
                return '6';
            case '/':
                return '7';
            case '(':
                return '8';
            case ')':
                return '9';
            case '=':
                return '0';
            case ';':
                return ',';
            case ':':
                return '.';
            case '?':
                return SpecialCharacters::SZ;
            case '_':
                return '-';
            case '`':
                return SpecialCharacters::ACUTE_ACCENT;
            case '*':
                return '+';
            case '\'':
                return '#';
            case SpecialCharacters::BIG_UE:
                return SpecialCharacters::SMALL_UE;
            case SpecialCharacters::BIG_OE:
                return SpecialCharacters::SMALL_OE;
            case SpecialCharacters::BIG_AE:
                return SpecialCharacters::SMALL_AE;
            }
        }
    }
    return (char)ret;
}

bool Framework::isWritable(
    unsigned char zeichen) // checks if zeichen is a printable character
{
    if (zeichen > 32 && zeichen < 127) return 1;
    if (zeichen == 128 || zeichen == 181 || zeichen == 178 || zeichen == 179)
        return 1;
    if (zeichen > 191 && zeichen < 198) return 1;
    if (zeichen > 199 && zeichen < 208) return 1;
    if (zeichen > 209 && zeichen < 215) return 1;
    if (zeichen > 216 && zeichen < 221) return 1;
    if (zeichen > 222 && zeichen < 230) return 1;
    if (zeichen > 231 && zeichen < 240) return 1;
    if (zeichen > 241 && zeichen < 247) return 1;
    if (zeichen > 248 && zeichen < 253) return 1;
    if (zeichen == ' ' || zeichen == '\t') return 1;
    return 0;
}

unsigned int Framework::TextZuInt(
    const char* c, int system) // converts c to int
{
    if (system == 16) return (unsigned int)strtoul(c, 0, system);
    return (unsigned int)strtol(c, 0, system);
}

unsigned int Framework::TextZuInt(const char* c, char** c_ende, int system)
{
    if (system == 16) return (unsigned int)strtoul(c, c_ende, system);
    return (unsigned int)strtol(c, c_ende, system);
}

unsigned __int64 Framework::TextZuInt64(const char* c, int system)
{
    if (system == 16) return strtoull(c, 0, system);
    return (unsigned __int64)strtoll(c, 0, system);
}

unsigned __int64 Framework::TextZuInt64(
    const char* c, char** c_ende, int system)
{
    if (system == 16) return strtoull(c, c_ende, system);
    return (unsigned __int64)strtoll(c, c_ende, system);
}

double Framework::TextZuDouble(const char* c) // converts c to double
{
    return strtod(c, 0);
}

float Framework::TextZuFloat(const char* c) // converts c to float
{
    return strtof(c, 0);
}

double Framework::TextZuDouble(const char* c, char** c_ende)
{
    return strtod(c, c_ende);
}

float Framework::TextZuFloat(const char* c, char** c_ende)
{
    return strtof(c, c_ende);
}

int Framework::textLength(const char* txt) // returns the length of txt
{
    if (!txt) return 0;
    int ret = 0;
    for (; txt[ret]; ++ret)
        ;
    return ret;
}