MODULE TFLog; (** AUTHOR "TF"; PURPOSE "Log utility"; *)

IMPORT KernelLog, Clock, Files;

CONST
	Unknown* = 0;
	Information* = 1;
	Warning* = 2;
	Error* = 3;

TYPE
	Log* = OBJECT
	VAR
		appName : ARRAY 64 OF CHAR;
		logPos : LONGINT;
		logLine : ARRAY 1024 OF CHAR;
		kind : LONGINT;
		locked : BOOLEAN;
		disableLogToOut : BOOLEAN;
		f : Files.File;
		w : Files.Writer;

		PROCEDURE &Init*(logName : ARRAY OF CHAR);
		BEGIN
			COPY(logName, appName)
		END Init;

		PROCEDURE SetLogFile*(fn : ARRAY OF CHAR);
		BEGIN {EXCLUSIVE}
			IF f # NIL THEN Files.Register(f) END;
			IF fn = "" THEN f := NIL
			ELSE
				f := Files.Old(fn);
				IF f = NIL THEN f := Files.New(fn) END;
				Files.OpenWriter(w, f, f.Length())
			END
		END SetLogFile;

		PROCEDURE SetLogToOut*(enabled : BOOLEAN);
		BEGIN {EXCLUSIVE}
			disableLogToOut := ~enabled
		END SetLogToOut;

		PROCEDURE SetKind(kind : LONGINT);
		BEGIN {EXCLUSIVE}
			SELF.kind := kind
		END SetKind;

		PROCEDURE InternalLn;
		BEGIN
			logPos := 0; kind := Unknown;
			IF ~disableLogToOut THEN
				KernelLog.Enter;
				IF kind = Information THEN KernelLog.String("[I] ") END;
				IF kind = Warning THEN KernelLog.String("[W] ") END;
				IF kind = Error THEN KernelLog.String("[E] ") END;
				KernelLog.String(appName); KernelLog.String(" : "); KernelLog.String(logLine);
				KernelLog.Exit
			END;
			logLine[0] := 0X;
			IF f # NIL THEN w.Ln; w.Update; f.Update() END
		END InternalLn;

		PROCEDURE Ln*;
		BEGIN {EXCLUSIVE}
			InternalLn
		END Ln;

		PROCEDURE Enter*;
		BEGIN {EXCLUSIVE}
			AWAIT(~locked); locked := TRUE
		END Enter;

		PROCEDURE Exit*;
		BEGIN {EXCLUSIVE}
			InternalLn;
			locked := FALSE
		END Exit;

		PROCEDURE InternalChar(x: CHAR);
		BEGIN
			IF logPos >= LEN(logLine) - 1 THEN InternalLn END;
			logLine[logPos] := x; logLine[logPos + 1] := 0X; INC(logPos);
			IF f # NIL THEN w.Char(x) END
		END InternalChar;

		PROCEDURE Char*(x: CHAR);
		BEGIN {EXCLUSIVE}
			InternalChar(x)
		END Char;

		PROCEDURE InternalString*(VAR x: ARRAY OF CHAR);
		VAR i : LONGINT;
		BEGIN
			WHILE (i < LEN(x)) & (x[i] # 0X) DO InternalChar(x[i]); INC(i) END
		END InternalString;

		PROCEDURE String*(x: ARRAY OF CHAR);
		BEGIN {EXCLUSIVE}
			InternalString(x)
		END String;

		PROCEDURE Hex*(x, w: LONGINT);
		VAR i, j: LONGINT; buf: ARRAY 10 OF CHAR;
		BEGIN {EXCLUSIVE}
			IF w >= 0 THEN j := 8 ELSE j := 2; w := -w END;
			FOR i := j+1 TO w DO InternalChar(" ") END;
			FOR i := j-1 TO 0 BY -1 DO
				buf[i] := CHR(x MOD 10H + 48);
				IF buf[i] > "9" THEN
					buf[i] := CHR(ORD(buf[i]) - 48 + 65 - 10)
				END;
				x := x DIV 10H
			END;
			buf[j] := 0X;
			InternalString(buf)
		END Hex;

		PROCEDURE Int*(x, w: LONGINT);
		VAR i, x0: LONGINT; a: ARRAY 12 OF CHAR;
		BEGIN {EXCLUSIVE}
			IF x < 0 THEN
				IF x = MIN(LONGINT) THEN
					DEC(w, 11);
					WHILE w > 0 DO Char(" "); DEC(w) END;
					a := "-2147483648"; InternalString(a);
					RETURN
				ELSE
					DEC(w); x0 := -x
				END
			ELSE
				x0 := x
			END;
			i := 0;
			REPEAT
				a[i] := CHR(x0 MOD 10 + 30H); x0 := x0 DIV 10; INC(i)
			UNTIL x0 = 0;
			WHILE w > i DO InternalChar(" "); DEC(w) END;
			IF x < 0 THEN InternalChar("-") END;
			REPEAT DEC(i); InternalChar(a[i]) UNTIL i = 0
		END Int;

		PROCEDURE TimeStamp*;
		TYPE TimeDate = RECORD h, m, s, day,month,year: LONGINT END;
			VAR s : ARRAY 32 OF CHAR;
				now : TimeDate;

			PROCEDURE LZ(v, len: LONGINT; VAR s: ARRAY OF CHAR; VAR pos: LONGINT);
			VAR i: LONGINT;
			BEGIN
				FOR i := 1 TO len DO s[pos+len-i] := CHR(ORD("0")+v MOD 10); v := v DIV 10 END;
				INC(pos, len)
			END LZ;

			PROCEDURE GetTime(VAR dt: TimeDate);
			BEGIN
				Clock.Get(dt.h, dt.year);
				dt.s := dt.h MOD 64; dt.h := dt.h DIV 64;
				dt.m := dt.h MOD 64; dt.h := dt.h DIV 64;
				dt.h := dt.h MOD 24;
				dt.day := dt.year MOD 32; dt.year := dt.year DIV 32;
				dt.month := dt.year MOD 16; dt.year := dt.year DIV 16;
				INC(dt.year, 1900)
			END GetTime;

			PROCEDURE TimeDateToStr(dt: TimeDate; VAR s: ARRAY OF CHAR);
			VAR p: LONGINT;
			BEGIN
				LZ(dt.day, 2, s, p); s[p] := "."; INC(p);
				LZ(dt.month, 2, s, p); s[p] := "."; INC(p);
				LZ(dt.year, 2, s, p); s[p] := " "; INC(p);
				LZ(dt.h, 2, s, p); s[p] := ":"; INC(p);
				LZ(dt.m, 2, s, p); s[p] := ":"; INC(p);
				LZ(dt.s, 2, s, p); s[p] := 0X
			END TimeDateToStr;

		BEGIN
			GetTime(now);
			TimeDateToStr(now, s);
			InternalString(s); InternalChar(" ")
		END TimeStamp;

		PROCEDURE Close*;
		BEGIN
			IF f # NIL THEN Files.Register(f)
			END
		END Close;

	END Log;

END TFLog.


System.Free TFLog ~