(*----------------------------------------------------------------------*)

IMPLEMENTATION MODULE Searches;

(*----------------------------------------------------------------------*)
(*

Version: 0.05/01

*)
(*----------------------------------------------------------------------*)

FROM Strings IMPORT LengthC;

FROM Universe IMPORT BYTE, TabCh, EndOfLineCh, EscapeCh, MarkerFlagByte;
FROM List IMPORT ListType, ListP, True, False, Self;
FROM Errors IMPORT EditorErrors;
FROM Windows IMPORT WindowStatusF;
FROM Memory IMPORT ALLOCATE;
FROM Actions IMPORT BuiltInAction, Bind, PushAndBind, PopAndUnBind;
FROM DoSearch IMPORT DoSearch, DoReplace;

IMPORT
   Errors, Commands, Windows, Buffers, StringData, Execute,
   Display, Screen, Characters, List;

IMPORT HighSearch;

(*----------------------------------------------------------------------*)

CONST
   BackwardsMeta  = 002H; (* ctrl-B *)
   ForwardsMeta   = 006H; (* ctrl-F *)

   SearchKey      = 087H;
   ReplaceKey	  = 088H;
   CopyKey	  = 08BH;
   ReturnKey	  = 00DH;

VAR Previous: RECORD
                 Search	  : StringData.StringP;
                 Replace  : StringData.StringP;
  	      END;

(*----------------------------------------------------------------------*)

PROCEDURE DisplaySearchCh (Ch: BYTE; VAR Pending: Windows.PendingR): BOOLEAN;

   VAR marker: Buffers.MarkerP;

   BEGIN
      IF (Ch = BYTE (ForwardsMeta)) OR (Ch = BYTE (BackwardsMeta)) THEN
         IF Pending.CountValid THEN
  	    Pending.WidePartialCount := 0;
	    Pending.CountValid := FALSE;
	    IF Ch = BYTE (ForwardsMeta) THEN Screen.UpdateCh (BYTE (Characters.RightArrowHeadChar))
	    ELSE Screen.UpdateCh (BYTE (Characters.ArrowShaftChar))
	    END (* if *);
	    RETURN TRUE
         ELSE
 	    IF Ch = BYTE (ForwardsMeta) THEN Screen.UpdateCh (BYTE (Characters.ArrowShaftChar))
 	    ELSE Screen.UpdateCh (BYTE (Characters.LeftArrowHeadChar))
	    END (* if *);
	    Pending.WidePartialCount := 1;
	    Pending.CountValid := TRUE;
	    RETURN FALSE
         END (* if *)
      ELSIF Ch = TabCh THEN
         Screen.UpdateCh (CHAR (Characters.TabHeadChar));
         RETURN TRUE
      ELSIF CARDINAL (Ch) >= 080H THEN
	 IF Ch = MarkerFlagByte THEN
	    marker := NIL;
	    WHILE Buffers.MarkerAt (Display.CurrentWindow^.Buffer, Display.CurrentArea.Area.Start, marker) DO
	       IF marker^.DisplaySize > 0 THEN
		  RETURN Display.CurrentWindow^.DisplayMarker (marker, Pending)
	       END (* if *)
	    END (* while *)
	 END (* if *);
         Screen.InverseCh (CHAR (ORD (Ch) - 080H));
         RETURN TRUE
      ELSE RETURN Display.DisplayCharacter (Ch, Pending)
      END (* if *)
   END DisplaySearchCh;

(*----------------------------------------------------------------------*)

PROCEDURE DisplaySearchChWidth (Ch: BYTE; X, ArrayIndex: CARDINAL): CARDINAL;

   VAR marker: Buffers.MarkerP;

   BEGIN
      IF (Ch = BYTE (ForwardsMeta)) OR (Ch = BYTE (BackwardsMeta)) THEN RETURN 2
      ELSIF Ch = TabCh THEN RETURN 1
      ELSIF CARDINAL (Ch) >= 080H THEN
         IF Ch = MarkerFlagByte THEN
 	    marker := NIL;
	    WHILE Buffers.MarkerAt (Display.CurrentWindow^.Buffer, ArrayIndex, marker) DO
	       IF marker^.DisplaySize > 0 THEN
                  RETURN marker^.DisplaySize
  	       END (* if *)
	    END (* while *)
         END (* if *);
         RETURN 1
      ELSE RETURN Display.DisplayWidth (Ch, X, ArrayIndex)
      END (* if *)
   END DisplaySearchChWidth;

(*----------------------------------------------------------------------*)

PROCEDURE UpDatePrevious (VAR previous : StringData.StringP;
     			      new      : ARRAY OF CHAR      );

   BEGIN
      IF previous # NIL THEN StringData.Delete (previous) END;
      IF StringData.CopyCD (new, previous) THEN END;
   END UpDatePrevious;

(*----------------------------------------------------------------------*)

PROCEDURE Forwards (VAR searchStr: ARRAY OF CHAR): BOOLEAN;

   VAR
      ch   : CHAR;
      len  : CARDINAL;
      i    : CARDINAL;

   BEGIN
      ch := searchStr [0];
      IF (ch = CHAR (BackwardsMeta)) OR (ch = CHAR (ForwardsMeta)) THEN
         len := LengthC (searchStr);
         FOR i := 0 TO len - 2 DO searchStr [i] := searchStr [i + 1] END;
         searchStr [len - 1] := 0C;
         RETURN (ch = CHAR (ForwardsMeta))
      ELSE RETURN TRUE
      END (* if *);
   END Forwards;

(*----------------------------------------------------------------------*)

PROCEDURE SearchInit;

   CONST
      BackSlashKey  = 05CH;
      CtrlBKey      = 002H;
      CtrlFKey      = 006H;

   VAR
      meta  : CARDINAL;
      ch    : CHAR;
      arg   : List.ListP;

   BEGIN
(*
      PushAndBind (Windows.Selected^.Buffer, BYTE (BackSlashKey), "Literal", NIL);
*)
      arg := List.Cons (List.MakeAtom (BackwardsMeta, NumberT), NIL);
      PushAndBind (Windows.Selected^.Buffer, BYTE (CtrlBKey), "Insert", arg);
      arg := List.Cons (List.MakeAtom (ForwardsMeta, NumberT), NIL);
      PushAndBind (Windows.Selected^.Buffer, BYTE (CtrlFKey), "Insert", arg);
(*
      FOR meta := MetaSensitivity TO MetaFound DO
         ch := VARSearchReplaceMeta [meta - 080H];
         arg := List.Cons (List.MakeAtom (meta, NumberT), NIL);
         PushAndBind (Windows.Selected^.Buffer, ch, "Insert", arg)
      END (* for *);
*)
      Windows.Selected^.DisplayCh := DisplaySearchCh;
      Windows.Selected^.WidthDisplayCh := DisplaySearchChWidth;
      Windows.Selected^.ValidateRow := HighSearch.ValidateSearch;

      INCL (Windows.Selected^.Status, DisplayTabsAsArrowsF);
      
   END SearchInit;

(*----------------------------------------------------------------------*)

PROCEDURE SearchAckn (param: ARRAY OF CHAR; VAR acknowledge: ARRAY OF CHAR);

   BEGIN
      acknowledge [0] := ']';
      acknowledge [1] := 0C;
   END SearchAckn;

(*----------------------------------------------------------------------*)

PROCEDURE Search (arg: ListP): ListP;

   VAR
      activeWindow  : Windows.WindowP;
      res           : INTEGER;

   BEGIN
      activeWindow := Windows.Selected;
      res := Commands.Interactive1 (activeWindow,
                                    SearchInit,
                                    "Search [",
                                    Previous.Search,
                                    SearchAckn,
                                    SearchProc      );
      IF res >= 0 THEN RETURN True
      ELSE
         Errors.Message ("");
         RETURN False
      END (* if *);
   END Search;

PROCEDURE SearchProc (VAR activeWindow: Windows.WindowP; searchStr: ARRAY OF CHAR): INTEGER;

   VAR forwards: BOOLEAN;

   BEGIN
      UpDatePrevious (Previous.Search, searchStr);
      forwards := Forwards (searchStr);
      IF DoSearch (activeWindow, searchStr, forwards) THEN RETURN 0
      ELSE RETURN -1
      END (* if *);
   END SearchProc;

(*----------------------------------------------------------------------*)

PROCEDURE Replace (arg: ListP): ListP;

   VAR
      activeWindow  : Windows.WindowP;
      res           : INTEGER;

   BEGIN
      activeWindow := Windows.Selected;
      res := Commands.Interactive2 (activeWindow,
                                    SearchInit,
                                    "Replace [",
                                    Previous.Search,
                                    "] by [",
                                    Previous.Replace,
                                    SearchAckn,
                                    ReplaceProc      );
      IF res >= 0 THEN RETURN True
      ELSE
         Errors.Message ("");
         RETURN False
      END (* if *);
   END Replace;
   
PROCEDURE ReplaceProc (VAR activeWindow: Windows.WindowP; searchStr, replaceStr: ARRAY OF CHAR): INTEGER;

   VAR
      paramsEnd  : CARDINAL;
      forwards   : BOOLEAN;

   BEGIN
      paramsEnd := Windows.Selected^.Buffer^.Before.End;
      UpDatePrevious (Previous.Search, searchStr);
      UpDatePrevious (Previous.Replace, replaceStr);
      forwards := Forwards (searchStr);
      IF DoReplace (activeWindow, searchStr, replaceStr, forwards) THEN RETURN 0
      ELSE
         Windows.Selected^.Buffer^.Before.End := paramsEnd;
         RETURN -1
      END (* if *);
   END ReplaceProc;

(*----------------------------------------------------------------------*)

PROCEDURE RepeatSearch (arg: ListP): ListP;

   VAR
      activeWindow  : Windows.WindowP;
      seq           : ARRAY [0..2] OF BYTE;

   BEGIN
      activeWindow := Windows.Selected;
      PushAndBind (activeWindow^.Buffer, BYTE (SearchKey), "Search", NIL);
      seq [0] := BYTE (SearchKey);
      seq [1] := BYTE (CopyKey);
      seq [2] := BYTE (ReturnKey);
      Execute.SetSequence (seq, 3);
      PopAndUnBind (activeWindow^.Buffer, BYTE (SearchKey));
      RETURN True
   END RepeatSearch;

(*----------------------------------------------------------------------*)

PROCEDURE RepeatReplace (arg: ListP): ListP;

   VAR
      activeWindow  : Windows.WindowP;
      seq           : ARRAY [0..4] OF BYTE;

   BEGIN
      activeWindow := Windows.Selected;
      PushAndBind (activeWindow^.Buffer, BYTE (ReplaceKey), "Search", NIL);
      seq [0] := BYTE (ReplaceKey);
      seq [1] := BYTE (CopyKey);
      seq [2] := BYTE (ReturnKey);
      seq [3] := BYTE (CopyKey);
      seq [4] := BYTE (ReturnKey);
      Execute.SetSequence (seq, 5);
      PopAndUnBind (activeWindow^.Buffer, BYTE (ReplaceKey));
      RETURN True
   END RepeatReplace;

(*----------------------------------------------------------------------*)

PROCEDURE Initialise;

   BEGIN
      Previous.Search := NIL;
      Previous.Replace := NIL;

      BuiltInAction ("ISearch", Search, "Search from Cursor");
      BuiltInAction ("IReplace", Replace, "Search and Replace");

      BuiltInAction ("RepeatSearch", RepeatSearch, "Repeat last search");
      BuiltInAction ("RepeatReplace", RepeatReplace, "Repeat last replace");

      Bind (BYTE (SearchKey), "ISearch", NIL);
      Bind (BYTE (ReplaceKey), "IReplace", NIL);

      Bind (BYTE (097H), "RepeatSearch", NIL);
      Bind (BYTE (098H), "RepeatReplace", NIL)

  END Initialise;

(*----------------------------------------------------------------------*)

PROCEDURE Terminate;

   BEGIN
   END Terminate;

(*----------------------------------------------------------------------*)

END Searches.

