grendel-1.0.0a7/backup/
grendel-1.0.0a7/bin/
grendel-1.0.0a7/boards/
grendel-1.0.0a7/clans/
grendel-1.0.0a7/documentation/todo/
grendel-1.0.0a7/help/
grendel-1.0.0a7/logs/
grendel-1.0.0a7/players/
grendel-1.0.0a7/progs/
grendel-1.0.0a7/races/
grendel-1.0.0a7/src/contrib/
grendel-1.0.0a7/src/modules/speller/
grendel-1.0.0a7/src/modules/status/
grendel-1.0.0a7/src/tests/
grendel-1.0.0a7/src/tests/dunit/
{
	Summary:
  	Abstract console interface
  	
  ##	$Id: console.pas,v 1.16 2004/04/14 19:35:12 druid Exp $
}

unit console;

interface


uses
	SysUtils,
	Contnrs,
	dtypes;
  

type
	GConsoleWriter = class
	public
		procedure write(timestamp : integer; const text : string; debugLevel : integer = 0); virtual; abstract;
	end;
  
	GConsoleLogWriter = class(GConsoleWriter)
	private
		logFile : textfile;

	public
		constructor Create(const moduleName : string);
		destructor Destroy(); override;
	
		procedure write(timestamp : integer; const text : string; debugLevel : integer = 0); override;
	end;

	GConsole = class(GSingleton)
	private
		writers : GDLinkedList;
		history : GDLinkedList;
		
		synchronizer : TMultiReadExclusiveWriteSynchronizer;

	public
		constructor actualCreate(); override;
		destructor actualDestroy(); override;

	published
		procedure write(const text : string; debugLevel : integer = 0);

		procedure attachWriter(writer : GConsoleWriter);
		procedure detachWriter(writer : GConsoleWriter);

		procedure fetchHistory(callback : GConsoleWriter; max : integer = 0);
		
		function fetchHistoryTimestamp(callback : GConsoleWriter; timestamp : integer) : integer;
	end;

  
procedure writeConsole(const text : string; debugLevel : integer = 0);


implementation


uses
	DateUtils,
	fsys;


type
	GConsoleHistoryElement = class
	private
		_timestamp : integer;
		_text : string;
		_debugLevel : integer;

	public
		property timestamp : integer read _timestamp write _timestamp;
		property text : string read _text write _text;
		property debugLevel : integer read _debugLevel write _debugLevel;
	end;
  

const
	{ Maximum number of items in the console history }
	CONSOLE_HISTORY_MAX = 200;
  

var
	cons : GConsole;


{ GConsole constructor }
constructor GConsole.ActualCreate();
begin
	writers := GDLinkedList.Create();
	history := GDLinkedList.Create();
	
	synchronizer := TMultiReadExclusiveWriteSynchronizer.Create();
end;

{ GConsole destructor }
destructor GConsole.ActualDestroy();
begin
	writers.clear();
	history.clear();
	
	writers.Free();
	history.Free();
	
	synchronizer.Free();
end;

{ Attach a GConsoleWriter object to the console }
procedure GConsole.attachWriter(writer : GConsoleWriter);
begin
	writers.add(writer);
end;

{ Detach a GConsoleWriter object from the console }
procedure GConsole.detachWriter(writer : GConsoleWriter);
begin
	writers.remove(writer);
end;

{ Write a message to the console }
procedure GConsole.write(const text : string; debugLevel : integer = 0);
var
	he : GConsoleHistoryElement;
	iterator : GIterator;
	writer : GConsoleWriter;	
begin
	// lock write
	synchronizer.BeginWrite();
	
	try
		if (history.size() >= CONSOLE_HISTORY_MAX) then
			begin
			he := GConsoleHistoryElement(history.head.element);
			history.remove(history.head);
			he.Free();
			end;

		he := GConsoleHistoryElement.Create();
		he.timestamp := DateTimeToUnix(Now);
		he.text := text;
		he.debugLevel := debugLevel;
		history.add(he);

		iterator := writers.iterator();

		while (iterator.hasNext()) do
			begin
			writer := GConsoleWriter(iterator.next());

			writer.write(he.timestamp, he.text, he.debugLevel);
			end;    

		iterator.Free();	
	finally
		// unlock write
		synchronizer.EndWrite();
	end;
end;

{ Fetch (up to) max items from the history and feed them to callback }
procedure GConsole.fetchHistory(callback : GConsoleWriter; max : integer = 0);
var
	iterator : GIterator;
	he : GConsoleHistoryElement;
	count : integer;
begin
	// lock read
	synchronizer.BeginRead();
	
	try
		iterator := history.iterator();
		count := 0;

		while (iterator.hasNext()) do
			begin
			he := GConsoleHistoryElement(iterator.next());

			callback.write(he.timestamp, he.text);

			inc(count);

			if (max > 0) and (count >= max) then
				break;
			end;

		iterator.Free();
	finally
		// unlock read
		synchronizer.EndRead();
	end;
end;

{ Enumerate all items with item.timestamp >= timestamp, return last known timestamp }
function GConsole.fetchHistoryTimestamp(callback : GConsoleWriter; timestamp : integer) : integer;
var
	iterator : GIterator;
	he : GConsoleHistoryElement;
begin
	// lock read
	synchronizer.BeginRead();
	
	Result := timestamp;
	
	try
		iterator := history.iterator();

		while (iterator.hasNext()) do
			begin
			he := GConsoleHistoryElement(iterator.next());
			
			Result := he.timestamp;
			
			if (he.timestamp <= timestamp) then
				continue;

			callback.write(he.timestamp, he.text);
			end;
			
		iterator.Free();
	finally
		// unlock read
		synchronizer.EndRead();
	end;
end;

procedure writeConsole(const text : string; debugLevel : integer = 0);
begin
	cons.write(text, debugLevel);
end;


{ GConsoleLogWriter constructor }
constructor GConsoleLogWriter.Create(const moduleName : string);
begin
	inherited Create();

	{ open a standard log file, filename is given by current system time }
	AssignFile(logFile, translateFileName('logs\' + moduleName + '.log'));

	{$I-}
	Append(logFile);

	if (IOResult <> 0) then
		Rewrite(logFile);
	{$I+}

	if (IOResult <> 0) then
		writeConsole('Could not open logfile');
end;

{ GConsoleLogWriter destructor }
destructor GConsoleLogWriter.Destroy();
begin
	if (TTextRec(logfile).mode = fmOutput) then
		CloseFile(LogFile);

	inherited Destroy();
end;

{ Writes to logfile }
procedure GConsoleLogWriter.write(timestamp : integer; const text : string; debugLevel : integer = 0);
begin
	if (TTextRec(logfile).mode = fmOutput) then
		begin
		system.writeln(logfile, '[' + FormatDateTime('yyyymmdd hh:nn:ss', UnixToDateTime(timestamp)) + '] ' + text);
		system.flush(logfile);
		end;
end;


initialization
	cons := GConsole.Create();
	
finalization
	FreeAndNil(cons);
	
end.