unit Text_file;
interface
implementation
(*
/*

CLASS {Text_file|_Delphi_and_FPC},
STOPPING, NATIVE, UNTESTED, BYTES 
		:

IMPORT: {System_functions}, {String_functions}, {File_stream}, {Stream}, 
		{Memory_stream_contig}, {Long_int} .
IMPORT, TEST: {File_path} .

NATIVE:@"uses System_functions,"
		"	  {$IFDEF LINUX} Text_file "
		"	  {$ELSE} Windows "
	    "	  {$ENDIF} ;" 
	    .
NATIVE: "///COPY Text_file.pas" .

STR Path|_to_text_file, INIT

{String_functions}.{encoding} Encoding

FUN Text_load|_first_clear_lines(
STR Path|_file, STR Lines|_array[] )
    :
    Lines[].Clear
    temp|_loader = {Text_file}(Path = Path)
    NONE = temp.Load(Lines[]) .
    
FUN Load_text|_first_clear_lines(
STR Lines|_array[], STR Path|_from)
    :
	Text_load(Path, Lines[]) .


METHOD Load|_strings(STR Lines|_to_APPEND_to[]) ==> BOOL
    :
	Lines[].Clear
	{File_stream} fs|_to_load_temp = Read_file_stream(Path)
	//DEBUG: << "was encoding: " Encoding.Name " ~~~ " fs.Encoding.Name #NL ;
    //CASE fs.Encoding IN ['DEFAULT', 'ASCII'] && Encoding IN ['UTF8', 'ASCII'] ?
    //     fs.Set_encoding(Encoding)
    //     //DEBUG: "-->" fs.Encoding.Name #NL ;
    //;
	-------------------- 'new version - fast'
	mem|ory_stream_temp = {Memory_stream_contig}
	mem.Set_capacity(fs.Size.To_int)
	mem.Copy_stream(fs)
	mem.Set_encoding(fs.Encoding)
	mem.Rewind
	fs.Close
	fs = NONE
	-------------------- 'check for faster ways' 
	CASE Encoding=='DEFAULT' ? mem.Detect_encoding
	ELSE mem.Set_encoding(Encoding) ;     
	STR pinfo|_of_platform = Platform_info.Lower
    CASE ?
    [mem.Encoding IN ['DEFAULT', 'ASCII'] && "fpc" IN pinfo] ?
        SILENT:
			STR source|_as_string = mem.Memory[].Ansi_bytes_to_utf8 ;
    //[Encoding == 'UTF16' && "c#" IN pinfo] ? -- this code is for Delphi/FPC only
    //    source = mem.Memory[].Bytes_to_utf16
    [mem.Encoding IN ['DEFAULT', 'ASCII'] && ("vcl" IN pinfo //|| "kol" IN pinfo -- for KOL there is another version of {Text_file}
     )] ?
		source = mem.Memory[0 TO *].Bytes_to_ascii
    [mem.Encoding == 'UTF8' && "fpc" IN pinfo] ?
        source = mem.Memory[0 TO *].Bytes_to_utf8
    ELSE
    	//DEBUG: "old version mem.Size=" mem.Size.S #NL ;
		-------------------- 'old version - slow'
		FOR i|_dummy IN [0 TO mem.Size.To_int] :
		    CASE mem.Ended ? BREAK i ;
			Lines[] << mem.Get_line
			//DEBUG: << "read:[" Lines[*] "]"#NL ;
		;
		RESULT = TRUE ==>
    ;
	Lines[].Split(source, #LF)
	//DEBUG: "source=[" source "] " Lines[].Count #NL ;
	FOR s IN Lines[] : Lines[s.INDEX] = s.Remove_ending(#CR) ;
	RESULT = TRUE .
	
FUN From_text_file(STR Lines|_cleared_first[],
	STR Path|_text_file, {encoding} Encoding) 
    :
    temp|_loader = {Text_file}(Path= Path, Encoding= Encoding)
    Lines[].Clear
    NONE = temp.Load(Lines[]) .
         
FUN To_text_file(
STR Lines|_to_save[], STR Path|_where, {encoding} Encoding)
    :
    temp|saver = {Text_file}(Path= Path, Encoding= Encoding)
    NONE = temp.Save(Lines[]) .
         
	
    
FUN Unix_text_save( STR Path|_file, STR Lines|_array[] ) :
    temp|_saver = {Text_file}(Path = Path)
    temp.Line_end = #10
    NONE = temp.Save(Lines[]) .
    
STR Line_end|ing_sequence = #13#10	

FUN Text_save( STR Path|_file, STR Lines|_array[] ) :
    temp|_saver = {Text_file}(Path = Path)
    NONE = temp.Save(Lines[]) .
    
FUN Save_to|_file(STR Lines|_array[], STR Path|_file) :
	Text_save(Path, Lines[]) .
    

FUN Unicode_save( STR Path|_file, STR Lines|_array[] ) :
    temp|_saver = {Text_file}(Path = Path, Encoding= 'UTF8')
    NONE = temp.Save(Lines[]) .

METHOD Save|_strings_as_ANSI_or_UTF8_only(
    STR Lines|_to_save[]) ==> BOOL
    := do_Save(Path, Lines[], Line_end, Encoding == 'UTF8') .
FUN do_Save|_strings(STR Path|_save_to, STR Lines|_to_save[], STR Line_end,
BOOL Is_UTF8|_encoding)
==> BOOL, NATIVE :
    Stop_if_test(Path)
    NATIVE @
    "var dir, s: String;"
    "var SL: TStringList;"
    "var i: Integer;"
    "var WS: WideString;"
    "var size: Integer;"
    "var b: PByte; len_b: Integer;"
    "var fs: TFileStream;"
    "    dir := ExtractFilePath({$IFDEF FPC}Str_X_Path{$ELSE}X_Path{$ENDIF});"
    "    if dir <> '' then begin"
    " 		if not DirectoryExists(dir) then"
    "          CreateDir(dir);"
    "    	if not DirectoryExists(dir) then exit;"
    "	 end;"     
    "    SL := TStringList.Create;"
    "    try"
    "		{$IFNDEF LINUX}"
    "        if X_Is_utf8 then"
    "        begin"
    "           GetMem(b, 4096);"
    "			len_b := 4096;"     
    "           for i := 0 to X_Lines.Count()-1 do"
    "           begin"
    "               WS := X_Lines.GetStrings(i);"
    "               size := WideCharToMultiByte(CP_UTF8, 0,"
    "                   PWideChar(WS), Length(WS), nil, 0, nil, nil);"
    "			 //writeln('handle line: ' + WS + ' size=' + IntToStr(size));"
    "               if size = 0 then"
    "                  SL.Add('')"
    "               else"
    "               begin"
    "                   if len_b <= size then"
    "					begin"
    "						len_b := size + 1;"     
    "                       //SetLength(b, size+1);"
    "						ReallocMem(b, size + 1)"     
    "					end;"     
    "                   WideCharToMultiByte(CP_UTF8, 0, PWideChar(WS), "
    "                      Length(WS), PChar(b), size, nil, nil);"
    "                   PByte(Integer(b) + size)^ := 0;"
    "                   SL.Add(PChar(b));"
    "               end"
    "           end;"
    "           FreeMem(b);"
    "        end"
    "        else"
    "		{$ENDIF}"
    "           for i := 0 to X_Lines.Count()-1 do"
    "               SL.Add(X_Lines.GetStrings(i));"
    "		 fs := TFileStream.Create("
    "			{$IFDEF FPC}Str_X_Path{$ELSE}X_Path{$ENDIF}, fmCreate);"
    "	  //writeln('path=' + X_Path + ' handle=' + IntToStr(fs.Handle));"     
    "		 try"
    "			for i := 0 to SL.Count-1 do begin"
    "				s := SL {$IFDEF KOL} .Items {$ENDIF} [i];"
    "	 //writeln('writing line len=' + IntToStr(Length(s)) + ':' + s);"     
    "				fs.Write(s[1], Length(s));"
    "				if i = SL.Count-1 then break;"     
    "				fs.Write({$IFDEF FPC}Str_X_Line_end"
    "						 {$ELSE}X_Line_end{$ENDIF}[1],"
    "						 Length({$IFDEF FPC}Str_X_Line_end"
    "						 {$ELSE}X_Line_end{$ENDIF}));"
    "			end;"  
	"		 finally"
	"			fs.Free;"
	"		 end;"
    "        Result := TRUE;"
    "    finally SL.Free;"
    "    end;" .

TEST save_load :
     t|emp_text = {Text_file}(
     	Path = Start_dir "$TEST".Trailing_slash "test.txt")
     STR lines|_to_save[] << "test1" << "test2" << "test3"
     Delete_file(t.Path)
     ASSERT !t.Path.Exists
     ASSERT t.Save(lines[])
     ASSERT t.Path.Exists
     BOOL ok|_loaded = t.Load(lines[])
     ASSERT ok, lines[].Count.S
     .
        
END
*/
*)
end.

