MODULE editor1;

(* based on the kilo editor building course*)

(* Step 28 *)

(******************** INCLUDES ******************)

IMPORT IO, Strings, ASCII, NumberIO, termios, FIO, Delay, SplitV1, STextIO;
IMPORT libc;

(******************** DATA ******************)

VAR
    editorConfig        : RECORD
                            screenrows : CARDINAL;
                            screencols : CARDINAL;
                            theTermios : termios.TERMIOS;
                         END;

    TCSAFLUSH           : INTEGER;

    theStructure        : SplitV1.Structure;

    cursorposStr        : ARRAY [0..15] OF CHAR;
    clearScreenStr      : ARRAY[0..6] OF CHAR;
    cursorHomeStr       : ARRAY[0..2] OF CHAR;
    requestCursorPosStr : ARRAY[0..15] OF CHAR;

    resultStr           : ARRAY [0..15] OF CHAR;
    tmpString           : ARRAY[0..15] OF CHAR;
    tmpString1          : ARRAY[0..15] OF CHAR;
    tmpString2          : Strings.String1;
    cc                  : Strings.String1;
    i                   : CARDINAL;
    str                 : ARRAY [0..15] OF CHAR;
 
(******************** Utilities *****************)

    PROCEDURE Card2Str ( x : CARDINAL; VAR str : ARRAY OF CHAR);

    BEGIN
    IF x < 10 THEN
            NumberIO.CardToStr(x,1,str);
        ELSIF x < 100 THEN
            NumberIO.CardToStr(x,2,str);
        ELSE
            NumberIO.CardToStr(x,3,str);
        END;
    END Card2Str;

    (******************** TERMINAL ******************)

    PROCEDURE CtrlKey(c: CHAR) : CARDINAL;
        (* return a value < ORD(" ") for the control keys *)
        VAR
            n : BITSET;

    BEGIN
        n := BITSET(ORD(c));
        n := n * BITSET(1FH);
        RETURN CARDINAL(n)
    END CtrlKey;

    PROCEDURE die (s : ARRAY OF CHAR);
    BEGIN
        libc.perror(s);
        FOR i := 0 TO Strings.Length(s) DO
            IO.Write(s[i])
        END;
        HALT
    END die;

    PROCEDURE disablerawMode() : INTEGER;

        VAR
            result : INTEGER;
    BEGIN
        IO.BufferedMode(0,TRUE);
        IO.BufferedMode(1,TRUE);
        result := termios.tcsetattr(FIO.StdIn, TCSAFLUSH, editorConfig.theTermios);
        IF result = -1 THEN
            die("tcsetattr")
        ELSE 
            RETURN result
        END;
    END disablerawMode;

    PROCEDURE enablerawMode;
    BEGIN
        libc.atexit(disablerawMode);

        editorConfig.theTermios := termios.InitTermios();
        (* raw := termios.InitTermios(); *)
        IF termios.tcgetattr(FIO.StdIn, editorConfig.theTermios) = -1 THEN
            die("tcgetattr")
        END;

        IO.UnBufferedMode(0,TRUE);
        IO.UnBufferedMode(1,TRUE);
    END enablerawMode;

    PROCEDURE  editorReadKey() : CHAR;
        VAR
            c : CHAR;
    BEGIN
        IO.Read(c);
        RETURN c
    END editorReadKey; 

    (******************** output ******************)

    PROCEDURE editorDrawRows;

        VAR
            y : CARDINAL;

    BEGIN
        FOR y := 1 TO 24 DO
            IO.Write("~");
            IO.Write(CHR(13));
            IO.Write(CHR(10));
        END;
    END editorDrawRows;

   PROCEDURE editorRefreshScreen;

    BEGIN
        (* modif *)
        (* WriteString(clearScreenStr);
        WriteString(cursorHomeStr); *)
        FOR i := 0 TO 3 DO
            IO.Write(clearScreenStr[i])
        END;
        FOR i := 0 TO 3 DO
            IO.Write(cursorHomeStr[i])
        END;
        editorDrawRows;
        (* WriteString(cursorHomeStr); *)
        FOR i := 0 TO 3 DO
            IO.Write(cursorHomeStr[i])
        END;
    END editorRefreshScreen;

    PROCEDURE requestCursorPosition(VAR x,y : CARDINAL);

        VAR
            c   : CHAR;

    BEGIN
        str := "";
        SplitV1.InitStructure(theStructure);
        Strings.Concat(str,ASCII.esc,str);
        Strings.Concat(str,"[6n", str);
        (* sending request *)
        FOR i := 0 TO 3 DO
            IO.Write(str[i])
        END;
        (* getting answer *)
        str := "";
        REPEAT
            IO.Read(c);
            cc[0] := c;
            Strings.Concat(str,cc,str);
        UNTIL c = "R";

        (* displaying the retour , changing the escape to ? *)
        str[0] := "?";
        FOR i := 0 TO Strings.Length(str) DO
            IO.Write(str[i])
        END;

        (* extracting the coordinates *)
        Strings.Delete(str,0,2);
        Strings.Delete(str,Strings.Length(str) -1 ,1);
        SplitV1.SplitStr(str, ";", theStructure);
        NumberIO.StrToCard(theStructure[0].element,x);
        NumberIO.StrToCard(theStructure[1].element,y);
        
        (* NumberIO.WriteCard(x, 5);
        NumberIO.WriteCard(y, 5); *)
    END requestCursorPosition;

    PROCEDURE cursorpos(x,y : CARDINAL);
    BEGIN
        cursorposStr := "";
        Strings.Concat(cursorposStr, ASCII.esc,cursorposStr);
        Strings.Concat(cursorposStr, "[",cursorposStr);
        tmpString := "";
        Strings.Concat(tmpString,cursorposStr, tmpString);
        tmpString1 := "";
        Card2Str(y,tmpString1);
        Strings.Concat(tmpString,tmpString1, tmpString);
        tmpString2[0] := ";";
        Strings.Concat(tmpString, tmpString2,tmpString);
        tmpString1 := "";
        Card2Str(x,tmpString1);
        Strings.Concat(tmpString,tmpString1, tmpString);
        tmpString2[0] := "H";
        Strings.Concat(tmpString, tmpString2,tmpString);
        FOR i := 0 TO Strings.Length(tmpString) DO
            IO.Write(tmpString[i])
        END;

    END cursorpos;

    PROCEDURE getWindowSize(VAR y : CARDINAL; VAR x: CARDINAL);
    
    BEGIN
        cursorpos(999,999);
        requestCursorPosition(x,y);
    END getWindowSize;

(******************** input  ******************)

    PROCEDURE editorProcessKeypress;
        VAR
            c       : CHAR;
            b       : BOOLEAN;
    BEGIN
        c := editorReadKey();
        CASE ORD(c) OF
          17 : HALT;
        END
    END editorProcessKeypress;

(********************  INIT  ******************)

PROCEDURE InitScreenEscapes;

VAR temp : ARRAY[0..10] OF CHAR;
BEGIN
    cursorHomeStr := "";
    Strings.Concat(cursorHomeStr,ASCII.esc,cursorHomeStr);
    Strings.Concat(cursorHomeStr,"[H",cursorHomeStr);

    clearScreenStr := "";
    Strings.Concat(clearScreenStr,ASCII.esc,clearScreenStr);
    Strings.Concat(clearScreenStr,"[2J",clearScreenStr);

    cursorposStr := "";
    Strings.Concat(cursorposStr,ASCII.esc,cursorposStr);
    Strings.Concat(cursorposStr,"[",cursorposStr);
   
    requestCursorPosStr := "";
    Strings.Concat(requestCursorPosStr,ASCII.esc,requestCursorPosStr);
    Strings.Concat(requestCursorPosStr,"[6n",requestCursorPosStr);
END InitScreenEscapes;

    PROCEDURE InitEditor;

    BEGIN
        TCSAFLUSH := termios.tcsflush (); 
        InitScreenEscapes;
        getWindowSize(editorConfig.screenrows, editorConfig.screencols);
    END InitEditor;


BEGIN
    
    enablerawMode;
    InitEditor;
                            
    LOOP
        editorRefreshScreen;
        editorProcessKeypress;
        editorRefreshScreen;
    END;


    (* Delay.Delay(2000); *)
    IF disablerawMode() = -1 THEN
        HALT
    END;    
       
    NumberIO.WriteCard(editorConfig.screencols, 5);
    NumberIO.WriteCard(editorConfig.screenrows, 5);
END editor1.