MODULE InfoReplaceTexts;

	(*
	Replace Texts
	Helmut Zinn - 

	"StdCmds.OpenToolDialog('Info/Rsrc/ReplaceAll', 'Replace in all Docu')"
	*)

	IMPORT
		Converters, Dialog, Documents, Files, InfoDialog, InfoForEachEntry, InfoForEachFile, InfoLog,
		StdFolds, StdLog, Stores, Strings, TextCmds, TextModels, TextViews, Views;

		(*
		uses
		InfoDialog.param.source
		InfoDialog.param.include
		InfoDialog.param.expand
		&
		TextCmds.find.find
		TextCmds.find.replace
		TextCmds.find.ignoreCase
		*)

	VAR
		single: BOOLEAN;

	PROCEDURE ChangeDoc (IN path, file: ARRAY OF CHAR; md: TextModels.Model; VAR hit: INTEGER);
		CONST
			maxPat = 256;
		VAR
			rd: TextModels.Reader; i, j, a, b, e, nfd, nrp: INTEGER; ch: CHAR; ref: ARRAY maxPat OF CHAR;
			wr: TextModels.Writer; buf: TextModels.Model; attr: TextModels.Attributes;
	BEGIN
		IF InfoDialog.param.expand THEN StdFolds.ExpandFolds(md, TRUE, '') END;
		hit := 0;
		nfd := LEN(TextCmds.find.find$);
		nrp := LEN(TextCmds.find.replace$);
		rd := md.NewReader(NIL);
		rd.SetPos(0); rd.ReadChar(ch);
		WHILE ~rd.eot DO
			(* Find *)
			ref[0] := ch; i := 0; j := 0; b := 0; e := 1;
			WHILE ~rd.eot & (i < nfd) DO
				IF (TextCmds.find.find[i] = ch)
					OR (TextCmds.find.ignoreCase) & (Strings.Upper(TextCmds.find.find[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 rd.ReadChar(ch); ref[j] := ch; e := (e + 1) MOD maxPat
				END
			END;
			(* Replace *)
			IF i = nfd THEN
				INC(hit);
				b := rd.Pos() - 1;
				a := b - nfd;
				buf := TextModels.CloneOf(md); wr := buf.NewWriter(NIL);
				rd.SetPos(a); rd.ReadChar(ch); attr := rd.attr; wr.SetPos(0); wr.SetAttr(attr);
				FOR i := 0 TO nrp - 1 DO
					wr.WriteChar(TextCmds.find.replace[i]);
				END;
				md.Delete(a, b); md.Insert(a, buf, 0, nrp);
				rd.SetPos(a + nrp);
			END;
			rd.ReadChar(ch);
		END;
	END ChangeDoc;

	PROCEDURE ChangeAllDoc (IN path, filename, filetype: ARRAY OF CHAR);
		VAR
			loc: Files.Locator; fname: Files.Name;
			cv: Converters.Converter; st: Stores.Store; vw: Views.View; md: TextModels.Model;
			hit, res: INTEGER;
	BEGIN
		IF (InfoDialog.param.filter$ = '') OR (InfoDialog.param.filter$ = filetype$) THEN
			loc := Files.dir.This(path);
			fname := filename$;
			(* search in converter list for a converter that can import a file of this type *)
			cv := Converters.list; WHILE (cv # NIL) & (cv.fileType # filetype) DO cv := cv.next END;
			Converters.Import(loc, filename$, cv, st);
			vw := Views.Old(Views.dontAsk, loc, fname, cv);
			IF (vw # NIL) & (vw IS Documents.Document) THEN
				vw := vw(Documents.Document).ThisView()
			END;
			IF (vw # NIL) & (vw IS TextViews.View) THEN
				md := vw(TextViews.View).ThisModel();
				IF single THEN
					ChangeDoc("", "", md, hit);
				ELSE
					InfoForEachEntry.Do(ChangeDoc, path, filename, md, hit);
				END;
				IF hit > 0 THEN
					StdLog.String('> Save file: '); StdLog.Int(hit); StdLog.String(' ');
					InfoLog.WriteLink(path, filename); StdLog.Ln;
					(* Views.OpenView(vw); *)
					Views.Register(vw, Views.dontAsk, loc, fname, cv, res);
					ASSERT(res = 0, 100);
				END;
			END;
		END;
	END ChangeAllDoc;

	PROCEDURE ReplaceTexts*;
	BEGIN
		single := TRUE;
		InfoDialog.ProofParam;
		StdLog.Ln; 
		StdLog.String("Replace Texts started ..."); StdLog.Ln;
		StdLog.String("Root Directory: "); StdLog.String(InfoDialog.param.source); StdLog.Ln;
		IF TextCmds.find.ignoreCase THEN
			Strings.ToLower(TextCmds.find.find, TextCmds.find.find);
			Dialog.Update(TextCmds.find);
		END;
		InfoForEachFile.Do(ChangeAllDoc, InfoDialog.param.source, InfoDialog.param.include);
		StdLog.String("Replace All Text done."); StdLog.Ln;
	END ReplaceTexts;
	
	PROCEDURE ReplaceTextList*;
	BEGIN
		single := FALSE;
		InfoDialog.ProofParam;
		StdLog.Ln; 
		StdLog.String("Replace Text List started ..."); StdLog.Ln;
		StdLog.String("Root Directory: "); StdLog.String(InfoDialog.param.source); StdLog.Ln;
		InfoForEachEntry.LogList;
		InfoForEachFile.Do(ChangeAllDoc, InfoDialog.param.source, InfoDialog.param.include);
		StdLog.String("Replace Text List done."); StdLog.Ln;
	END ReplaceTextList;

END InfoReplaceTexts.
