MODULE StdLog;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- 20160408, center #111, code cleanups
	- 20210225, dia, reduce HostDialog dependency
	"
	issues	= "
	- ...
	"

**)

	IMPORT
		Log, Fonts, Ports, Stores, Models, Views, Dialog, StdDialog,
		TextModels, TextMappers, TextRulers, TextViews, TextControllers;

	CONST
		(** IntForm base **)
		charCode* = TextMappers.charCode; decimal* = TextMappers.decimal; hexadecimal* = TextMappers.hexadecimal;

		(** IntForm showBase **)
		hideBase* = TextMappers.hideBase; showBase* = TextMappers.showBase;

		mm = Ports.mm;

	TYPE
		ShowHook = POINTER TO RECORD (Dialog.ShowHook) END;
		LogHook = POINTER TO RECORD (Log.Hook) END;

	VAR
		logAlerts: BOOLEAN;

		text-, buf-: TextModels.Model;
		defruler-: TextRulers.Ruler;
		dir-: TextViews.Directory;

		out, subOut: TextMappers.Formatter;
		
		showHook: ShowHook;


	PROCEDURE Flush;
	BEGIN
		text.Append(buf); Views.RestoreDomain(text.Domain())
	END Flush;

	PROCEDURE Char* (ch: CHAR);
	BEGIN
		out.WriteChar(ch); Flush
	END Char;

	PROCEDURE Int* (i: LONGINT);
	BEGIN
		out.WriteChar(" "); out.WriteInt(i); Flush
	END Int;

	PROCEDURE Real* (x: REAL);
	BEGIN
		out.WriteChar(" "); out.WriteReal(x); Flush
	END Real;

	PROCEDURE String* (IN str: ARRAY OF CHAR);
	BEGIN
		out.WriteString(str); Flush
	END String;

	PROCEDURE Bool* (x: BOOLEAN);
	BEGIN
		out.WriteChar(" "); out.WriteBool(x); Flush
	END Bool;

	PROCEDURE Set* (x: SET);
	BEGIN
		out.WriteChar(" "); out.WriteSet(x); Flush
	END Set;

	PROCEDURE IntForm* (x: LONGINT; base, minWidth: INTEGER; fillCh: CHAR; showBase: BOOLEAN);
	BEGIN
		out.WriteIntForm(x, base, minWidth, fillCh, showBase); Flush
	END IntForm;

	PROCEDURE RealForm* (x: REAL; precision, minW, expW: INTEGER; fillCh: CHAR);
	BEGIN
		out.WriteRealForm(x, precision, minW, expW, fillCh); Flush
	END RealForm;

	PROCEDURE Tab*;
	BEGIN
		out.WriteTab; Flush
	END Tab;

	PROCEDURE Ln*;
	BEGIN
		out.WriteLn; Flush;
		TextViews.ShowRange(text, text.Length(), text.Length(), TextViews.any)
	END Ln;

	PROCEDURE Para*;
	BEGIN
		out.WritePara; Flush;
		TextViews.ShowRange(text, text.Length(), text.Length(), TextViews.any)
	END Para;

	PROCEDURE View* (v: Views.View);
	BEGIN
		out.WriteView(v); Flush
	END View;

	PROCEDURE ViewForm* (v: Views.View; w, h: INTEGER);
	BEGIN
		out.WriteViewForm(v, w, h); Flush
	END ViewForm;

	PROCEDURE ParamMsg* (IN msg, p0, p1, p2: ARRAY OF CHAR);
	BEGIN
		out.WriteParamMsg(msg, p0, p1, p2); Flush
	END ParamMsg;

	PROCEDURE Msg* (IN msg: ARRAY OF CHAR);
	BEGIN
		out.WriteMsg(msg); Flush
	END Msg;
	

	PROCEDURE^ Open*;

	PROCEDURE (hook: ShowHook) ShowParamMsg (IN s, p0, p1, p2: ARRAY OF CHAR);
	BEGIN
		(*IF Dialog.showsStatus THEN
			Dialog.ShowParamStatus(s, p0, p1, p2);
			IF logAlerts THEN
				ParamMsg(s, p0, p1, p2); Ln
			END
		ELSE*)
			IF logAlerts THEN
				Open;
				ParamMsg(s, p0, p1, p2); Ln
			ELSE
				IF ~(Dialog.defShowHook IS ShowHook) THEN
					Dialog.defShowHook.ShowParamMsg(s, p0, p1, p2)
				END
			END
		(*END*)
	END ShowParamMsg;
	
	PROCEDURE (hook: ShowHook) ShowParamStatus (IN s, p0, p1, p2: ARRAY OF CHAR);
	BEGIN
		IF ~(Dialog.defShowHook IS ShowHook) THEN
			Dialog.defShowHook.ShowParamStatus(s, p0, p1, p2)
		END
	END ShowParamStatus;


	PROCEDURE NewView* (): TextViews.View;
		VAR v: TextViews.View;
	BEGIN
		Flush;
		Dialog.SetShowHook(showHook); 	(* attach alert dialogs *)
		v := dir.New(text);
		v.SetDefaults(TextRulers.CopyOf(defruler, Views.deep), dir.defAttr);
		RETURN v
	END NewView;

	PROCEDURE New*;
	BEGIN
		Views.Deposit(NewView())
	END New;


	PROCEDURE SetDefaultRuler* (ruler: TextRulers.Ruler);
	BEGIN
		defruler := ruler
	END SetDefaultRuler;

	PROCEDURE SetDir* (d: TextViews.Directory);
	BEGIN
		ASSERT(d # NIL, 20); dir := d
	END SetDir;


	PROCEDURE Open*;
		VAR v: Views.View; pos: INTEGER;
	BEGIN
		v := NewView();
		StdDialog.Open(v, "#Dev:Log", NIL, "", NIL, FALSE, TRUE, FALSE, FALSE, TRUE);
		Views.RestoreDomain(text.Domain());
		pos := text.Length();
		TextViews.ShowRange(text, pos, pos, TextViews.any);
		TextControllers.SetCaret(text, pos)
	END Open;

	PROCEDURE Clear*;
	BEGIN
		Models.BeginModification(Models.notUndoable, text);
		text.Delete(0, text.Length());
		buf.Delete(0, buf.Length());
		Models.EndModification(Models.notUndoable, text)
	END Clear;
	

	(* Sub support *)

	PROCEDURE Guard (o: ANYPTR): BOOLEAN;
	BEGIN
		RETURN
			(o # NIL) &
			~(		(o IS TextModels.Model) & (o = text)
				OR   (o IS Stores.Domain) & (o = text.Domain())
				OR   (o IS TextViews.View) & (o(TextViews.View).ThisModel() = text)
			)
	END Guard;

	PROCEDURE ClearBuf;
		VAR subBuf: TextModels.Model;
	BEGIN
		subBuf := subOut.rider.Base(); subBuf.Delete(0, subBuf.Length())
	END ClearBuf;

	PROCEDURE FlushBuf;
		VAR buf: TextModels.Model;
	BEGIN
		buf := subOut.rider.Base();
		IF buf.Length() > 0 THEN
			IF ~Log.synch THEN Open() END;
			text.Append(buf)
		END
	END FlushBuf;

	PROCEDURE SubFlush;
	BEGIN
		IF Log.synch THEN
			FlushBuf;
			IF Log.force THEN Views.RestoreDomain(text.Domain()) END
		END;
	END SubFlush;

	PROCEDURE (log: LogHook) Guard* (o: ANYPTR): BOOLEAN;
	BEGIN RETURN Guard(o)
	END Guard;
	
	PROCEDURE (log: LogHook) ClearBuf*;
	BEGIN ClearBuf
	END ClearBuf;
	
	PROCEDURE (log: LogHook) FlushBuf*;
	BEGIN FlushBuf
	END FlushBuf;
	
	PROCEDURE (log: LogHook) Beep*;
	BEGIN Dialog.Beep
	END Beep;
	
	PROCEDURE (log: LogHook) Char* (ch: CHAR);
	BEGIN
		subOut.WriteChar(ch); SubFlush
	END Char;
	
	PROCEDURE (log: LogHook) Int* (n: LONGINT);
	BEGIN
		subOut.WriteChar(" "); subOut.WriteInt(n); SubFlush
	END Int;
	
	PROCEDURE (log: LogHook) Real* (x: REAL);
	BEGIN
		subOut.WriteChar(" "); subOut.WriteReal(x); SubFlush
	END Real;
	
	PROCEDURE (log: LogHook) String* (IN str: ARRAY OF CHAR);
	BEGIN
		subOut.WriteString(str); SubFlush
	END String;
	
	PROCEDURE (log: LogHook) Bool* (x: BOOLEAN);
	BEGIN
		subOut.WriteChar(" "); subOut.WriteBool(x); SubFlush
	END Bool;
	
	PROCEDURE (log: LogHook) Set* (x: SET);
	BEGIN
		subOut.WriteChar(" "); subOut.WriteSet(x); SubFlush
	END Set;
	
	PROCEDURE (log: LogHook) IntForm* (x: LONGINT; base, minWidth: INTEGER; fillCh: CHAR; showBase: BOOLEAN);
	BEGIN
		subOut.WriteIntForm(x, base, minWidth, fillCh, showBase); SubFlush
	END IntForm;
	
	PROCEDURE (log: LogHook) RealForm* (x: REAL; precision, minW, expW: INTEGER; fillCh: CHAR);
	BEGIN
		subOut.WriteRealForm(x, precision, minW, expW, fillCh); SubFlush
	END RealForm;
	
	PROCEDURE (log: LogHook) Tab*;
	BEGIN
		subOut.WriteTab; SubFlush
	END Tab;
	
	PROCEDURE (log: LogHook) Ln*;
	BEGIN
		subOut.WriteLn; SubFlush;
		IF Log.synch THEN Views.RestoreDomain(text.Domain()) END	
	END Ln;
	
	PROCEDURE (log: LogHook) Para*;
	BEGIN
		subOut.WritePara; SubFlush;
		IF Log.synch THEN Views.RestoreDomain(text.Domain()) END
	END Para;
	
	PROCEDURE (log: LogHook) View* (v: ANYPTR);
	BEGIN
		IF (v # NIL) & (v IS Views.View) THEN
			subOut.WriteView(v(Views.View)); SubFlush
		END
	END View;
	
	PROCEDURE (log: LogHook) ViewForm* (v: ANYPTR; w, h: INTEGER);
	BEGIN
		ASSERT(v # NIL, 20);
		IF (v # NIL) & (v IS Views.View) THEN
			subOut.WriteViewForm(v(Views.View), w, h); SubFlush
		END
	END ViewForm;

	PROCEDURE (log: LogHook) ParamMsg* (IN s, p0, p1, p2: ARRAY OF CHAR);
		VAR msg: ARRAY 256 OF CHAR; i: INTEGER; ch: CHAR;
	BEGIN
		IF logAlerts THEN
			IF Log.synch THEN Open END;
			Dialog.MapParamString(s, p0, p1, p2, msg);
			i := 0; ch := msg[0];
			WHILE ch # 0X DO
				IF ch = TextModels.line THEN subOut.WriteLn
				ELSIF ch = TextModels.para THEN subOut.WritePara
				ELSIF ch = TextModels.tab THEN subOut.WriteTab
				ELSIF ch >= " " THEN subOut.WriteChar(ch)
				END;
				INC(i); ch := msg[i];
			END;
			subOut.WriteLn; SubFlush
		ELSE
			Dialog.defShowHook.ShowParamMsg(s, p0, p1, p2)
		END
	END ParamMsg;


	PROCEDURE AttachSubLog;
		VAR h: LogHook;
	BEGIN
		subOut.ConnectTo(TextModels.dir.New());
		NEW(h);
		Log.SetHook(h);
	END AttachSubLog;

	PROCEDURE DetachSubLog;
	BEGIN
		Log.SetHook(NIL);
	END DetachSubLog;


	PROCEDURE Init;
		VAR font: Fonts.Font; p: TextRulers.Prop; x: INTEGER; i: INTEGER;
	BEGIN
		logAlerts := TRUE; (* logReports := FALSE; *)

		text := TextModels.dir.New();
		buf := TextModels.CloneOf(text);
		out.ConnectTo(buf);

		font := TextModels.dir.attr.font;
		defruler := TextRulers.dir.New(NIL);
		TextRulers.SetRight(defruler, 80*mm);
		dir := TextViews.dir;
		NEW(showHook)
	END Init;

BEGIN
	Init; AttachSubLog
CLOSE
	DetachSubLog; 
END StdLog.
