MODULE InfoSearch;

	(* Helmut Zinn  *)

	IMPORT
		Containers, Converters, Dialog, Documents, DosKeyboard, (* DosSort, *) Files, Fonts,
		InfoDialog, Models, Ports, StdFolds, StdLinks, Stores, Strings, TextCmds, TextMappers,
		TextModels, TextRulers, TextViews, Views;

	CONST
		maxPat = 64;
		underline = FALSE;

	TYPE
		Pattern = ARRAY maxPat OF CHAR;
		Text = POINTER TO RECORD
			next: Text;
			num: INTEGER;
			title: Files.Name
		END;

	VAR
		msg: ARRAY 256 OF CHAR;
		w: TextMappers.Formatter;
		count: INTEGER;

	PROCEDURE List (list: Text; pat: Pattern);
		VAR a0: TextModels.Attributes; cmd: ARRAY 384 OF CHAR; this, t: Text; max, cnt: INTEGER;
	BEGIN
		IF list = NIL THEN
			Dialog.MapString("#Dev:NoMatchFound", cmd);
			w.WriteString(cmd)
		ELSE
			cnt := 0;
			a0 := w.rider.attr;
			w.rider.SetAttr(TextModels.NewWeight(w.rider.attr, Fonts.bold));
			Dialog.MapString("#Dev:Location", cmd);
			w.WriteString(cmd); w.WriteTab;
			Dialog.MapString("#Dev:Count", cmd);
			w.WriteString(cmd); w.WriteLn;
			w.rider.SetAttr(a0);
			REPEAT
				t := list; max := 1; this := NIL;
				WHILE t # NIL DO
					IF t.num >= max THEN
						this := t;
						IF InfoDialog.param.sortHit THEN max := t.num END;
					END;
					t := t.next
				END;
				IF this # NIL THEN
					cnt := cnt + 1;
					cmd := "InfoCmds.OpenDoc('" + this.title + "'); InfoCmds.Select('" + pat + "')";
					w.WriteView(StdLinks.dir.NewLink(cmd));
					w.rider.SetAttr(TextModels.NewColor(w.rider.attr, Ports.blue));
					IF underline THEN w.rider.SetAttr(TextModels.NewStyle(w.rider.attr, {Fonts.underline})) END;
					w.WriteString(this.title);
					w.rider.SetAttr(a0);
					w.WriteView(StdLinks.dir.NewLink(""));
					w.WriteTab; w.WriteInt(this.num); w.WriteLn;
					this.num := 0
				END
			UNTIL this = NIL;
			w.WriteInt(cnt); w.WriteString(' Documents found'); w.WriteLn;
		END;
	END List;

	PROCEDURE NewRuler (): TextRulers.Ruler;
		CONST mm = Ports.mm;
		VAR p: TextRulers.Prop;
	BEGIN
		NEW(p);
		p.valid := {TextRulers.right, TextRulers.tabs, TextRulers.opts};
		p.opts.val := {TextRulers.rightFixed}; p.opts.mask := p.opts.val;
		p.right := 100 * mm;
		p.tabs.len := 1;
		p.tabs.tab[0].stop := 80 * mm;
		p.tabs.tab[0].type := {TextRulers.rightTab};
		RETURN TextRulers.dir.NewFromProp(p)
	END NewRuler;

	PROCEDURE ThisText (loc: Files.Locator; name: Files.Name): TextModels.Model;
		VAR fi: Files.File; cn: Converters.Converter; st: Stores.Store; vw: Views.View; md: Models.Model;
	BEGIN
		fi := Files.dir.Old(loc, name$, Files.shared);
		(* search in converter list for a converter that can import a file of this type *)
		cn := Converters.list; WHILE (cn # NIL) & (cn.fileType # fi.type) DO cn := cn.next END;
		Converters.Import(loc, name$, cn, st);
		(* vw := Views.OldView(loc, name);	-- wenn nur .odc Dateien *)
		vw := Views.Old(Views.dontAsk, loc, name, cn);
		IF vw = NIL THEN RETURN NIL END;
		IF vw IS Documents.Document THEN vw := vw(Documents.Document).ThisView() END;
		md := vw.ThisModel();
		IF md = NIL THEN RETURN NIL END;
		WITH md: TextModels.Model DO RETURN md ELSE RETURN NIL END;
	END ThisText;

	PROCEDURE SearchDoc (t: TextModels.Model; pat: Pattern; title: Files.Name; VAR list: Text);
		VAR r: TextModels.Reader; num: INTEGER; i, j, b, e, n: INTEGER; ch: CHAR; ref: Pattern; l: Text;
	BEGIN
		IF InfoDialog.param.expand THEN StdFolds.ExpandFolds(t, TRUE, '') END;
		n := 0; num := 0;
		WHILE pat[n] # 0X DO INC(n) END;
		r := t.NewReader(NIL);
		r.SetPos(0); r.ReadChar(ch);
		WHILE ~r.eot DO
			ref[0] := ch; i := 0; j := 0; b := 0; e := 1;
			WHILE ~r.eot & (i < n) DO
				IF (pat[i] = ch) OR (TextCmds.find.ignoreCase) & (Strings.Upper(pat[i]) = Strings.Upper(ch))
					THEN INC(i); j := (j + 1) MOD maxPat
				ELSE i := 0; b := (b + 1) MOD maxPat; j := b
				END;
				IF j # e THEN ch := ref[j]
				ELSE r.ReadChar(ch); ref[j] := ch; e := (e + 1) MOD maxPat
				END
			END;
			IF i = n THEN INC(num) END
		END;
		IF num > 0 THEN
			NEW(l); l.num := num; l.title := title; l.next := list; list := l
		END
	END SearchDoc;

	PROCEDURE SearchLoc (path: ARRAY OF CHAR; VAR list: Text; pat: Pattern);
		VAR
			root: Files.Locator;
			locs: Files.LocInfo;
			files: Files.FileInfo;
			t: TextModels.Model;
	BEGIN
		root := Files.dir.This(path);
		files := Files.dir.FileList(root);
		(* DosSort.FileList(files); *)
		WHILE ~DosKeyboard.Break() & (files # NIL) DO
			(* The Extension files.type$ is always lower case *)
			IF (InfoDialog.param.filter$ = '') OR (InfoDialog.param.filter$ = files.type$) THEN
				t := ThisText(root, files.name);
				IF t # NIL THEN
					INC(count);
					InfoDialog.ShowStatus(count, msg$ + ' ' + path$ + files.name$);
					SearchDoc(t, pat, path$ + files.name$, list)
				END;
			END;
			files := files.next
		END;
		IF InfoDialog.param.include THEN
			locs := Files.dir.LocList(root);
			(* DosSort.LocList(locs); *)
			WHILE ~DosKeyboard.Break() &  (locs # NIL) DO
				SearchLoc(path$ + locs.name$ + "/", list, pat);
				locs := locs.next
			END;
		END;
	END SearchLoc;

	PROCEDURE SearchInAllDocuments*;
		VAR
			pat: Pattern;
			log: TextModels.Model; v: Views.View; title: Views.Title; (* c: Containers.Controller; *) list: Text;
	BEGIN
		InfoDialog.ProofParam;
		pat := TextCmds.find.find$;
		IF pat # "" THEN
			count := 0;
			Dialog.MapString("#Dev:Searching", msg);
			Dialog.ShowStatus(msg);
			TextCmds.find.find := pat$;
			log := TextModels.dir.New();
			w.ConnectTo(log); w.SetPos(0);
			Dialog.MapString("#Std:Search for", title);
			Strings.Extract(title$ + '  "' + pat + '"', 0, 127, title);
			w.WriteString(title); w.WriteLn;
			SearchLoc(InfoDialog.param.source, list, pat);
			List(list, pat);
			v := TextViews.dir.New(log);
			v(TextViews.View).SetDefaults(NewRuler(), TextViews.dir.defAttr);
			Views.OpenView(v);
			(*
			Views.OpenAux(v, title);
			(* set Browser mode: *)
			c := v(Containers.View).ThisController();
			c.SetOpts(c.opts + {Containers.noCaret});
			*)
			w.ConnectTo(NIL);
			IF DosKeyboard.Break() THEN
				DosKeyboard.ResetBreak;
			ELSE
				Dialog.ShowStatus("");
			END;
		END;
	END SearchInAllDocuments;

	PROCEDURE SearchGuard* (VAR par: Dialog.Par);
	BEGIN
		par.disabled := TextCmds.find.find$ = "";
	END SearchGuard;

END InfoSearch.
