/**
 * @package flib
 */
//---------------------------------------------------------------------------
#ifndef _fstring_def_H_
#define _fstring_def_H_

#include <limits.h>
#include <stdio.h>
#include <stdarg.h>

#include "ftypes.h"
//#include "ccfstring.h"
//#include "datetime.h"
#include "fclassproperty.h"

// templates
#include "flist_def.h"

//#include "fstring_exc.h"

//---------------------------------------------------------------------------
typedef char (*pfnTranslateChar)(char ch);
//---------------------------------------------------------------------------
inline char ToSame(char ch) {return ch;}
char To7Bit(char ch);
/// nalezne pro ch jeho 7-mi bitovou representaci
char To7BitLower(char ch);
//?char ToUpperCZ(char ch);
//?char ToLowerCZ(char ch);
//---------------------------------------------------------------------------
int StrMatch(const char *str, const char *pattern, pfnTranslateChar tr = NULL, int len = INT_MAX);
/// pri hledani shody ignoruje hacky a carky <br>
/// * zastupuje libovolne mnozstvi znaku <br>
/// ? zastupuje  prave 1 znak <br>
/// pattern muze obsahovat libovolne mnozstvi * na libovolnych posicich <br>
/// skupina * je jako jedna *
/// vraci pocet znaku str, kterse shoduji s pattern nebo -1
int StrICaseMatch(const char *str, const char *pattern, int len = INT_MAX);
int StrCaseMatch(const char *str, const char *pattern, int len = INT_MAX);
//?int StrICaseMatchCZ(const char *str, const char *pattern, int len = INT_MAX);
//?int StrCaseMatchCZ(const char *str, const char *pattern, int len = INT_MAX);
/// funkce ...CZ ignoruji diakritiku
//---------------------------------------------------------------------------
//?byte CzSortValue(byte ch);
/// priradi ceskym znakum hodnoty tak, aby je slo srovnat podle abecedy
/// nerozlisuje mala/velka a carky krouzky
/// hacky rozlisuje

//?int StrICaseCZCmp(const char *str1, const char *str2, int len1 = INT_MAX, int len2 = INT_MAX);
/// porovnava retezce podle cesky abecedy
int StrICaseCmp(const char *str1, const char *str2, int len1 = INT_MAX, int len2 = INT_MAX);
int StrCaseCmp(const char *str1, const char *str2, int len1 = INT_MAX, int len2 = INT_MAX);
//---------------------------------------------------------------------------

/*
char UpperCharCZ(char ch);
char LowerCharCZ(char ch);

char *StrUpper(char *str);
//---------------------------------------------------------------------------
// snazi se nejak prevest VKey na word
// podrobnosti dopisu, jakmile znovu prijdu na to jak
word VKey2Char(word Key, bool shift = false, bool ctrl = false, bool alt = false);
*/
//=================================================================
//============================== FString ==========================
//=================================================================
class FString
///@para Obecny popis
/// FString pouziva pocitane reference na buffer obsahujici string.
/// String je urcen pocatkem Start a delkou Length
/// Funkce Str() upravi string tak, aby to byl c-string ukonceny 0 a vraci ukazatel
/// na pocatek tohoto stringu.
/// Buffer je FVector<char>. Pri pouziti konstruktoru @<FString(const char *str)> a
/// funkce @<Set(const char *str)>, kdy alokuje buferr dlouhy presne na delku stringu.
/// Jinak ma buffer delku v mocninach 2. Dalsi moznost, jak buffer zkratit presne
/// na deku stringu je pouzit @<Shrink()>. <br>
/// Funkce menici string jako jsou typu const FString&,
/// tedy meni obsazeny string. Varianta, ze by byl puvodni string zachovan a
/// funkce vracely typ FString nebyla pouzita z duvodu efektivity.
/// Pokud je to potreba, pouzijte s2 = FString(s1).Slice(1,1). Je to efektivni,
/// protoze se kopiruji pouze reference.
///@/para
{
private:
        char *ZeroTermStr;
protected:
        FVector<byte> *data;
///property:
fpropertyr (int, Offset);
        int fLength; // neni property, protoze se cte jako Len()
protected:
        static int strlenf(const char *str);        
        static const char *escapecharf(byte c);
        static int unescapecharf(const char *str, byte *ret);
public:
        enum enumCmpMethod {cmpCase = 0, cmpICase, cmpICaseCZ};
public:
        static const char* dbNullStr() {return "{null}";}
public:
        FString() {Init();}
        FString(const FString& fs);
        FString(const char *str);
        FString(char c);
        FString(int i);
        FString(unsigned int u);
        FString(double d, int dec = 2);
        ///@para Konstrukce
        /// FString() naalokuje buffer dlouhy DYNA_BUFF_1ST_BLOCK_SIZE (32) <br>
        /// FString(const char *str) naalokuje buffer dlouhy strlen(str) + 1 <br>
        /// FString s3 = "123" naalokuje buffer dlouhy 4, protoze je to ekvivalentni
        /// s FString("123") <br>
        /// FString(const FString &fstr) pouze zvysi RefCnt() o jednu, nealokuje nic,
        /// coz je super
        ///@/para
        ~FString();
protected:
        void Init();
public:
        int RefCnt() const {return data->RefCnt();}
        /// vraci pocet referenci na data stringu

        byte* Buff() const {return data->Vector();}
        char* CharBuff() const
        {
            return (char*)data->Vector();
        }
        char* StrBuff() const
        {
            return CharBuff() + Offset();
        }
public:
        const char* Str() const;
        /// pokud StrBuff()[Len()] == '\0' vraci StrBuff()
        /// jinak vytvori nulou ukoncenou kopii stringu v ZeroTermStr a vrati pointer na ni
        const char* CStr();
        /// upravi maximalne citlive string tak, aby to byl c-string ukonceny 0 a vraci ukazatel
        /// na pocatek tohoto stringu.

        bool isEmpty() const {return Len() <= 0;}
protected:
        int BuffLen() const {return data->Cnt();}
        int Capacity() const {return data->Capacity();}
        void setCapacity(unsigned newcapacity);
public:
        int Len() const {return fLength;}
        /// changes string length to new_len
        /// if new_len < Len() original content is trimmed.
        /// if new_len > Len() original content is padded by random chars.
        void setLen(int new_len);

        void Touch();
        /// pokud je na string vice referenci, vytvori separatni kopii dat

        void Append(char c);
        /// pripoji znak na konec stringu

        void Set(const char *str, int maxlen = INT_MAX);
        /// zkopiruje maximalne maxlen znaku ze str do bufferu
		/// a nastavi jeho delku presne na delku stringu

        void Copy(const char *str, int maxlen = INT_MAX);
        /// zkopiruje maximalne maxlen znaku ze str do bufferu
		/// a nastavi jeho delku  na mocninu 2

        void Cat(const char *str, int maxsrclen = INT_MAX);
        /// concantenate, nastavi delku na mocninu 2

        void MemCpy(const void *buff, unsigned len);
        /// zkopiruje do stringu dest
		/// a nastavi jeho delku  na mocninu 2

        void MemCat(const void *buff, unsigned len);
        /// concantenate buff, nastavi delku na mocninu 2

        void CheckStrLen();
        /// zkonroluje, jestli Len() odpovida strlen(StrBuff()) a pripadne to napravi

        void Shrink() {if(data) data->Shrink();}
        /// prealokuje buffer tak, aby byl dlouhy presne na obsazeny string

        char& getRef(int index);
        /// vraci referenci na znak na posici index
        /// zapsanim do teto reference lze primo menit jednotlive znaky stringu
        /// getRef() vola Touch() (copy on write)
        //char& operator()(int index) {return getRef(index);}
        //const char& operator()(int index) const {return operator()(index);}
        /// vraci referenci na znak na posici index, pokud index < 0 pocita se od konce
        /// pokud takova posice nelezi uvnitr stringu vrha exception
        /// vola Touch()

        char operator[](int index) const;
        /// vraci znak na posici index, pokud index < 0 pocita se od konce
        /// pokud takova posice nelezi uvnitr stringu vraci '\0'

        int Pos(char c, int n = 1, bool backward = false) const;
        /// returns index of n-th occurence of c or -1 if not found
        /// if backward searches from the end of string

        int QuotedPos(const char *str, char quote = '"', bool icase = false) const;
        /// ignores chars in quoted sequences

        int Pos(const char *str, int n = 1, int start = 0) const;
        /// returns index of n-th occurence of str or -1 if not found

        int IPos(const char *str, int n = 1, int start = 0) const;
        /// returns index of n-th occurence of str or -1 if not found
        /// ignore case
        /*?
        int Match(const char *pattern, int len = 0) const
        /// behaviour is the same like strmatchf()
        {
            return strmatchf(StrBuff(), pattern, Len());
        }
        ?*/
        /*?
        int WildMatch(const char *pattern) const
        /// pattern muze obsahovat pismena a zastupne znaky * a ?
        /// v libovolnem poradi a mnozstvi viz. StrCZMatch()
        {
            return StrICaseMatchCZ(StrBuff(), pattern, Len());
        }
        ?*/

        /**
         * vrati posici uzavirajici zavorky
         */
        int MatchingBracketPos(char open_bracket, char close_bracket, char quote = '\0') const;

        /**
         * @param start
         * @param end start i end mohou byt zaporna cisla,
         *        pokud start resp. end presahuji zacatek resp. konec stringu,
         *        bere se novy string od zacatku resp. po konec jako v pythonu s[start:end]
         * @return string, ktery zacina na posici start a konci pred posici end
         */
        FString Slice(int start, int end = INT_MAX) const;
        
        /**
         * get token from string
         * @param pattern consists of characters and '~' matching one or more of spaces<br>
         * every char in pattern, including '~', can be preceeded by '!',
         * in such a case comparison function for this char is inverted.
         * if you can match '~' or '!' char, you shall escape it
         * ie. '~hello' matches ' hello', '  hello', '   hello' etc.
         * ie. '!~~\!' matches nonblanks-blanks-! 'hello !', 'sorry   !' 
         *
         * @return the number of used chars from str if match
         * or negative value (number of partially matched chars from str - 1) 
         * if template does not match
         */
        int getToken(const FString& pattern, bool icase = false);

        /**
         * string je zkracen na Slice(len)
         * @return this->Slice(0, len)
         * @see Slice()
         */
        FString CutSlice(int len);

        FString CutMatch(const char *pattern);
        /// ukroji od zacatku stringu cast, ktera sedi na pattern a vrati ji
        /// puvodni string obsahuje zbytek po odriznuti
        /// pro vyhledani pattern pouziva funkci Match()

        FString CutPos(const char *findstr, bool ignore_case = false);
        /// ukroji od zacatku stringu cast, ktera predchazi findstr a vrati ji
        /// pro vyhledani findstr pouziva funkci Pos() nebo IPos()
        /// pokud findstr neni nalezen vrati funkce cely string
        /// puvodni obsah FStringu je oriznut za vyskytem findstr

        /// Cuts from FString part before delim
        /// Original FString is trimmed to be a rest of cut part<br>
        /// If delim is not found whole string is cut
        /// @param quote if quote != 0, function ignores all delims closed in quotes
        /// @return part of FString before delim including delim and possibly quotes
        FString Cut(char delim, char quote = 0);

        // Cuts from FString part before delim
        //FString CutAndTrim(char delim, char quote = 0, bool ignore_case = false);

        FString CutDelimited(char *delimiters, char quotechar='"');
        /// deprecated
        /// najde nektery znak z delimiters a zachova se jako MatchSlice
        /// pokud se delimiter nachazi mezi quotechar je ignorovan
        /// pokud delimiter neni nalezen vrati funkce cely string
        /// puvodni obsah FStringu je oriznut pred vyskytem delimiteru

//        FString CutQuoted(char quotechar='"');
        /// ukroji od zacatku stringu uvozeny celek a vrati ho
        /// pokud uvozovky nejsou, vrati 1 slovo
        /// puvodni string obsahuje zbytek po odriznuti

        /** 
         * cut wihitespaces & signed decimal number from beginnig of string and return it
         */
        FString CutInt();
        /** 
         * cut wihitespaces & unsigned hexadecimal number from beginnig of string and return it.
         * number should not begin with 0x
         */
        FString CutHex();

        FString Replace(const char* findstr, const char* replstr, char quote = 0) const;
        /// nahradi vsechny vyskyty findstr retezcem replstr
        /// pokud quote != 0 ignoruje vyskyty v uvozovkach

        FString NoSpaces() const;
        /// vynda vsechny netisknutelne znaky ze stringu

        FString OneSpace() const;
        /// zredukuje pocet sousedicich mezer ve stringu na 1
        FString White2Space() const;
        /// nahradi vsechny bile znaky mezerami

        FString LTrim() const;
        FString RTrim() const;
        FString Trim() const;

        FString LPad(int newlen, char padchar = ' ') const;
        FString RPad(int newlen, char padchar = ' ') const;
        FString CPad(int newlen, char padchar = ' ') const;
		/// prida padchars z leva, z prava nebo z obou stran tak,
		/// aby delka stringu byla newlen
		/// pokud newlen<Len() string bude zkracen

        static FString Rep(char c, int cnt);
        /// vrati string vznikly opakovanim c cnt-krat

        FString& Crypt();
        FString& UnCrypt();
        /// pouziva muj vlastni, pomerne prustrelny kryptovaci mechanismus

        /** create new string using sprintf function */
        static FString printf(const char * format, ...);
        static FString vprintf(const char *_fmt, va_list _arg);

        FString toUpper() const;
        /// converts max maxlen chars of the sring to uppercase
        /// returns converted string
        FString toLower() const;
        /// converts max maxlen chars of the sring to lowercase
        /// returns converted string

        FString toAscii7() const;
        /// converts chars of the sring to 7 bit ASCII
        /// returns converted string

        /**
         * @param base can be 0, 8, 10, 16 like for strtol() function
         */
		int toInt(int base = 0) const;// {return str2intf(Str());}
		unsigned toUInt(int base = 0) const;// {return str2uintf(Str());}
		int toDecInt(int dec_places) const;// {return decimalstr2intf(Str(), dec_places);}
		//?int toTimeInt() const {return Str2Time(Str());}
		//?int toDateInt() const {return Str2Date(Str());}
		double toFloat() const {return atof(Str());}
		bool toBool() const;

        const FString& setInt(int i);// {setLen(32); int2strf(i, StrBuff(), 32); CheckStrLen(); return *this;}
        const FString& setUInt(unsigned int i);// {setLen(32); uint2strf(i, StrBuff(), 32); CheckStrLen(); return *this;}
        const FString& setInt64(int64_t i);// {setLen(32); int642strf(i, StrBuff(), 32); CheckStrLen(); return *this;}
        const FString& setTimeInt(int i);// {Copy(Time2Str(i)); return *this;}const FString& setDateInt(int i) {Copy(Date2Str(i)); return *this;}
        const FString& setDecInt(int i, int dec_places);// {setLen(32); int2decimalstrf(i, dec_places, StrBuff(), 32); CheckStrLen(); return *this;}
        const FString& setFloat(double val, int dec);
        /// returns string with dec decimal places

        static FString fromBool(bool b);
        static FString fromInt(int i);
        static FString fromUInt(unsigned u);
        static FString fromFloat(double val, int dec);

        static FString Escape(const byte *data, int len);
        FString Escape() const;
        FString UnEscape() const;

        /**
         * pokud string obsahuje nejaky vyraz, maximalne ho zjednodusi
         * zatim je implementovano pouze porovnavani stringu
         */
        FString eval() const;

        const FString& operator=(const char *str);
        /// Capacity() == 2^n

        const FString& operator=(const FString& fstr);

        const FString& operator+=(char ch) {
            Append(ch); return *this;
        }
        const FString& operator+=(const char *str) {Cat(str); return *this;}
        const FString& operator+=(const FString& fstr);

        /**
         * Compares FString and str
         * @param str string to compare with
         * @param cmp compare method
         * @see FString::enumCmpMethod
         */
        int Cmp(const char *str, enumCmpMethod cmp = FString::cmpCase) const;
        /**
         * Compares FString and other FString
         * @param fstr FString to compare with
         * @param cmp compare method
         * @see FString::enumCmpMethod
         */
        int Cmp(const FString& fstr, enumCmpMethod cmp = FString::cmpCase) const;

        bool operator<(const char *str) const {return Cmp(str) < 0;}
        bool operator<(const FString& fstr) const {return Cmp(fstr) < 0;}
        bool operator==(const char *str) const {return Cmp(str) == 0;}
        bool operator==(const FString& fstr) const {return Cmp(fstr) == 0;}
        bool operator!=(const char *str) const {return !operator==(str);}
        bool operator!=(const FString& fstr) const {return !operator==(fstr);}
        /**
         * @return true if string is empty
         */
        bool operator!() const {return isEmpty();}

        FString operator+(char c) const;
        FString operator+(int i) const;
        FString operator+(const char *str) const;
        FString operator+(const FString& fstr) const;
        friend FString operator+(const char* str, const FString &fstr);
        friend FString operator+(char c, const FString &fstr);

//        operator const char*() const {return Str();}
        /// tenhle vyvonanej operator nemuzu pouzivat, protoze je nebezpecnej
        /// if(fstr != "*") se mi prelozi na if(fstr.Str() != "*") a porovnaj se pointery
        /// pokud neexistuje bool operator!=(const char *str) const
        
        int fromStream(FILE *f);
        int toStream(FILE *f) const;

        FString noTblEof() const {
            if(*this == "TBL_EOF") return "";
            return *this;
        }

        FString noNull() const {
            if(*this == dbNullStr()) return "";
            return *this;
        }
};

//=================================================================
//=========================== FStringList =========================
//=================================================================
class FStringList : public FList<FString>
{
fpropertyrw (flist_ix_t, CmpIndex);
/// index stringu, ktery se pouzije pro operatory < a ==
public:
    FStringList(unsigned capacity = 0) : FList<FString>(capacity) {
//        setCnt(n);
        setCmpIndex(0);
    }

    /// bud StringList zkrati a uvolni prebytecne stringy
    /// nebo StringList natahne a doplni prazdnymi stringy
        void setLen(flist_ix_t newlen);
    /// funkce pro kompatibilitu s
    /// template<typename T> FString toFormatedString(...)
//    unsigned RowCnt() {return 1;}
//    int ColCnt() {return (int)Cnt();}
//    FString AsFStr(int col, unsigned row = 0) {return (*this)[col];}
    //..................................................................

    void Insert(const char *str, unsigned before_ix = 0);
    void Insert(const FString &fs, unsigned before_ix = 0);
    void Append(const char *str) {Insert(str, Cnt());}
    void Append(const FString &fs) {Insert(fs, Cnt());}

    /// fills FStringList from FString
    /// If fs ends with delim, an empty string is appended at the end of string list.
    /// if fs contain n delim chars FStringList will contain n+1 strings.
    /// Empty string produce empty string list
    /// @param strip_it strip string list after parsing
    void fromString(const FString &fs, char delim = '\t', char quote = 0, bool strip_it = true);

    /// strips leading and trailing white spaces from all strings in string list
    /// @param quotes if(quotes != 0) also quoting is stripped after stripping of white spaces
    void strip(char quotes = 0);

    FString toString(char delim = '\t', char text_delim = 0);

    void LoadFromFile(const char *filename, char text_delim = 0,
                      int skiplines=0, int linescnt=FEOFLIST);
    /// if text_delim != 0, function ignores \n in strings quoted by text_delim

    void SaveToFile(const char *filename, bool overwrite = false);

    bool operator<(const FStringList& fl) {
        if(Cnt() < CmpIndex()) return true;
        if(fl.Cnt() < CmpIndex()) return false;
        return (*this)[CmpIndex()] < fl[CmpIndex()];
    }
    bool operator==(const FStringList& fl) {
        if(Cnt() < CmpIndex()) return false;
        if(fl.Cnt() < CmpIndex()) return false;
        return (*this)[CmpIndex()] == fl[CmpIndex()];
    }

	FString& operator[](int ix) const {
        if(ix < 0) ix = Cnt() + ix;
        return FList<FString>::operator[](ix);
    }
};
//=================================================================
//========================== FStringTable =========================
//=================================================================
class FStringTable : public FList<FStringList>
{
protected:
        static FString dummyStr;
fpropertyrw (int, ColCnt);
//fpropertyrw(unsigned, CurrRow); 
public:
        FStringTable(int colcnt = 0)
            : FList<FStringList>(0) {setColCnt(colcnt); dummyStr.CStr();}

        /// neni-li specifikovan row, bere se posledni radek
        /// zjednodusuje to praci s tabulkou, kdyz se do ni pridava radek po radku
        FString& AsCell(int col, int row = FEOFLIST)
        {
            if(((unsigned)row) == FEOFLIST && RowCnt() > 0) row = RowCnt() - 1;
            return (*this)[row][col];
        }
//        const FString& Get(unsigned col, unsigned row = FEOFLIST) const;
        /// vrati referenci na bunku
        /// pokud row == FEOFLIST, bere posledni radek
        flist_ix_t RowCnt() const {return Cnt();}

        void InsertRow(unsigned before_ix = 0, FStringList *str_src = NULL);
        void AppendRow(FStringList *str_src = NULL) {InsertRow(RowCnt(), str_src);}

        const char* AsStr(int col, unsigned row = FEOFLIST) const {
            if(row == FEOFLIST) return (*this)[RowCnt() - 1][col].Str();
            return (*this)[row][col].Str();
        }
        FString AsFStr(int col, unsigned row = FEOFLIST) const {
            if(row == FEOFLIST) return (*this)[RowCnt() - 1][col];
            return (*this)[row][col];
        }
        int AsInt(int col, unsigned row = FEOFLIST) const {
            if(row == FEOFLIST) return (*this)[RowCnt() - 1][col].toInt();
            return (*this)[row][col].toInt();
        }
        FString toString(char delim = '\t', char quote = '\0');
};
//=================================================================
//---------------------------------------------------------------------------
/**
 * format je podobny printf
 * %[align][padchar][width][type]
 * align: R,L,C
 * type: n,f,s nebo 'abcdef'
 *      'abcdef' - string, ktery se tam vepise
 *       n - cislo radku
 *       f - aktualni sloupec
 *       s - zmen aktualni sloupec (SKIP) napr. %s, %+5s, %-2s, %1s
 *           jak je videt skoky mohou byt absolutni nebo relativni
 * formatovaci retezec muze byt uvozen informaci o care pred a za
 * ve tvaru <[befor char][after char]>, napr. <=-> vyrobi caru z '=' pred
 * a caru z '-' za zformatovanymi radky, < -> resp <- > vyrobi caru pouze pred resp. za
 * ma li radek zacinat znakem '<' musi se napsat "<  ><atd..."
 */
 /*?
template<typename T>
FString toFormatedString(const FString& format, const T& table, unsigned rowcnt=FEOFLIST, unsigned startrow=0)
{
    FString fs, fss;
    unsigned endrow = startrow + rowcnt;
    if(endrow > table.RowCnt()) endrow = table.RowCnt();
    int col;
    const char *fmt = format.Str();
    char padc, align, type;
    char beforec = ' ', afterc = ' ';
    int pos, width, linewidth, fmtoffset = 0;

    // cary pred a za
    if(fmt[0] == '<') {
        beforec = fmt[1]; afterc = fmt[2];
        fmtoffset = 4;
    }

    for(unsigned u=startrow; u<endrow; u++) {
        col = 0;
        linewidth = 0;
        for(pos = fmtoffset; fmt[pos]; pos++) {
            if(fmt[pos] == '%') {
                pos++;
                width = 0; padc = ' '; align = 'L';
                if(fmt[pos] == 'L' || fmt[pos] == 'R' || fmt[pos] == 'C') {
                    align = fmt[pos++];
                }
                if(!(fmt[pos] > '0' && fmt[pos] <= '9')) {
                    padc = fmt[pos++];
                }
                pos += strgetintf(fmt + pos, &width);
                type = fmt[pos];
                if(type == '\'') {  // slovo v apostrofech
                    pos++;
                    int i;
                    for(i=pos; fmt[i] && fmt[i] != '\''; i++);
                    fss = format.Slice(pos, i);
                    pos = i;
                }
                else if(type == 'f') { // field + posun ukazetel fieldu na nasledujici
                    fss = table.AsFStr(col, u);
                    col++;
                }
                else if(type == 'n') // cislo radku
                    fss = FString(u-startrow+1);
                else if(type == 's') { // shift ukazatel na field
                    if(width == INT_MIN) {width = 1; padc = '+';}
                    if(padc == '-') col -= width;
                    else            col += width;
                    continue;
                }
                else if(type == '%') fss = "%";
                else fss = "UNKNOWN_TYPE";

                if(width != INT_MIN) {
                    if(align == 'R') fss = fss.LPad(width, padc);
                    else if(align == 'C') fss = fss.CPad(width, padc);
                    else fss = fss.RPad(width, padc);
                }
                fs += fss;
                linewidth += fss.Len();
            }
            else {fs += fmt[pos]; linewidth++;}
        }
        fs += "\r\n";
    }
    if(beforec != ' ') fs = FString::Rep(beforec, linewidth) + "\r\n" + fs;
    if(afterc != ' ') fs += FString::Rep(afterc, linewidth) + "\r\n";
    return fs;
};
?*/
//---------------------------------------------------------------------------
#endif
