#ifndef CONSOLE_INC_PAS
#define CONSOLE_INC_PAS

{***************** ANSI console Terminal Functions  ***********************}


  {derived from ... }
  {ANSI Alternative to turbo pascal CRT unit}
  {By Rick Housh - CIS PIN 72466,212}
  {Uses standard ANSI calls for all cursor placement, color attribute }
  { changes, etc., }

  { google "ansi console codes" for details }

(**************************************************************************)
{ All of the Text Color constants are supported.

  None of the Crt Mode constants are supplied.

  The variable CheckBreak is present, but not implemented.
  None of the other variables are supported, as almost all have to do
  with various aspects of direct screen writing, which is not supported.

  It is possible to do much more with ANSI actually, than with many of
  Turbo's standard CRT procedures, but no extras were implemented, in
  the interest of compatibility with Turbo.

  There is one major limitation.  The window procedure is not supported.
  In the interest of universal compatibility Textmode is also not supported,
  although it could be.

  The following CRT unit functions and procedures are supported as follows:
    AssignCrt      :   Not supported
    ClrEol         :   Fully supported
    ClrScr         :   Fully supported
    Delay          :   Not supported
    DelLine        :   Not supported    (Could easily be, but never used it)
    GotoXY         :   Fully supported
    HighVideo      :   Fully supported
    InsLine        :   Not Supported    (See DelLine)
    LowVideo       :   Fully supported
    NoSound        :   Not supported
    Sound          :   Not supported
    TextBackground :   Fully supported
    TextColor      :   Fully supported
    TextMode       :   Not supported
    Window         :   Not supported
    KeyPressed     :   Fully supported
    NormVideo      :   Fully supported
    ReadKey        :   Fully supported
    WhereX         :   Fully supported
    WhereY         :   Fully supported


        This program is dedicated to the public domain.
        No copyright is claimed.
        I would be interested in reports.
                    Rick Housh
                    5811 W. 85th Terr.
                    Overland Park, KS 66207
                    Tel. 913/341-7592
                    Compuserve PIN #72466,212

}

{@@ #include <termios.h> @@}
{@@ #include <sys/select.h> @@}


  Const
     Black = 0;
     Blue = 1;
     Green = 2;
     Cyan = 3;
     Red = 4;
     Magenta = 5;
     Brown = 6;
     LightGray = 7;
     DarkGray = Black + 8;
     LightBlue = Blue + 8;
     LightGreen = Green + 8;
     LightCyan = Cyan + 8;
     LightRed = Red + 8;
     LightMagenta = Magenta + 8;
     Yellow = Brown + 8;
     White = LightGray + 8;
     Blink = 128;

  type
     byte   = 0..255;  {a small integer}

  Var
     CheckBreak, Blinking   : Boolean;
     ForeColour, BackColour : Byte;


  {Note:
     In pascal (and C), console i/o is line buffered.
     This means you can't read a character until the return key
     is pressed.
     The keyPressed and ReadKey functions below use low level trickery to
     get round this.
     This is for linux and similar systems only, other systems need to reimplement this.
  }

  { Replacement for CRT.KeyPressed
    Detects whether a key is pressed
    Does nothing with the key
    Returns true if key is pressed
    Otherwise, false
    Key remains in kbd buffer}
Function KeyPressed : boolean;

  Begin

/**
 (POSIX) implementation of _kbhit().
 Morgan McGuire, morgan@cs.brown.edu

eg see http://www.flipcode.com/archives/_kbhit_for_Linux.shtml

see also
http://www.linuxquestions.org/questions/programming-9/differences-between-ncurses-library-and-termios-struct-w-r-t-keyboard-reading-805611/#post3956458

 */
{@@ {}

    bool r;
    struct termios term, orig;

    // Use termios to turn off line buffering
    tcgetattr(STDIN_FILENO, &orig);
    term = orig;
    term.c_lflag &= ~ICANON & ~ECHO;
    tcsetattr(STDIN_FILENO, TCSANOW, &term);
    //setbuf(stdin, NULL);

    struct timeval timeout;
    timeout.tv_sec  = 0;
    timeout.tv_usec = 0;

    fd_set rdset;
    FD_ZERO(&rdset);
    FD_SET(STDIN_FILENO, &rdset);
    select(STDIN_FILENO + 1, &rdset, NULL, NULL, &timeout);
    r = FD_ISSET(STDIN_FILENO, &rdset);
    tcsetattr(STDIN_FILENO, TCSANOW, &orig);
    return r;
@@}

      KeyPressed := true; {dummy asign to prevent compile error}

  end; { KeyPressed }

  Function ReadKey : char;  { Replacement for CRT.ReadKey }
                            { Just like ReadKey in CRT unit}
  var chrout: char;
    Begin

      {Char input w/o echo}
      If CheckBreak and (chrout = chr(3)) then  {If it's a ^C and CheckBreak}
        Begin                             {then execute Ctrl_Brk}
        end;

{@@ {}
   struct termios old, tmp;
   char ch;

   tcgetattr(STDIN_FILENO, &old);
   tmp = old;
   tmp.c_lflag &= ~ICANON & ~ECHO;
   tcsetattr(STDIN_FILENO, TCSANOW, &tmp);
   int r = read(STDIN_FILENO, &ch, 1);

   tcsetattr(STDIN_FILENO, TCSANOW, &old);
   return ch;
@@}

       ReadKey := ' ';      {unused code}
  end; {ReadKey()}


{the constructor attribute means this procedure is called
 before the main program starts}
procedure  ansiSetup {@@ __attribute__ ((constructor)) @@};
begin
   {we want text emitted immediately without waiting for writeln}
   {@@ setvbuf(stdout, NULL, _IONBF, 0); @@}

   CheckBreak := false;
   BackColour := black;
   ForeColour := lightgray;
   Blinking := false;

   {pascal standard output is not ready yet => use printf}
   {@@ printf("\e[5n"); @@}
   if      (ReadKey <> chr(27) {'\e'})
        or (ReadKey <> '[')
        or (ReadKey <> '0')
        or (ReadKey <> 'n')
   then begin
      {@@ printf("ANSI console not found, quitting" ); @@}
      halt(1);
   end;
end; { ansiSetup() }


  Procedure  ClrEol;     { ANSI replacement for CRT.ClrEol }
    Begin
      Write(chr(27), '[K');
    end;

  Procedure ClrScr;     { ANSI replacement for CRT.ClrScr }
    Begin
      Write(chr(27), '[2J');
    end;

{cursor position, 1 is top left corner}
procedure getPos(var xPos,yPos : byte);
var                         { Cursor position report. }
   ch  : char;               { This is column or X axis report.}

   procedure getNum(var pos :byte );
   begin
      pos := 0;
      ch := ReadKey;
      while ch in ['0'..'9'] do begin
         pos := 10*pos + ord(ch) - ord('0');
         ch := ReadKey;
      end;
   end; { getNum }

begin
   Write(chr(27), '[6n');    { Ansi string to get X-Y position }
   ch := ReadKey;            { Return will be }
                             { Esc - [ - Ypos - ; - Xpos - R }
   xPos := 0;
   if ch = chr(27) then begin
      ch := ReadKey;
      if ch = '[' then begin
         getNum(yPos);
         if ch = ';' then begin
            getNum(xPos);
            {if ch = 'R' then
            writeln('ansi terminal, cursor is at (',
            xPos:1, ',', yPos:1, ')');}
         end;
      end;
   end;
end; { getPos }

  Function WhereX : byte;       { ANSI replacement for CRT.WhereX }
    var
      xPos,yPos   : byte;
    begin
       getPos(xPos, yPos);
       WhereX := xPos;            { Return the number }
    end;

  Function WhereY : byte;       { ANSI replacement for CRT.WhereY }
    var
      xPos,yPos   : byte;
    begin
       getPos(xPos, yPos);
       WhereY := yPos;            { Return the number }
    end;


    Procedure GotoXY(x : byte ; y : byte); { ANSI replacement for CRT.GoToXY}
      Begin
        If (x >= 1) and (y >= 1) {and
           (x <= 80) and (y <= 25)} then
        Write(chr(27), '[',y,';',x,'H');
      end;


{ Move the cursor n positions in the given direction.
  If the cursor is already at the edge of the screen, this has no effect.
}

procedure CursorUp(n :integer);
begin
   write(chr(27), '[', n:1, 'A');
end; { CursorUp }

procedure CursorDown(n :integer);
begin
   write(chr(27), '[', n:1, 'B');
end; { CursorDown }

procedure CursorForward(n :integer);
begin
   write(chr(27), '[', n:1, 'C');
end; { CursorForward }

procedure CursorBack(n :integer);
begin
   write(chr(27), '[', n:1, 'D');
end; { CursorBack }


procedure hideCursor;
begin
  writeln(chr(27), '[?25l');  // hidecursor
end; { hideCursor }

procedure showCursor;
begin
  writeln(chr(27), '[?25h');  // restore cursor
end; { showCursor }

{screen & text colours}
   Procedure TextBackGround(Back : Byte); {Replacement for CRT.TextBackground}
     Begin
       If Back <= 7 then begin     { No illegal values allowed }
          BackColour := Back;
          Case Back of
            0  :  Write(chr(27), '[40m');
            1  :  Write(chr(27), '[44m');
            2  :  Write(chr(27), '[42m');
            3  :  Write(chr(27), '[46m');
            4  :  Write(chr(27), '[41m');
            5  :  Write(chr(27), '[45m');
            6  :  Write(chr(27), '[43m');
            7  :  Write(chr(27), '[47m');
          end;  { Case }
       end;
     end;


   Procedure TextColor(Fore : Byte);
      Begin
       If not ((Fore in [0..15]) or (Fore in [128..143])) then begin
          writeln('unhandled value of fore(', fore:1, ')' );
       end
       else begin
          ForeColour := Fore;
          Blinking := False;
          Write(chr(27), '[0m');
          TextBackGround(BackColour);
          If Fore >=  128 then begin
             Fore := Fore - 128;
             Blinking := True;
             Write(chr(27), '[5m');
          end;
          Case Fore of
            0  :  Write(chr(27), '[30m');
            1  :  Write(chr(27), '[34m');
            2  :  Write(chr(27), '[32m');
            3  :  Write(chr(27), '[36m');
            4  :  Write(chr(27), '[31m');
            5  :  Write(chr(27), '[35m');
            6  :  Write(chr(27), '[33m');
            7  :  Write(chr(27), '[37m');
            8  :  Write(chr(27), '[1;30m');
            9  :  Write(chr(27), '[1;34m');
            10  :  Write(chr(27), '[1;32m');
            11  :  Write(chr(27), '[1;36m');
            12  :  Write(chr(27), '[1;31m');
            13  :  Write(chr(27), '[1;35m');
            14  :  Write(chr(27), '[1;33m');
            15  :  Write(chr(27), '[1;37m');
          end;  { Case }
       end; {if}
     end;


   {video modes}
   Procedure NormVideo;   { ANSI Replacement for CRT.NormVideo }
     Begin
       Write(chr(27), '[0m');
       ForeColour := LightGray;
       BackColour := Black;
     end;

   Procedure LowVideo;    { Replacement for CRT.LowVideo }
     Begin
       If ForeColour > 7 then ForeColour := ForeColour - 8;
       Write(chr(27), '[0m');
       TextBackGround(BackColour);
       If not Blinking then TextColor(ForeColour)
       else TextColor(ForeColour + 128);
     end;

   Procedure HighVideo;   { Replacement for CRT.HighVideo }
     Begin
       If ForeColour < 8 then ForeColour := ForeColour + 8;
       If Not Blinking then TextColor(ForeColour)
       else TextColor(ForeColour + 128);
     end;

{%%%%%%%%%%%%%%%%%%%%%%%%% end of console.inc.pas %%%%%%%%%%%%%%%%%%%%%%%%%%%%%}

#endif // CONSOLE_INC_PAS
