{**************************************************
 ***        PROGRAM CONFIGURATION/SETUP         ***
 **************************************************}
{$I compflgs}

{The options are all stored in one file - kameleon.cfg - for overall
desktop/global options, as well as ones specific to objects, such as
person lists.

Objects have access to the options via the Get and Put methods.

At the moment, the options are all stored in memory, and are written
away any time anything is put - as the options will not be changed
often (?) and this means the saves are permanant}

unit kamsetup;

INTERFACE

uses objects;

const
	MaxFileLines = 90;

type
	PProgramSetup = ^TProgramSetup;
	TProgramSetup = object(TObject)

		FileLine : array[1..MaxFileLines] of PString;
		LastLine : byte;
		Group : string; {current group}

		constructor Init;

		procedure Clear;
		procedure Load;
		procedure Store;

		procedure SetGroup(NGroup : string);
		function FindGroup : integer;
		function FindCommand(Command : string) : integer;

		function CommandOfLine(L : integer) : string;
		function ParameterOfLine(L : integer) : string;


		procedure Put(const Command : string; Parameter : string);
		function  Get(const Command, Default : string) : string;
		function  GetPath(const Command, Default : string) : string;

		function GetBoolean(const Command : string; Default : boolean) : Boolean;
		procedure PutBoolean(Command : string; Parameter : boolean);
	end;


function ConfigFileName : FNameStr; {for editconfig in maintenance}

procedure LoadGlobalSetup; {loads terminal number, dead data mode, etc, etc}

var
	ProgramSetup : TProgramSetup;

	{see also global.pas for direct access vars, eg datapath}

IMPLEMENTATION

uses
{$IFDEF Windows}
			wincrt, {for debug writeln's etc}
			winmsgs,
{			winfiles, {for path checking}
{$ELSE}
{			dosutils, {for path checking}
			tuimsgs,
{$ENDIF}
			dosutils,
			help,
			tasks,
			messtext,
			global,
			strings,
			minilib;

function ConfigFileName : FNameStr;
var I : integer;
		S : string;

begin
	{Locate configfile name from parameters, if given}
{	S := StrNew('KAMELEON.CFG'); {Default - make sure the name, ie Kameleon.cfg
															is the maximum size of the string, so there is
															room for the messing about below (ie don't do
															a newstr('HI') as that would not leave enough
															memory aside for longer names}
	S := '';

	{look for /cfg= on command line}
	for I := 1 to ParamCount do
		if ucase(Copy(ParamStr(I),1,5))='/CFG=' then
			S := Copy(ParamStr(I),6,255);

	if length(S) = 0 then S := 'KAMELEON.CFG'; {JIC not given properly}

	if Pos('.',S)=0 then S := S + '.CFG';

{$IFDEF Windows}
sdf	ConfigFileName := S;
{$ELSE}
	ConfigFileName := S;
{$ENDIF}
end;

{*******************************************
 ***            INITIALISE               ***
 *******************************************}

constructor TProgramSetup.Init;
var I : integer;

begin
	inherited Init;

	for I := 1 to MaxFileLines do FileLine[I] := nil;

	LastLine := 0;
	Group := '';
	Load;
end;


{*******************************************
 ***          LOAD/STORE                 ***
 *******************************************}
procedure TProgramSetup.Clear;
var LineNo : byte;
begin
	for LineNo := 1 to MaxFileLines do
		if FileLine[LineNo]<>nil then begin
			DisposeStr(FileLine[LineNo]);
			FileLine[LineNo] := nil;
		end;
	LastLine := 0;
end;



procedure TProgramSetup.Load;
var	Line : string;
		ConfigFile : text;
		LastIOResult : longint;

begin
	{Just reads straight into array
	for specific Setup to extract as desired}
{	ThinkingOn('Loading Configuration');{}

	Clear;   {First clear array - eg if already loaded}

	{Set up file}
	FileMode := 2;
	Assign(ConfigFile, ConfigFileName);

{$I-}
	reset(ConfigFile);
	LastIOResult := IOresult;
	if LastIOResult=0 then begin {May be empty}
{$I+}
		while not eof(ConfigFile) do begin
			Readln(ConfigFile, Line);

			inc(LastLine, 1);
			FileLine[LastLine] := NewStr(Line);  {Reserve space on heap & set}
		end;

		close(ConfigFile);
	end else
		ProgramError('Could not locate cfg '+#13#10+ConfigFileName+#13#10+IOError(LastIOResult),hcNoConfigMsg);

{	ThinkingOff;{}
end;


procedure TProgramSetup.Store;
var LineNo : integer;
		ConfigFile : text;
		LastIOResult : integer;

begin
	{Write straight out to file}
{	ThinkingOn('Storing Configuration');{}

	{do a check here, 'cos if this fails it tends to scrap the whole file...}
	LastIOresult := IOresult;
	if LastIOREsult<>0 then
		ProgramWarning('Waiting IOResult='+IOError(LastIOResult)+#13#10
										+'Found at TProgramSetup.Store', hcIOErrorMsg);

	{Set up file}
	Assign(ConfigFile, ConfigFileName);
{$I-}

	rewrite(ConfigFile);
{$I+}
	LastiOResult := IOResult;
	if LastIOResult = 0 then begin
		for LineNo := 1 to LastLine do
			if FileLine[LineNo]<>nil then
				writeln(ConfigFile, FileLine[LineNo]^)
			else
				writeln(ConfigFile,'');

		close(ConfigFile);
	end else
		ProgramWarning('Could not store Config File'#13#10
										+IOError(LastIOResult), hcIOErrorMsg);

{	ThinkingOff;{}
end;


procedure TProgramSetup.SetGroup;
begin Group := NGroup; end;

function TProgramSetup.FindGroup;
var L : integer;
begin
	if Group = '' then
		FindGroup := 1
	else begin
		L := 1;
		while (L<=LastLine) and
			((FileLine[L]=nil) or (ucase(delspaceR(FileLine[L]^))<>'['+ucase(Group)+']')) do
				inc(L);

		if L>LastLine then FindGroup := -1 else FindGroup := L;
	end;
end;

function TProgramSetup.FindCommand(Command : string) : integer;
var L : integer;
begin
	L := FindGroup;
	if L = -1 then begin FindCommand := -1; exit; end;

	{try and locate command in group}
	if (FileLine[L]<>nil) and (FileLine[L]^[1]='[') then L := L+1;
	while (L<=LastLine) and
				((FileLine[L]=nil) or
				 ((FileLine[L]^[1] <> '[') and (CommandOfLine(L)<>ucase(Command)))) do
					inc(L);

	if (L>LastLine) or ((FileLine[L]<>nil) and (FileLine[L]^[1]='[')) then
		FindCommand := -1
	else
		FindCommand := L;
end;

function TProgramSetup.CommandOfLine(L : integer) : string;
begin
	if FileLine[L] = nil then
		CommandOfLine := ''
	else
		CommandOfLine := ucase(delspaceR(Copy(FileLine[L]^,1,pos('=',FileLine[L]^+'=')-1)));
end;

function TProgramSetup.ParameterOfLine(L : integer) : string;
begin
	if FileLine[L] = nil then
		ParameterOfLine := ''
	else
		ParameterOfLine := ucase(delspace(Copy(FileLine[L]^,pos('=',FileLine[L]^+'=')+1,255)));
end;


{***************************************
 ***   CHANGE/ADD ONE PARAMETER      ***
 ***************************************}
procedure TProgramSetup.Put;
var LineNo : integer;
		I : integer;

begin
	LineNo := FindCommand(Command);

	if delspaceR(Parameter) = '' then begin
		{clear}
		if LineNo<>-1 then begin
			disposeStr(FileLine[LineNo]);
			FileLine[LineNo] := nil;
			{remove, ie shuffle up?}
		end;
	end else begin
		{add/change}

		if LineNo = -1 then begin
			{no command found}
			LineNo := FindGroup;
			if LineNo = -1 then begin
				{not even group found}
				LastLine := LastLine + 2; {leave spare line}
				FileLine[LastLine] := NewStr('['+Group+']');
				LineNo := LastLine+1;
				LastLine := LineNo;
			end else begin
				{group found, no command, so make space}
				inc(LineNo); {go to past group}
				{make gap}
				for I := LastLine downto LineNo do
					FileLine[I+1] := FileLine[I];
				FileLine[LineNo] := nil; {gap made, don't dispose of below}
				inc(LastLine);
			end;
		end;

		{Set}
		if FileLine[LineNo]<>nil then disposeStr(FileLine[LineNo]);
		FileLine[LineNo] := NewStr(COmmand + ' = '+Parameter);
	end;
end;


procedure TProgramSetup.PutBoolean;
begin
	if Parameter then Put(Command, 'YES') else Put(Command,'NO');
end;


{***************************************
 ***   EXTRACT A PARAMETER           ***
 ***************************************}
function TProgramSetup.Get;
var LineNo : integer;
		S : string;

begin
	LineNo := FindCommand(Command);
	if LineNo=-1 then begin
		{nothing at all found}
		if Group<>'' then begin
			{old cfg from pre v4.1d}
			S := Group;
			Group := '';
			Get := Get(S + ' '+Command, Default);
			Group := S;
		end else
			Get := Default;
	end else begin
		S := ParameterOfLine(LineNo);
		if S = '' then
			Get := Default {revert to default}
		else
			Get := S;
	end;
end;


{=== GETPATH ==================}
{get parameter with path checking}
function TProgramSetup.GetPath;
var S : string;
begin
{$IFDEF Windows}
	S := Get(Command, Default);
{$ELSE}
	S := ChkEndSlash(Get(Command, Default));
	CheckPath(S);
	GetPath := S;
{$ENDIF}
end;

{==== GETBOOLEAN ==============}
function TProgramSetup.GetBoolean;
begin
	if Default then
		GetBoolean := delspaceR(Get(Command,'YES'))<>'NO'
	else
		GetBoolean := delspaceR(Get(Command,'NO'))<>'NO';
end;


procedure ShutDownSetup; far;
begin
	ProgramSetup.Store;
	ProgramSetup.Done;
end;

{**************************************
 ***            LOAD GLOBALS        ***
 **************************************}
procedure LoadGlobalSetup;
begin
	with ProgramSetup do begin
		TerminalNo := S2Num(Get(siTerminalNo,''));
		DeadDataMode := GetBoolean(siDeadDataMode, False);

		UserCoyID := S2Num(Get(siUserCoyID, '0')); {default new database has first coy as user's coy}
	end;
end;

{****************************************************
 ***                 GENERAL GLOBAL SETUP         ***
 ****************************************************}
{all done in the init unit}
begin
{$IFDEF fixit}	write('Initialising KamSetup unit...'); {$ENDIF}

	{==== LOAD GLOBAL SETUP =====================}
	ProgramSetup.Init; {Load from file}

	DataPath  := ProgramSetup.Get(siDataPath,'data');
	DataPath := ChkEndSlash(DataPath);
	if not ValidPath(DataPath) then begin
		ProgramError('CONFIGURATION ERROR'+#13#10
										+'Data Path not found:'+DataPath+#13#10
										{$IFDEF Windows}
											+'Check '+StrPas(ConfigFileName), hcInvalidPathMsg);
										{$ELSE}
											+'Check '+ConfigFileName, hcInvalidPathMsg);
										{$ENDIF}
		halt(1);
	end;{}
	WorkPath  := ProgramSetup.GetPath('WORK PATH',DataPath);
	FormsPath := ProgramSetup.GetPath('FORMS PATH',DataPath);
	PrintersPath := ProgramSetup.GetPath('PRINTERS PATH',DataPath);{}

	LoadGlobalSetup;

	RegisterTask(ShutDownTasks, 10, @ShutDownSetup);

{$IFDEF fixit} writeln('..done'); {$ENDIF}
end.
