MODULE InfoReplaceLinks;

	(*
	Replace Links
	Helmut Zinn - 

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

	IMPORT
		Converters, Dialog, Files, InfoDialog, InfoForEachFile, InfoForEachEntry, InfoLog, Kernel, 
		StdFolds, StdLinks, StdLog, Strings, TextCmds, TextMappers, 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 MakeDocName (VAR name: Files.Name);
	BEGIN
		Kernel.MakeFileName(name, ""); 	(* Append Extension *)
	END MakeDocName;

	PROCEDURE Delimiter (ch: CHAR): BOOLEAN;
	BEGIN
		RETURN (ch = "'") OR (ch = '"') OR (ch = "/")
	END Delimiter;

	PROCEDURE Stale (IN s: ARRAY OF CHAR): BOOLEAN;
		VAR
			i, j: INTEGER; ch: CHAR; loc: Files.Locator; name: Files.Name;
			t: ARRAY 1024 OF CHAR;
	BEGIN
		i := 0; ch := s[0]; WHILE (ch # 0X) & (ch # "(") DO INC(i); ch := s[i] END;
		IF ch = "(" THEN
			t := s$; t[i] := 0X;
			IF (t = "StdCmds.OpenBrowser") OR (t = "StdCmds.OpenDoc")
			OR (t = "StdCmds.OpenAuxDialog") OR (t = "InfoCmds.OpenDoc") THEN
				INC(i); ch := s[i]; WHILE (ch # 0X) & (ch # "'") & (ch # '"') DO INC(i); ch := s[i] END;
				IF ch # 0X THEN
					loc := Files.dir.This("");
					REPEAT
						j := 0;
						INC(i); ch := s[i]; WHILE ~Delimiter(ch) DO t[j] := ch; INC(j); INC(i); ch := s[i] END;
						t[j] := 0X;
						IF ch = "/" THEN loc := loc.This(t) END
					UNTIL ch # "/";
					name := t$; MakeDocName(name);
					RETURN Files.dir.Old(loc, name, Files.shared) = NIL (* file not found *)
				ELSE RETURN TRUE	(* wrong syntax *)
				END
			ELSE RETURN FALSE	(* unknown kind of command *)
			END
		ELSIF (t = "InfoCmds.Start") OR (t = "LibMisc.ShellExecute") 
		OR (t = "Dialog.OpenExternal") OR (t = "Dialog.RunExternal") THEN
			RETURN FALSE;
		ELSE RETURN FALSE	(* unknown kind of command *)
		END
	END Stale;

	PROCEDURE ChangeDoc (IN path, file: ARRAY OF CHAR; t: TextModels.Model; VAR hit: INTEGER);
		VAR 
			r: TextModels.Reader; f: TextMappers.Formatter; v: Views.View;
			pos, len: INTEGER; attr: TextModels.Attributes;
			oldcmd, cmd: ARRAY 1024 OF CHAR;
	BEGIN
		IF InfoDialog.param.expand THEN StdFolds.ExpandFolds(t, TRUE, '') END;
		hit := 0;
		f.ConnectTo(t);
		r := t.NewReader(NIL);
		r.ReadView(v);
		WHILE v # NIL DO
			WITH v: StdLinks.Link DO
				IF v.leftSide THEN
					v.GetCmd(cmd);
					IF TextCmds.find.ignoreCase THEN 
						Strings.ToLower(cmd, oldcmd) 
					ELSE 
						oldcmd := cmd 
					END;
					len := LEN(TextCmds.find.find$);
					Strings.Find(oldcmd, TextCmds.find.find, 0, pos);
					IF pos >= 0 THEN
						Strings.Replace(cmd, pos, len, TextCmds.find.replace);
						IF ~(Stale(cmd)) THEN	(* file found - link is ok *)
							INC(hit);
							(* StdLog.String('   Link changed: '); StdLog.String(cmd); StdLog.Ln; *)
							pos := r.Pos();
							attr := r.attr;
							f.SetPos(pos - 1);
							f.rider.SetAttr(attr);
							f.WriteView(StdLinks.dir.NewLink(cmd));
							t.Delete(pos, pos + 1);
						ELSIF Stale(oldcmd) THEN
							(* file not found at old position then the link is not ok *)
							StdLog.String('   *** Link failed: '); StdLog.String(cmd); StdLog.Ln;
							StdLog.String('   *** in file: '); InfoLog.WriteLink(path, file); StdLog.Ln;
						END;
					END
				END
			ELSE
			END;
			r.ReadView(v)
		END;
	END ChangeDoc;

	PROCEDURE ChangeAllDoc (IN path, filename, filetype: ARRAY OF CHAR);
		VAR
			loc: Files.Locator; fname: Files.Name;
			cv: Converters.Converter; vw: Views.View; md: TextModels.Model; 
			hit, res: INTEGER;
	BEGIN
		IF filetype = Kernel.docType THEN
			loc := Files.dir.This(path);
			fname := filename$; MakeDocName(fname);
			cv := NIL;
			vw := Views.Old(Views.dontAsk, loc, fname, cv);
			IF (vw # NIL) & (vw IS TextViews.View) THEN
				md := vw(TextViews.View).ThisModel();
				IF single THEN
					ChangeDoc(path, filename, 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(v); *)
					cv := NIL;
					Views.Register(vw, Views.dontAsk, loc, fname, cv, res);
					ASSERT(res = 0, 100);
				END;
			END;
		END;
	END ChangeAllDoc;

	PROCEDURE ReplaceLinks*;
	BEGIN
		single := TRUE;
		InfoDialog.ProofParam;
		StdLog.Ln; 
		StdLog.String("Replace Links 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("Change All Links done."); StdLog.Ln;
	END ReplaceLinks;

	PROCEDURE ReplaceLinkList*;
	BEGIN
		single := FALSE;
		InfoDialog.ProofParam;
		StdLog.Ln; 
		StdLog.String("Replace Link 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("Change Link List done."); StdLog.Ln;
	END ReplaceLinkList;

END  InfoReplaceLinks.
