{
    TextFiles содержит методы для работы с текстовыми файлами: сохранение, загрузка,
    разбиение на массив строк, объединение массива строк.

    Copyright © 2016, 2019, 2022 Малик Разработчик

    Это свободная программа: вы можете перераспространять её и/или
    изменять её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она может быть полезна,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <http://www.gnu.org/licenses/>.
}

unit TextFiles;

{$MODE DELPHI}

interface

uses
    Lang,
    IOStreams;

{%region routine }
    procedure saveStringsToStream(stream: Output; const strings: AnsiString_Array1d; count: int);
    function loadStringsFromStream(stream: Input): AnsiString_Array1d;
    function splitTextToStrings(const text: AnsiString): AnsiString_Array1d;
    function makeTextStringOf(const strings: AnsiString_Array1d; count: int): AnsiString;
{%endregion}

implementation

{%region routine }
    procedure saveStringsToStream(stream: Output; const strings: AnsiString_Array1d; count: int);
    begin
        stream.write(stringToByteArray(makeTextStringOf(strings, count)));
    end;

    function loadStringsFromStream(stream: Input): AnsiString_Array1d;
    var
        b: byte_Array1d;
    begin
        b := byte_Array1d_create(int(stream.available()));
        stream.read(b);
        result := splitTextToStrings(extractString(b, 0, length(b)));
    end;

    function splitTextToStrings(const text: AnsiString): AnsiString_Array1d;
    const
        CR = #13;
        LF = #10;
    var
        i: int;
        j: int;
        len: int;
        count: int;
        strings: AnsiString_Array1d;
    begin
        len := length(text);
        if len = 0 then begin
            result := nil;
            exit;
        end;
        count := 0;
        strings := String_Array1d_create(1);
        if (len >= 3) and (text[1] = #$ef) and (text[2] = #$bb) and (text[3] = #$bf) then begin
            if len = 3 then begin
                result := nil;
                exit;
            end;
            i := 4;
        end else begin
            i := 1;
        end;
        j := i;
        repeat
            while (j <= len) and (not (text[j] in [CR, LF])) do begin
                inc(j);
            end;
            if count = length(strings) then begin
                result := String_Array1d_create((count shl 1) + 1);
                arraycopy(strings, 0, result, 0, count);
                strings := result;
                result := nil;
            end;
            strings[count] := copy(text, i, j - i);
            inc(count);
            if j > len then begin
                break;
            end;
            if (j < len) and (text[j] = CR) and (text[j + 1] = LF) then begin
                inc(j, 2);
                i := j;
                continue;
            end;
            inc(j);
            i := j;
        until false;
        if count < length(strings) then begin
            result := String_Array1d_create(count);
            arraycopy(strings, 0, result, 0, count);
            exit;
        end;
        result := strings;
    end;

    function makeTextStringOf(const strings: AnsiString_Array1d; count: int): AnsiString;
    var
        i: int;
        j: int;
        len: int;
        rlen: int;
        str: AnsiString;
    begin
        len := min(length(strings), max(0, count)) - 1;
        if len < 0 then begin
            result := '';
            exit;
        end;
        rlen := 0;
        for i := 0 to len do begin
            inc(rlen, length(strings[i]));
        end;
        inc(rlen, length(LINE_ENDING) * len);
        result := String_create(rlen);
        j := 1;
        for i := 0 to len do begin
            str := strings[i];
            rlen := length(str);
            move(str[1], result[j], rlen);
            inc(j, rlen);
            if i < len then begin
                str := LINE_ENDING;
                rlen := length(str);
                move(str[1], result[j], rlen);
                inc(j, rlen);
            end;
        end;
    end;
{%endregion}

end.

