{****************************************************************************
 ***                                                                      ***
 *** New Fabbo Singing Dancing OOP                                        ***
 ***                          R A L L Y !                                 ***
 ***                                                                      ***
 *** M Hill                                                      MAR 1996 ***
 ****************************************************************************}
{$I compdirs}  {Compiler directives}

{Forms:
	STARTLST - startlist hdr/ftr/frm
	CARPOS, CARLIST - reports
	CAR - car full print
	CARTIME - each timing in car print
	CONTROL - control full print
	CTRLTIME - each timing in control print}

unit KRally;

INTERFACE

uses jimmys, scodes,
			idindex,
			drivers,
			objects,JIMindxs,
			notes,
			jimprint,
			devices,
			dialogs,
			views, global, dattime, forms, files, tuiedit;

const
	{-- control types --}
	ctRoad	 		= 'RS ';
	ctServIn		= 'SI ';
	ctServOut		= 'SO ';
	ctComp 			= 'CS ';
	ctPass 			= 'PP ';
	ctLegStart 	= 'LS ';
	ctLegFinish = 'LF ';

	stRunning = $01;
	stRetired = $02;
{	stTimeBar = $04; {time barred (but running if not retired)}
	stInvalidTime = $04; 	{an invalid time/out of order time is entered}
	stDisqualified = $08;

{	hkControls = 1;{}

	hkCarTiming = 1;  {timings for car}
	hkCtrlTiming = 1; {timings for CP}

{	StartInterval = 180; {3 mins}
{	CPGap = 180; {3 mins between cars}

	opRoute = 1; {"other pointer"}

type
	PLeg = ^TLeg;
	TLeg = object(TJimmy)
		Name : string[20];
		Date : TDate;
		CPStart 	: longint;
		CPFinish	: longint;

		Comp, Road, Total : longint; {purely to catch calced fields from editbox}

		LegIdx : longint;

		constructor Init(Param : PJimmyInitParam);

		function GetName(naType : byte; Maxlen : integer) : string; virtual;
		function DisplayLine(ListForWho : longint; lstype : byte; Maxlen : integer; View : word) : string; virtual;
		procedure MakeEditBox(var EditBox : PEditBox; Caller : PView); virtual;

		{--- file ----}
		function RecSize : word; virtual;
		function srType : word; virtual;

		constructor Load(var S : TDataStream);
		procedure   StoreFields(var S : TDataSTream); virtual;

		{--- Indexing ----}
		function NumixTypes : byte; virtual;
		procedure GetIndex(const ixType : byte; var IdxRec : Plongint; var fiType : byte); virtual;
		function GetIndexKey(const ixType : byte) : string; virtual;
	end;


	PControl = ^TControl;
	TControl = object(TJimmy)
		Number 		: word;
		RoutePos 	: longint; {pointer to position}
		CType			: TSCode; {type}
		Name 			: string[30];
		Dist 			: word; {100ths km from last}
		ETT 			: TTime;  {expected time taken}
		ETALdr 		: TTime; {of leader}
{		ETDLdr 		: TTime; {of leader}
		MaxLate 	: TTime; {lateness allowance}

		TimingsHook	: longint; {hooking timings}

		CPIdx 			: longint; {index to reference CP's by Ctype/number}

		constructor Init(Param : PJimmyInitParam);
		procedure CommonInit; virtual;
		destructor Done; virtual;

		function GetName(naType : byte; Maxlen : integer) : string; virtual;
		function DisplayLine(ListForWho : longint; lstype : byte; Maxlen : integer; View : word) : string; virtual;
		procedure MakeEditBox(var EditBox : PEditBox; Caller : PView); virtual;

		function AllowDeletion : boolean; virtual; {allow deletion from history list, etc - eg invoices should not be}

		function RecSize : word; virtual;
		function srType : word; virtual;

		constructor Load(var S : TDataStream);
		procedure   StoreFields(var S : TDataSTream); virtual;

		procedure PreStoreing(const DiskJImmy : PJimmy); virtual;    {extra storing method, done by storeself}

		{Route index}
		function NumopTypes : byte; virtual;
		procedure Getop(const opType : byte; var opRec : PLongint); virtual;

		{--- Indexing ----}
		function NumixTypes : byte; virtual;
		procedure GetIndex(const ixType : byte; var IdxRec : Plongint; var fiType : byte); virtual;
		function GetIndexKey(const ixType : byte) : string; virtual;

		{--- Hooking on others -----}
		function NumhkTypes : byte; virtual;
		procedure GetHookOn(const hkType : byte; var HookRec : PLongint); virtual;

		{--- Printing ---}
		function GetPrintType(var PrintType : TJimmyPrintType; PrintAs : PSItem; PrintAsLink : pointer) : word; virtual;
		procedure GetDefaultPrintType(var PrintType : TJimmyPrintType; var PrintAs : PSItem; var PrintAsLink : pointer); virtual;
		procedure PrintFull(const Device : PDeviceStream; const PrintAs : word); virtual;
		procedure SetFormCodes(const FormCodes: PFormCodeCollection); virtual;
	end;

	PTiming = ^TTiming;
	TTiming = object(TJimmy)
		{Entry}
		CP		: longint; {control point}
		CarID 	: longint;
		TA 		: TTime; {time of arrival}
		TD 		: TTime; {time departure}

		{Calculated}
		TT 		: TTime;  {time taken since last}
		ETT 	: TTime;     {expected time taken}
		Penalty : TTime; {penalty for lateness/earliness}
		CumTotal : TTime; {penalty up to this point}
		Speed : word; {average in kph}
		Early : boolean; {marker if turned up early for road section}
		inValid : boolean; {marker if previous timing exists, and so this is
														calced ok}
		cpType : TSCode; {from control point, so that hook keys can be worked out}

		constructor Init(Param : PJimmyInitParam);
		procedure CommonInit; virtual;
		destructor Done; virtual;

		function DisplayLine(ListForWho : longint; lstype : byte; Maxlen : integer; View : word) : string; virtual;

		procedure MakeEditBox(var EditBox : PEditBox; Caller : PView); virtual;

		function RecSize : word; virtual;
		function srType : word; virtual;

		constructor Load(var S : TDataStream);
		procedure   StoreFields(var S : TDataSTream); virtual;

		{-- Hooking to others -----}
		function NumHookTo : byte; virtual;
		procedure GetHookTo(const htType : byte; var HookToID,SubHookToID : PLongint;
												var hkType : byte; var Key : longint; var InsertBias : boolean); virtual;

		{sets up pointer in timing matrix}
		procedure PreStoreing(const DiskJImmy : PJimmy); virtual;    {extra storing method, done by storeself}

		{--- Printing ---}
		procedure SetFormCodes(const FormCodes: PFormCodeCollection); virtual;

		{recalcs tt, ett, etc from previous one.  May need to be done if
		any times are changed - all subsequent timings will then need to be
		recalculated}
		procedure CalculatePenalty(Control : PControl; CarNumber : word);
		procedure Recalculate;
	end;

	PCar = ^TCar;
	TCar = object(TJimmy)
		Number 		: word;
		Entrant 	: string[30];
		Driver 		: string[30];
		DriverNat : TSCode;
		CoDriver 	: string[30];
		CoDriverNat : TScode;
		Make 			: string[30];
		Event			: TSCode; {main/historic/classic/t/etc}
		Class 		: TSCode;
		Status 		: word;
		WhenBarred : TTime; {what time it gets time barred}

		CumulTime 	: TTime;

		Notes : PFreeTextData;

		NumberIdx		: longint;
		RacePosIdx 	: longint;
		RacePosByClassIdx 	: longint;
		GeogPosIdx 	: longint;

		TimingsHook : longint;
		StartPos		: longint;
		LastCPPos		: longint; {last control point passed - route pos}

		Recalculating : boolean; {marker for when recalculating}

		constructor Init(Param : PJimmyInitParam);
		procedure CommonInit; virtual;
		destructor Done; virtual;

		function GetName(naType : byte; Maxlen : integer) : string; virtual;
		function DisplayLine(ListForWho : longint; lstype : byte; Maxlen : integer; View : word) : string; virtual;

		procedure MakeEditBox(var EditBox : PEditBox; Caller : PView); virtual;

		function RecSize : word; virtual;
		function srType : word; virtual;

		constructor Load(var S : TDataStream);
		procedure   StoreFields(var S : TDataSTream); virtual;

		{Route index}
		function NumopTypes : byte; virtual;
		procedure Getop(const opType : byte; var opRec : PLongint); virtual;

		{--- Indexing ----}
		function NumixTypes : byte; virtual;
		procedure GetIndex(const ixType : byte; var IdxRec : Plongint; var fiType : byte); virtual;
		function GetIndexKey(const ixType : byte) : string; virtual;

		{--- Hooking on others -----}
		function NumhkTypes : byte; virtual;
		procedure GetHookOn(const hkType : byte; var HookRec : PLongint); virtual;

		function HookingOn(const hkType,htType : byte; const HookingJimmy : PJimmy) : boolean; virtual;
		function UnHooking(const hkType,htType : byte; const HookingJimmy : PJimmy) : boolean; virtual;
		function ReHooking(const hkType,htType : byte; const Jimmy, OldJimmy : PJimmy) : boolean; virtual;

		procedure PreStoreing(const DiskJImmy : PJimmy); virtual;    {extra storing method, done by storeself}

		{--- Printing ---}
		function GetPrintType(var PrintType : TJimmyPrintType; PrintAs : PSItem; PrintAsLink : pointer) : word; virtual;
		procedure PrintFull(const Device : PDeviceStream; const PrintAs : word); virtual;
		procedure SetFormCodes(const FormCodes: PFormCodeCollection); virtual;

		procedure Recalculate;

		function GetLastControl : PControl;
		function GetLastTiming : PTiming;

		function IsTimeBarred(const Time : TTime) : boolean;
	end;


	PPenalty = ^TPenalty;
	TPenalty = object(TJimmy)
		CarID : longint;
		TimeGiven : TTime;
		Reason : string[30];

		Penalty : TTime;

		constructor Init(Param : PJimmyInitParam);

		function DisplayLine(ListForWho : longint; lstype : byte; Maxlen : integer; View : word) : string; virtual;

		procedure MakeEditBox(var EditBox : PEditBox; Caller : PView); virtual;

		function RecSize : word; virtual;
		function srType : word; virtual;

		constructor Load(var S : TDataStream);
		procedure   StoreFields(var S : TDataSTream); virtual;

		{-- Hooking to others -----}
		function NumHookTo : byte; virtual;
		procedure GetHookTo(const htType : byte; var HookToID,SubHookToID : PLongint;
												var hkType : byte; var Key : longint; var InsertBias : boolean); virtual;
	end;



IMPLEMENTATION

uses
			printers,
			messtext,
			tuilist,
			country, {country scodes}
			tui,
			chains,
			tables,
			krlysetu,
			krlyview,
			krlyrpts,
			status,
			newsfl,
			inpdnt,
			indexes,
			inpjimmy,
			{$IFDEF kwplink}
			kwplink,
			{$ENDIF}
			kamsetup,
			tuijimmy,
			jimhooks,
			tasks,
			menus,
			linklist,
			app,
			tuimsgs,
			minilib;



{**************************************************************
 ***                                                        ***
 ***                        LEG                             ***
 ***                                                        ***
 **************************************************************}


constructor TLeg.Init(Param : PJimmyInitParam);
begin
	inherited Init;
	Date.SetToToday;
	CPStart 	:= -1;
	CPFinish 	:= -1;
	LegIdx 		:= -1;
end;

function TLeg.DisplayLine(ListForWho : longint; lstype : byte; Maxlen : integer; View : word) : string;
var S : string;
begin
	S := Name+' '+Copy(Date.Text(daFull),1,8)+' '+GetJimmyIDName(CPStart, naRef,0)+' to '+GetJimmyIDName(CPFinish, naRef,0);
	{$IFDEF fixit} S := S+#13+space(9)+'Idx'+N2Str(LegIdx); {$ENDIF}
	DisplayLine := S;
end;

function TLeg.GetName;
begin
	GetName := Name;
end;

const
	svLS = 1;
	svLF = 2;
	tvCompDist = 1;
	tvRoadDist = 2;
	tvTotalDist = 3;

{calculates distance for leg}
procedure LinkDist(const Linker : PInputLinker; const CallingView : PView); far;
var Control, LS,LF : PControl;
		RoutePos, ControlID : longint;
		Comp, Road : longint;
		RouteMax : longint;

begin
	LS := PControl(PInputJimmy(Linker^.SourceView[svLS])^.GetJimmy);
	LF := PControl(PInputJimmy(Linker^.SourceView[svLF])^.GetJimmy);
	Comp := 0; Road := 0;

	if (LS<>nil) and (LF<>nil) then begin

		ThinkingOn('Calculating distances');

		{run through from LS start to LF...}
		RoutePos := LS^.RoutePos+1;
		RouteMax := GetNewID(srControl)-1;
		while (RoutePos<=LF^.RoutePos) and (RoutePos<=RouteMax) do begin
			FileAdmin(fiRouteIDIdx)^.LogOn;
			ControlID := IDStream(fiRouteIDIdx)^.GetIDPtr(RoutePos);
			FileAdmin(fiRouteIDIdx)^.LogOff;
			Control := PControl(GetJimmy(ControlID));

			if Control^.CType=ctComp then
				Comp := Comp + Control^.Dist
			else
				Road := Road + Control^.Dist;

			dispose(Control, done);
			inc(RoutePos);
		end;
		ThinkingOff;
	end;{}

	Linker^.TargetView[tvCompDist]^.SetData(Comp);
	Linker^.TargetView[tvCompDist]^.DrawView;{}
	Linker^.TargetView[tvRoadDist]^.SetData(Road);
	Linker^.TargetView[tvRoadDist]^.DrawView;

	Road := Road + Comp;
	Linker^.TargetView[tvTotalDist]^.SetData(Road);
	Linker^.TargetView[tvTotalDist]^.DrawView;{}
end;

procedure TLeg.MakeEditBox(var EditBox : PEditBox; Caller : PView);
var R : TRect;
		DistLinker :PInputLinker;
begin
	R.Assign(0, 0, 31, 13); {Size of box}
	CentreOnView(R, Caller);
	EditBox := New(PJimmyEditBox, init(R, 'Leg Entry',Caller, @Self));

	New(DistLinker, init(@LinkDist, EditBox));
	DistLinker^.ForceInitLink := True;{}

	EditBox^.Options := EditBox^.Options or ofCenterX;
	with EditBox^ do begin
		{--- Set up box interior ---}
		{ X, Y, Boxlen,  FieldLen,     Title}
		Insert(New(PSkipBytes, init(sizeof(TJimmy))));

		InsTitledField( 8, 1, 20, 1, '~N~ame', New(PInputELine, init(R, 20)));

		InsTitledField( 8, 2, 10, 1, '~D~ate', New(PinputDate, init(R)));

		InsTitledField( 8, 3, 20, 1, '~S~tart', New(PinputCP, init(R,20,'LS')));
		DistLinker^.SetSourceView(Current, svLS);
		InsTitledField( 8, 4, 20, 1, '~F~inish', New(PinputCP, init(R,20,'LF')));
		DistLinker^.SetSourceView(Current, svLF);

		InsTitledField( 8, 6,  6, 1, 'Comp', 	New(PInputLint, init(R,6)));
		PInputNum(Current)^.DecPlaces := 2;
		DistLinker^.SetTargetView(Current, tvCompDist);
		Current^.SetState(sfDisabled, True);{}

		InsTitledField( 8, 7,  6, 1, 'Road', 	New(PInputLint, init(R,6)));
		PInputNum(Current)^.DecPlaces := 2;
		DistLinker^.SetTargetView(Current, tvRoadDist);
		Current^.SetState(sfDisabled, True);{}

		InsTitledField( 8, 8,  6, 1, 'Total', 	New(PInputLint, init(R,6)));
		PInputNum(Current)^.DecPlaces := 2;
		DistLinker^.SetTargetView(Current, tvTotalDist);
		Current^.SetState(sfDisabled, True);{}

		Insert(New(PJimmyOKButton, init(5,Size.Y-3, @Self)));
		Insert(New(PJimmyCancelButton, init(16,Size.Y-3, @Self)));

		EndInit;
	end;

	{$IFDEF RallyPress}
		{DisableViews;{}
	{$ENDIF}
end;

{*******************************
 ***      FILE               ***
 *******************************}
const
	 {--- Required for Stream ----}
	 RLeg : TStreamRec = (
		 ObjType : srLeg;
		 VmtLink : Ofs(TypeOf(TLeg)^);
		 Load : @TLeg.Load;
		 Store : @TLeg.Store
	 );

	TLegIdxSize = 40;


function TLeg.RecSize : word;
begin RecSize := 100; end;

function TLeg.srType : word;
begin srType := srLeg; end;

constructor TLeg.Load(var S : TDataStream);
var Ver : byte;

begin
	S.Read(Ver, 1);

	case Ver of
		1 : begin
			inherited Load(S);

			Name := S.ReadStr;
			Date.Load(S);

			S.Read(CPStart, 4);
			S.Read(CPFinish, 4);
		end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not recognised'#13#10'TLeg.Load',mfError,hcNoContext);
		fail;
	end; {}
end; {proc}


procedure   TLeg.StoreFields(var S : TDataSTream);
var	Ver : byte;

begin
	Ver := 1; S.Write(Ver, 1);

	inherited StoreFields(S);

	S.WriteStr(@Name);
	Date.Store(S);

	S.Write(CPStart, 4);
	S.Write(CPFinish, 4);
end;


{============= INDEXING/HOOKING ========================}
function TLeg.NumixTypes;
begin Numixtypes := 1; end;

procedure TLeg.GetIndex;
begin
	inherited GetIndex(ixType, Idxrec, fiType);
	case ixType of
		1 : begin
			IdxRec := @LegIdx;
			fiType := fiLegIdx;
		end;
	end;
end;

function TLeg.GetIndexKey;
begin
	GetIndexKey := '';
	case ixType of
		1 : if not Deleted then GetIndexKey := {Date.AsKey +{} Name;
	end;
end;


{**************************************************************
 ***                                                        ***
 ***                       CONTROL                          ***
 ***                                                        ***
 **************************************************************}
constructor TControl.Init(Param : PJimmyInitParam);
begin
	inherited Init;

	ETT.Clear;
	ETALdr.Clear;
	MaxLate.Clear;
end;

procedure TControl.CommonInit;
begin
	inherited CommonInit;
	FileAdmin(fiRouteIDIdx)^.LogOn;
end;

destructor TControl.Done;
begin
	FileAdmin(fiRouteIDIdx)^.LogOff;
	inherited Done;
end;

function TControl.DisplayLine(ListForWho : longint; lstype : byte; Maxlen : integer; View : word) : string;
var S : string;
begin
	S := GetName(naRef,0) + Tab+N2Str(RoutePos)+Tab+Name;
	if Dist=0				then S := S + Tab else
		S := S + Tab+PadSpaceL(R2Str(Dist/100,dcFixed+2,0),6);
	if ETT.Blank 		then S := S + Tab else S := S + Tab+ETT.Digit5;
	if ETALdr.Blank then S := S + Tab else S := S + Tab+ETALdr.Digit5;
	if not MaxLate.Blank then S := S+ Tab+MaxLate.Digit5;
	{$IFDEF fixit} S := S+#13+space(9)+'Idx'+N2Str(CPIdx)+' Tmgs'+N2Str(TimingsHook); {$ENDIF}
	DisplayLine := S;
end;


function TControl.GetName;
var S : string;
begin
	S := delspaceR(CType)+PadZero(N2Str(Number),2);
	if naType<>naRef then S := S + ' '+Name;
	GetName := S;
end;



{****************************************
 ***         EDIT CONTROL             ***
 ****************************************}
type
	PInputCtrlNumber = ^TInputCtrlNumber;
	TInputCtrlNumber = object(TInputWord)
		function Valid(Command : word) : boolean; virtual;
	end;

	function TInputCtrlNumber.Valid;
	var Num : word;
			I : integer;
			V : boolean;
			IdxRec, HoleRec, ID : longint;
	begin
		V := inherited Valid(Command);

		if V and DoValidFor(Command) then begin

			{check if number given already entered - and not self's number}
			GetData(Num);
			if (Num>=1) and (Num<>PControl(PJimmyEditBox(Owner)^.Jimmy)^.Number) then begin
				FileAdmin(fiControlIdx)^.LogOn;
				I := IndexStream(fiControlIdx)^.FindFirst(PadZero(N2Str(Num),3), IdxRec, HoleRec, ID);
				if I > -1 then begin
					V := False;
					InputWarning('Control Number '+N2Str(Num)+' already entered',hcNoContext);
				end;
				FileAdmin(fiControlIdx)^.LogOff;
			end;
		end;

		Valid := V;
	end;

{--- ETA/ETT Linker ---------}
{Looks up previous control and adds ett to get eta, or v.v. if ett blank}
{this is no good - can't do it because we don't have an etd of the previous
control.  Not worth bothering with}

{const
	svETT = 1;
	svETA = 2;

procedure LinkETAETT(const Linker : PInputLinker; const CallingView : PView); far;
var PrevControl : PControl;
		PrevControlID : longint;
		ETT, ETA : TTime;
		RoutePos : longint;

begin
	Linker^.SourceView[svETT]^.GetData(ETT);
	Linker^.SourceView[svETA]^.GetData(ETA);

	RoutePos := PControl(PJimmyEditBox(CallingView^.Owner)^.Jimmy)^.RoutePos;
	if RoutePos = -1 then RoutePos := GetNewID(srControl)-1 else dec(RoutePos);

	PrevControlID := GetIDPtr(srControl, RoutePos);
	PrevControl := PControl(GetJimmy(PrevControlID));

	if PrevControl<>nil then begin
		if CallingView = Linker^.SourceView[svETT] then begin
			ETA.SetTo(PrevControl^.{}



procedure TControl.MakeEditBox(var EditBox : PEditBox; Caller : PView);
var R : TRect;
		F : PView;
begin
	R.Assign(0, 0, 76, 14); {Size of box}
	CentreOnView(R, Caller);
	EditBox := New(PJimmyEditBox, init(R, 'Control Entry',Caller, @Self));

	with EditBox^ do begin

		{--- Set up box interior ---}
		{ X, Y, Boxlen,  FieldLen,     Title}
		Insert(New(PSkipBytes, init(sizeof(TJimmy))));

		R.XYLD(11,1,5,1);		Insert(New(PInputCtrlNumber, init(R, 3))); 	AddLabel('N~u~mber', Current);

		R.XYLD(21,1, 5, 1); Insert(New(PInputLint, init(R, 5))); 				AddLabel('Pos', Current);{}
		{$IFNDEF fixit}
		Current^.SetState(sfDisabled, true);
		{$ENDIF}

		R.XYLD(11,2, 5, 1); Insert(New(PInputSCode, init(R, scCPType))); AddLabel('T~y~pe', Current);

		R.XYLD(11,3,20, 1); Insert(New(PInputELine, init(R, 30))); 			AddLabel('~N~ame', Current);

		InstitledField(11, 5,  6, 1, '~D~ist/km', New(PinputWord, init(R, 6)));
		PInputWord(Current)^.DecPlaces := 2;

		InsTitledField(11, 7,  5, 1, 'ET ~T~aken',		New(PinputTime, init(R, itHM or itZeroBlank)));

		InsTitledField(11, 8,  5, 1, 'Ldr ~E~TA',		New(PinputTime, init(R, itHM or itZeroBlank or itTime)));
{		InsTitledField(10, 9,  5, 1, 'ETD',		New(PinputTime, init(R, itHM or itZeroBlank)));{}
		InsTitledField(11, 9,  5, 1, '~L~ateness',		New(PinputTime, init(R, itHM or itZeroBlank)));

		F := InsTitledField(35, 1, 38,12, '~T~imings',
														New(PTimingListView,	Init(R, lsControlTiming, 0, hkCtrlTiming, @Self, PJimmyEditBox(EditBox))));{}
		Insert(PTimingListVIew(F)^.VScrollBar);

		Insert(New(PJimmyOKButton, init(10,Size.Y-3, @Self)));
		Insert(New(PJimmyCancelButton, init(20,Size.Y-3, @Self)));

{$IFDEF fixit}
		InsTitledField(10,14,  6, 1, 'Idx', New(PinputLint, init(R,6)));
		InsTitledField(30,14,  6, 1, 'Timings', New(PinputLint, init(R,6)));
{$ENDIF}

		EndInit;

		if RecNo<>-1 then F^.FOcus;
	end;

	{$IFDEF RallyPress}
{		DisableViews;{}
	{$ENDIF}

end;

{*******************************
 ***      FILE               ***
 *******************************}
const
	 {--- Required for Stream ----}
	 RControl : TStreamRec = (
		 ObjType : srControl;
		 VmtLink : Ofs(TypeOf(TControl)^);
		 Load : @TControl.Load;
		 Store : @TControl.Store
	 );

	TControlIdxSize = 40;


function TControl.RecSize : word;
begin RecSize := 100; end;

function TControl.srType : word;
begin srType := srControl; end;

constructor TControl.Load(var S : TDataStream);
var B,Ver : byte;

begin
	S.Read(Ver, 1);

	case Ver of
		1 : begin
			inherited Load(S);

			S.Read(Number, 2);
			S.Read(CType, 4);
			Name := S.ReadStr;

			S.Read(Dist, 2);
			ETT.Load(S);
			ETALdr.Load(S);
{			ETDLdr.Load(S);{not used no more}  			MaxLate.Load(S);
			MaxLate.Load(S);

{			S.Read(RoutePos, 4);{}
		end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not recognised'#13#10'TControl.Load',mfError,hcNoContext);
		fail;
	end; {}
end; {proc}


procedure   TControl.StoreFields(var S : TDataSTream);
var	Ver : byte;
		IDindexFIle : TIDIndexFIle;

begin
	Ver := 1; S.Write(Ver, 1);

	inherited StoreFields(S);

	S.Write(Number, 2);
	S.Write(CType, 4);
	S.WriteStr(@Name);

	S.Write(Dist, 2);
	ETT.Store(S);
	ETALdr.Store(S);
{	ETDLdr.Store(S); {not used no more} ETALdr.Store(S);
	MaxLate.Store(S);

{	S.Write(RoutePos, 4);{}
end;

function TControl.AllowDeletion : boolean;
begin
	AllowDeletion :=  TimingsHook = -1; {allow only if no timings attached}
end;


procedure TControl.Prestoreing;
begin
	inherited PreStoreing(DiskJimmy);

	if Deleted then begin
		if RoutePos<>-1 then begin
			IDStream(fiRouteIDIdx)^.Delete(RoutePos); {actually moves to end, rather than deleting}
		end;
	end else begin
		if RoutePos=-1 then begin
			{add to end}
			RoutePos := GetNewID(srType);
			IDStream(fiRouteIDIdx)^.SetIDPtr(RoutePos, RecNo); {recno is set before put}
		end;
	end;

	Storeop(opRoute); {store pointer}
end;

{============= ROUTE PTR ===============================}
function TControl.NumopTypes;
begin NumopTypes := 1; end;

procedure TControl.Getop;
begin
	case opType of
		1 : opRec := @RoutePos;
	else
		opRec := nil;
	end;
end;


{============= INDEXING/HOOKING ========================}
function TControl.NumixTypes;
begin Numixtypes := 1; end;

procedure TControl.GetIndex;
begin
	inherited GetIndex(ixType, Idxrec, fiType);
	case ixType of
		1 : begin
			IdxRec := @CPIdx;
			fiType := fiControlIdx;
		end;
	end;
end;

function TControl.GetIndexKey;
begin
	GetIndexKey := '';
	case ixType of
		1 : if not Deleted then GetIndexKey := PadZero(N2Str(Number),3);
	end;
end;

function TControl.NumhkTypes;
begin NumhkTypes := 4; end;

procedure TControl.GetHookOn;
begin
	inherited GetHookOn(hkType, HookRec);

	case hkType of
		hkCtrlTiming : HookRec := @TimingsHook;
	end;
end;

{*****************************************
 ***          PRINTING                 ***
 *****************************************}
{procedure LinkControlForm(const Linker : PInputLinker; const CallingView : PView); far;
var PrintAs : word;
		FormName : string;
begin
	if CallingView = Linker^.SourceView[1] then begin
		CallingView^.GetData(PrintAs);

		case PrintAs of
			0 : begin FormName := 'CONTROL'; end;
			1 : begin FOrmName := 'CONTROLO'; end;
			2 : begin FormName := 'CONTROLD'; end;
		end;

		Linker^.TargetView[1]^.SetData(FOrmName);
		Linker^.TargetView[1]^.DrawView;
	end;
end;{}

procedure TControl.GetDefaultPrintType
										(var PrintType : TJimmyPrintType; var PrintAs : PSItem; var PrintAsLink : pointer);
begin
	inherited GetDefaultPrintType(PrintType, PrintAs, PrintAsLink);
	PrintAs := 	NewSItem('~S~ection Times',
							NewSItem('O~v~erall Times',
							newSitem('~D~eparture Times',nil)));
{	PrintAsLink := @LinkControlForm;{}
end;

function TControl.GetPrintType(var PrintType : TJimmyPrintType; PrintAs : PSItem; PrintAsLink : pointer) : word;
begin
	GetPrintType := inherited GetPrintType(PrintType, PrintAs, PrintAsLink);
	PrintType.FormName := ''; {so that it does a fullprint not a formprint}
end;

const
	stSec = 0;
	stCum = 1;
	stDep = 2;

type
	PTimingCollection = ^TTimingCollection;
	TTimingCollection = object(TSortedCollection)
		SortType : word;
		constructor Init(NSortType : word);
		function Compare(Key1, Key2: Pointer): Integer; virtual;
	end;

	constructor TTimingCollection.Init;
	begin
		inherited Init(10,10);
		SortType := NSortType;
		Duplicates := True;
	end;

	function TTimingCollection.Compare;
	var K1,K2 : longint;
			Car : PCar;
	begin
		case SortType of
			stCum : begin
				if PTiming(Key1)^.Invalid then K1 := 999999 else	K1 := PTiming(Key1)^.CumTotal.Secs;
				if PTiming(Key2)^.Invalid then K2 := 999999 else	K2 := PTiming(Key2)^.CumTotal.Secs;
			end;
			stDep : begin
				K1 := PTiming(Key1)^.TD.Secs;
				K2 := PTiming(Key2)^.TD.Secs;
			end;
		else
			if PTiming(Key1)^.Penalty.Blank and PTiming(Key2)^.Penalty.Blank then begin
				K1 := PTiming(Key1)^.TA.Secs;
				K2 := PTiming(Key2)^.TA.Secs;
			end else begin
				K1 := PTiming(Key1)^.Penalty.Secs;
				K2 := PTiming(Key2)^.Penalty.Secs;
			end;
			if PTiming(Key1)^.Invalid then K1 := 999999;
			if PTiming(Key2)^.Invalid then K2 := 999999;
		end;

		{$IFDEF RallyPress}
			{separate by event}
{			Car := PCar(GetJimmy(PTiming(Key1)^.CarID));
			K1 := K1 + S2Num(Car^.Event) shl 24;
			dispose(Car, done);
			Car := PCar(GetJimmy(PTiming(Key2)^.CarID));
			K2 := K2 + S2Num(Car^.Event) shl 24;
			dispose(Car, done);{}
		{$ENDIF}

		if K1<K2 then Compare := -1
			else if K1 = K2 then Compare := 0
				else Compare := 1;
	end;

procedure TControl.PrintFull(const Device : PDeviceStream; const PrintAs : word);
var
	TimingCollection : PTimingCollection;
	Fastest : TTime;
	Pos : word;
	LastEvent : string;

	procedure LoadTiming(Hook : PHook); far;
	var Jimmy : PJimmy;
			Time : TTime;
			Car : PCar;
			V : boolean;
	begin
		Jimmy := GetJimmy(Hook^.JimmyID);
		if (Jimmy<>nil) {and not PTiming(Jimmy)^.Invalid} then begin

			{check if valid for car}
			Car := PCar(GetJimmy(PTiming(Jimmy)^.CarID));

			if (Car^.Status and stInvalidTime)<>0 then PTiming(Jimmy)^.Invalid := True;
			dispose(Car, done);

{		if (PrintAs<>stCum) or (not PTiming(Jimmy)^.InValid) then begin{}
				TimingCollection^.Insert(Jimmy);

				{make up fastest times}
				case PrintAs of
					stCum : Time.SetTo(PTiming(Jimmy)^.CumTotal);
				else
					Time.SetTo(PTiming(Jimmy)^.Penalty);
				end;

				if Fastest.Blank then
					Fastest.SetTo(Time)
				else
					if Time.Secs<Fastest.Secs then Fastest.SetTo(Time);
{			end;{}
{			dispose(Jimmy, done); dont' dispose - has been inserted!}
		end;
	end;


	procedure PrintTIming(Timing : PTiming); far;
	var Car : PCar;
	begin
		inc(Pos);

		{$IFDEF RallyPress}
			{separate main/classic}
{			Car := PCar(GetJimmy(Timing^.CarID));
			if (Car^.Event<>LastEvent) then begin
				if LastEvent<>'*START*' then begin
					Pos := 1;
					Device^.Writeln('');
					Device^.writeln('-----'+ExpandSCode(scCarEvent, Car^.Event)+'-----');
				end;
				LastEvent := Car^.Event;
			end;
			dispose(Car, done);{}
		{$ENDIF}

		Device^.FormCodes^.SetStr('POS',N2Str(Pos));
		case PrintAs of
			stCum : Timing^.PrintForm(Device, 'CTROTIME.FRM');
			stDep : Timing^.PrintForm(Device, 'CTRDTIME.FRM');
		else
			Timing^.PrintForm(Device, 'CTRLTIME.FRM');
		end;

		{botch fix for safari rally - every 25, do a new page with hdr, etc}
{$IFDEF kwplink}
		if (Device=PDeviceStream(WPStream)) and ((Pos mod 20) = 0) then Device^.NewPage;
{$ENDIF}
	end;


begin
	SetFormCodes(Device^.FormCodes);
	LastEvent := '*START*';

	case PrintAs of
		stCum : DEvice^.StartPrint('CONTROLO','REPORT');
		stDep : DEvice^.StartPrint('CONTROLD','REPORT');
	else
		DEvice^.StartPrint('CONTROL','REPORT');
	end;

	if not Device^.FormFound then begin
		ProgramWarning('No form for printing control'#13#10
										+'Create CONTROL.HDR/FTR and CTRLTIME.FRM in Maintenance',hcNoContext);
		Device^.EndPrint;
		exit;
	end;

	New(TimingCollection, init(PrintAs));
	TimingCollection^.Duplicates := True;
	Fastest.Clear;

	{load timings}
	ThinkingOn('Loading Timings');
	FileAdmin(fiHooks)^.LogOn;
	HookFile^.ForEach(TimingsHook, @LoadTiming);
	FileAdmin(fiHooks)^.LogOff;
	ThinkingOff;

	{print timings}
	Device^.FormCodes^.SetStr('FASTEST',Fastest.Digit8);
	Device^.FormCodes^.SetStr('CUMTEST',Fastest.Digit8);
	Pos := 0;
	TimingCollection^.ForEach(@PrintTiming);

	dispose(TimingCollection, done);

	Device^.EndPrint;
end;

{type
	{special re-sort depending on control point type}
{	PTimingsFormCode = ^TTImingsFormCode;
	TTimingsFormCode = object(THookListFormCode)
		procedure DoSpecial(var Tree : PTree; Param : TFCodeStr); virtual;
	end;

	procedure TTimingsFormCode.DoSpecial;

		function CumKeyFunc(Hook : PHook) : longint; far;
		var Timing : PTiming;
		begin
			Timing := PTiming(GetJimmy(Hook^.JimmyID));
			if Timing^.Invalid then
				CumKeyFunc := 99999
			else
				CumKeyFunc := Timing^.CumTotal.Secs;
			dispose(Timing, done);
		end;

		function SecKeyFunc(Hook : PHook) : longint; far;
		var Timing : PTiming;
		begin
			Timing := PTiming(GetJimmy(Hook^.JimmyID));
			if Timing^.Invalid then
				SecKeyFunc := 99999
			else
				SecKeyFunc := Timing^.Penalty.Secs;
			dispose(Timing, done);
		end;

		function DepKeyFunc(Hook : PHook) : longint; far;
		var Timing : PTiming;
		begin
			Timing := PTiming(GetJimmy(Hook^.JimmyID));
			DepKeyFunc := Timing^.TD.Secs;
			dispose(Timing, done);
		end;

	begin
		ThinkingOn('Sorting...');
		if Code^='CTIMINGS' then begin
			{in order of cum total}
{			Tree^.ReOrder(@CumKeyFunc)
		end else
			if Code^='DTIMINGS' then begin
				Tree^.ReOrder(@DepKeyFunc)
			end else begin
				Tree^.ReOrder(@SecKeyFunc);
				if PControl(Info)^.CType<>ctCOmp then {competitive stuff already in right order}
{					Tree^.ReverseOrder; {ie latest last - ie departure order}
{			end;
		ThinkingOff;
	end;{}


procedure TControl.SetFormCodes(const FormCodes: PFormCodeCollection);
var Timing : PTiming;
		CumPen : TTime;
begin
	with FormCodes^ do begin
		SetStr('NO', N2Str(Number));
		SetStr('RPOS', N2Str(RoutePos));
		SetStr('TYPE', delspace(CType));
		SetStr('REF', GetName(naRef,0));
		SetStr('NAME', Name);
		SetStr('DIST', R2Str(Dist/100, dcFixed+2,0));
		SetStr('ETT', ETT.Digit5);
		SetStr('ETALDR', ETALdr.Digit5);
		SetStr('MAXLATE', MaxLate.Digit5);

{		Insert(New(PTimingsFormCode, init('TIMINGS', GetJimmy(RecNo), hkCtrlTiming, 'CTRLTIME.FRM',0)));
		Insert(New(PTimingsFormCode, init('CTIMINGS', GetJimmy(RecNo), hkCtrlTiming, 'CTROTIME.FRM',0)));
		Insert(New(PTimingsFormCode, init('DTIMINGS', GetJimmy(RecNo), hkCtrlTiming, 'CTRDTIME.FRM',0)));

		{get time taken of fastest car - first in timings list}
{		SetStr('FASTEST', '');
		SetStr('CUMTEST', '');
		FileAdmin(fiHooks)^.LogOn;
		Timing := PTiming(HookFile^.GetFirst(TimingsHook, srTiming));
		if Timing<>nil then begin
			Insert(New(PJimmyFormCode, init('FASTEST', Timing^.RecNo)));
			{find car with fastest cumulative timing}
{			CumPen.SetToStr('23:59:59');
			while Timing<>nil do begin
				if Timing^.CumTotal.Secs<CumPen.Secs then begin
					Insert(New(PJimmyFormCode, init('CUMTEST', Timing^.RecNo)));
					CumPen.SetTo(Timing^.CumTotal);
				end;
				dispose(Timing, done);
				Timing := PTiming(HookFile^.GetNextJimmy);
			end;
			if Timing<>nil then dispose(Timing, done);
		end;

		FileAdmin(fiHooks)^.LogOff;

	end;{}
	end;
end;



{**************************************************************
 ***                                                        ***
 ***                       CAR                              ***
 ***                                                        ***
 **************************************************************}
constructor TCar.Init(Param : PJimmyInitParam);
begin
	inherited Init;

	CumulTime.Clear;
	WhenBarred.Clear;
	Status := stRunning;
	StartPos := 0;
	DriverNat := ProgramSetup.Get(siCountry,'');
	CoDriverNat := ProgramSetup.Get(siCountry,'');
end;

procedure TCar.CommonInit;
begin
	inherited CommonInit;
	New(Notes, init);
	Recalculating := False;
	FileAdmin(fiRouteIDIdx)^.LogOn;
end;

destructor TCar.Done;
begin
	FileAdmin(fiRouteIDIdx)^.LogOff;
	Dispose(Notes, done);
	inherited Done;
end;

function TCar.DisplayLine(ListForWho : longint; lstype : byte; Maxlen : integer; View : word) : string;
var S,SS,SSS : string;
		Control : PControl;

		function DoStatus : string;
		var S : string;
		begin
			S := '';
			if Status and stRetired >0 then S := S + 'R/';
			if Status and stDisqualified >0 then S := S +'D/';
			if S = '' then begin
				{these ones don't matter if above has happened}
				if (Status and stInvalidTime >0) then S := S + 'IT/';
				{$IFNDEF RallyPress}
					if IsTimeBarred(TimeNow) then S := S +'TB/';
				{$ENDIF}
			end;
			if S<>'' then
				DoStatus := Copy(S,1,length(S)-1)
			else
				DoStatus := '';
		end;

begin
	if (lsType = lsCar) or (lsType=lsStartList) then begin
		{full details}
		if lsType=lsStartList then
			S := PadSpaceR(N2Str(StartPos),3)+Tab
		else
			S := '';
		S := S+GetName(naRef,0)+Tab;
		{build drivers/nationality}
		SS := Driver;
		if Maxlen>45 then begin
			if DriverNat<>'' then SS := SS+' ('+delspace(DriverNat)+')';
			SS := SS + ', '+CoDriver;
			if CoDriverNat<>'' then SS := SS+' ('+delspace(CoDriverNat)+')';
		end;
		S := S + SS + Tab
					 + Setlength(Class,3)+Event[1] + ' '
					 + DoStatus;

	end else begin
		{race details}
		S := N2Str(GetidxPtr(Gotbyix)+1)+Tab{position?}
				+GetName(naRef,0)+Tab
				+Driver;
		if Maxlen>50 then S := S +', '+CoDriver;
		S := S
				+Tab+Setlength(Class,3)+Event[1]+' '
				+CumulTime.Digit8+Tab
				+DoStatus;
	end;

	Control := GetLastControl;
	if Control<>nil then begin
		S := S +' @'+Control^.GetName(naRef, 0);
		dispose(Control, done);
	end else
		if lsType<>lsStartList then
			if StartPos=-1 then
				S := S + ' Non Starter'
			else
				S := S +' @St'+N2Str(StartPos);

	{$IFDEF fixit} S := S+#13+space(9)+'Idx Nu'+N2Str(NumberIdx)
											+' RP'+N2Str(RacePosIdx)
											+'; Tmg'+N2Str(TimingsHook)
											+'; LCP'+N2Str(LastCPPos);
	{$ENDIF}

	DisplayLine := S;
end;



function TCar.GetName;
var S : string;
begin
	S := PadZero(N2Str(number),RallySetup.CarNumDig);
	if natype<>naRef then S := S + ' '+Driver;
	GetName := S;
end;


{***************************************
 ***         EDIT BOX                ***
 ***************************************}
type
	PRecalcButton = ^TRecalcButton;
	TRecalcButton = object(TOurButton)
		procedure Press; virtual;
	end;

	procedure TRecalcButton.Press;
	begin
		inherited Press;

		PCar(DataItem)^.Recalculate;
		PCar(DataItem)^.StoreSelf;
	end;

type
	PInputCarNumber = ^TInputCarNumber;
	TInputCarNumber = object(TInputWord)
		function Valid(Command : word) : boolean; virtual;
	end;

	function TInputCarNumber.Valid;
	var Num : Word;
			I : integer;
			V : boolean;
			IdxRec, HoleRec, ID : longint;
	begin
		V := inherited Valid(Command);

		if V and DoValidFor(Command) then begin

			{check if number given already entered}
			GetData(Num);
			if (Num>=1) and (Num<>PCar(PJimmyEditBox(Owner)^.Jimmy)^.Number)  then begin
				FileAdmin(fiCarNumberIdx)^.LogOn;
				I := IndexStream(fiCarNumberIdx)^.FindFirst(PadZero(N2Str(Num),3), IdxRec, HoleRec, ID);
				if I >-1 then begin
					V := False;
					InputWarning('Car Number '+N2Str(Num)+' already entered',hcNoContext);
				end;
				FileAdmin(fiCarNumberIdx)^.LogOff;
			end;
		end;

		Valid := V;
	end;

{Copies entrant to driver when entering}
procedure LinkDriver(const Linker : PInputLinker; const CallingView : PView); far;
begin
	PInputELine(Linker^.TargetView[1])^.Data^ := PInputELine(Linker^.SourceView[1])^.Data^;
	PInputELine(Linker^.TargetView[1])^.Draw;
end;


procedure TCar.MakeEditBox(var EditBox : PEditBox; Caller : PView);
var R : TRect;
		DriverLinker : PInputLinker;
		F : PView;
begin
	if not Notes^.loaded then Notes^.LoadText;

	R.Assign(0, 0, 75,19); {Size of box}
	CentreOnView(R, Caller);
	{$IFDEF RallyPress}
	EditBox := New(PUpdaterRallyBox, init(R, 'Car Entry',Caller, @Self));
	{$ELSE}
	EditBox := New(PJimmyEditBox, init(R, 'Car Entry',Caller, @Self));
	{$ENDIF}

	New(DriverLinker, init(@LinkDriver, EditBox));

	with EditBox^ do begin

		{--- Set up box interior ---}
		{ X, Y, Boxlen,  FieldLen,     Title}
	{$IFDEF fixit}
		GrowTo(Size.X, Size.Y+3); {Add an extra line}

		Insert(New(PSkipBytes, init(Sizeof(TObject))));

		{data item stuff}
		R.XYLD(5, Size.Y-4, 6, 1); Insert(New(PInputLint, init(R,8))); AddLabel('Rec',Current);
		R.Move(10,0);							 Insert(New(PInputBYte, init(R,3))); AddLabel('Ter',Current);
		R.Move(10,0);							 Insert(New(PInputBYte, init(R,3))); AddLabel('Cou',Current);

		{jimmy stuff}
		R.Move(10,0);							 Insert(New(PInputBYte, init(R,3))); AddLabel('ix',Current);
		R.Move(10,0);							 Insert(New(PInputBoolean, init(R))); AddLabel('Del',Current);
		R.Move(10,0);							 Insert(New(PInputBoolean, init(R))); AddLabel('Tag',Current);
		Insert(New(PSkipBytes, init(sizeof(TTimer)))); {skip insert-ttime timer}
	{$ELSE}
		Insert(New(PSkipBytes, init(sizeof(TJimmy))));
	{$ENDIF}

		InsTitledField(10, 1,  3, 1, 'N~u~mber', New(PInputCarNumber, init(R, 3)));
		{$IFDEF RallyPress} Current^.SetState(sfDisabled, True);		{$ENDIF}

		InsTitledField(10, 2, 17, 1, 'E~n~trant', New(PInputELine, init(R, 30)));
		if RecNo=-1 then DriverLinker^.SetSourceView(Current, 1);
		{$IFDEF RallyPress} Current^.SetState(sfDisabled, True);		{$ENDIF}
		InsTitledField(10, 3, 17, 1, '~D~river', New(PInputELine, init(R, 30)));
		DriverLinker^.SetTargetView(Current, 1);
		{$IFDEF RallyPress} Current^.SetState(sfDisabled, True);		{$ENDIF}
		InstitledField(30, 3,  3, 1, '', New(PInputSCode, init(R, scCountries)));
		{$IFDEF RallyPress} Current^.SetState(sfDisabled, True);		{$ENDIF}
		InsTitledField(10, 4, 17, 1, 'Codriver', New(PInputELine, init(R, 30)));
		{$IFDEF RallyPress} Current^.SetState(sfDisabled, True);		{$ENDIF}
		InstitledField(30, 4,  3, 1, '', New(PInputSCode, init(R, scCountries)));
		{$IFDEF RallyPress} Current^.SetState(sfDisabled, True);		{$ENDIF}
		InsTitledField(10, 5, 17, 1, '~M~ake', New(PInputELine, init(R, 30)));
		{$IFDEF RallyPress} Current^.SetState(sfDisabled, True);		{$ENDIF}
		InsTitledField(10, 6, 17, 1, '~E~vent', New(PInputSCode, init(R, scCarEvent)));
		{$IFDEF RallyPress} Current^.SetState(sfDisabled, True);		{$ENDIF}
		InsTitledField(10, 7, 23, 1, 'Cl~a~ss', New(PInputSCode, init(R, scCarClass)));
		{$IFDEF RallyPress} Current^.SetState(sfDisabled, True);		{$ENDIF}

		F := InsTitledField(10, 9, 17, 4, '~S~tatus', New(PCheckBoxes, init(R,
			NewSItem('Running',NewSItem('Retired',
			NewSitem('Duff Time',
			NewSItem('Disqualified',nil)))))));
		{$IFDEF RallyPress} Current^.SetState(sfDisabled, True);		{$ENDIF}

		InsTitledField(46,15,  5, 1, 'Barred @', New(PInputTime, init(R, itHM))); Current^.SetState(sfDisabled, True);
		InsTitledField(46,13,  8, 1, 'Penalty', New(PInputTime, init(R, itHMS))); Current^.SetState(sfDisabled, True);

		R.XYLD(10,14,24,4); Insert(New(PInputFreeText, init(R, 1000, 25, nil))); AddLabel('~N~otes', Current);
		Insert(PInputFreeText(Current)^.VScrollBar);
		{$IFDEF RallyPress} Current^.SetState(sfDisabled, True);		{$ENDIF}

{$IFDEF fixit}
		InsTitledField(10,Size.Y-1, 6,1, 'Idx Nu', 	New(PInputLint, init(R,9)));
		InsTitledField(20,Size.Y-1, 6,1, 'RP', 			New(PInputLint, init(R,9)));
		Insert(new(PSkipBytes, init(4))); {skip old reaceposbyclassidx}
		Insert(new(PSkipBytes, init(4))); {skip old geogp[os}

		InsTitledField(30,Size.Y-1, 6,1, 'Hook', 		New(PInputLint, init(R,9)));
{		TimingsHook : longint;
		StartPos		: longint;
		LastCPPos		: longint; {last control point passed - route pos}
{$ENDIF}


		InsTitledField(36, 1, 36, 11, '~T~imings',
													New(PTimingListView,	Init(R, lsCarTiming, 0, hkCarTiming, @Self, PJimmyEditBox(EditBox))));
		{$IFDEF RallyPress} PTimingListView(Current)^.ViewOnly := True;		{$ENDIF}
		Insert(PTimingListVIew(Current)^.VScrollBar);

		{$IFNDEF RallyPress}
			Insert(New(PRecalcButton, init(63, Size.Y-5, '~R~ecalc', cmNone, bfGetData, @Self)));
			Insert(New(PJimmyOKButton, init(53,Size.Y-3, @Self)));
		{$ENDIF}
		Insert(New(PJimmyCancelButton, init(63,Size.Y-3, @Self)));

		EndInit;

		{$IFNDEF RallyPress}
		if (RecNo<>-1)  then F^.Focus;
		{$ENDIF}

	end;

	{$IFDEF RallyPress}
{		DisableViews;{}
	{$ENDIF}

end;

{*******************************
 ***      FILE               ***
 *******************************}
const
	 {--- Required for Stream ----}
	 RCar : TStreamRec = (
		 ObjType : srCar;
		 VmtLink : Ofs(TypeOf(TCar)^);
		 Load : @TCar.Load;
		 Store : @TCar.Store
	 );

	TCarIdxSize = 80;


function TCar.RecSize : word;
begin RecSize := 150; end;

function TCar.srType : word;
begin srType := srCar; end;

constructor TCar.Load(var S : TDataStream);
var Ver : byte;
		B : byte;

begin
	S.Read(Ver, 1);

	case Ver of
		1 : begin
			inherited Load(S);

			S.Read(Number, 2);
			Entrant := S.readstr;
			Driver := S.ReadStr;
			S.Read(DriverNat, 4);
			CoDriver := S.ReadStr;
			S.Read(CoDriverNat, 4);
			S.Read(Event, 4);
			S.Read(Class, 4);
			S.Read(Status, 2);
			Make := S.ReadStr;
			WhenBarred.Load(S);
			CumulTime.Load(S);
			Notes^.Load(S);
			S.Read(LastCPPos, 4);
		end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not recognised'#13#10'TCar.Load',mfError,hcNoContext);
		fail;
	end; {}
end; {proc}


procedure  TCar.StoreFields(var S : TDataSTream);
var	Ver : byte;

begin
	Ver := 1; S.Write(Ver, 1);

	inherited StoreFields(S);

	S.Write(Number, 2);
	S.WriteStr(@Entrant);
	S.WriteStr(@Driver);
	S.Write(DriverNat, 4);
	S.WriteStr(@CoDriver);
	S.Write(CoDriverNat, 4);
	S.Write(Event, 4);
	S.Write(Class, 4);
	S.Write(Status,2);
	S.WriteStr(@Make);
	WhenBarred.Store(S);
	CumulTime.Store(S);
	Notes^.Store(S);
	S.Write(LastCPPos,4);
end;


{============= INDEXING/HOOKING ========================}
function TCar.NumixTypes;
begin Numixtypes := 3; end;

{Get items the letter is supposed to hook up to when storing}
procedure TCar.GetIndex;
begin
	inherited GetIndex(ixType, Idxrec, fiType);
	case ixType of
		1 : begin IdxRec := @NumberIdx; 				fiType := fiCarNumberIdx; end;
		2 : begin RacePosByClassIDx := -1; IdxRec := @RacePosByClassIdx; fiType := 0 {fiCarRacePosClassIdx}; end;
		3 : begin IdxRec := @RacePosIdx; 				fiType := fiCarRacePosIdx; end;
{		4 : begin IdxRec := @GeogPosIdx; 				fiType := fiCarGeogPosIdx; end;{}
	end;
end;

function TCar.GetIndexKey;
var	ClassNum	: byte;
begin
	GetIndexKey := '';
	case ixType of
		1 : if not Deleted then GetIndexKey := PadZero(N2Str(Number),3)+' '+Driver;

{		2 : if (Status and stRunning)>0 then begin
					{position in race - given by cumul penalty, except that as each car
					does another stage it gets a larger penalty, so we have to store by last
					last control passed, then penalty. Subdivide by CLASS}
{					if (length(Class)>=2) then ClassNum := S2Num(Copy(Class,2,2)) else ClassNum := 0;
					GetIndexKey := N2Str(ClassNum) {split by class...}
{												+PadZero(N2Str(999-LastCPPos),3) {last control point}
{												+CumulTime.AsKey(itHMS)				{penalty}
{												+PadZero(N2Str(Number),3);   {and start pos/car number for beginning when all have 0 penalty{}
{				end;{}

		3 : if not Deleted and ((Status and stRunning)>0) then begin
					{position in race - as above}
					GetIndexKey := PadSpaceR(Event,3)
												+PadZero(N2Str(999-LastCPPos),3) {last control point}
												+CumulTime.AsKey(itHMS)				{penalty}
												+PadZero(N2Str(Number),3);   {and start pos/car number for beginning when all have 0 penalty{}
				end;

{		4 : if (Status and stRunning)>0 then begin
					{race geographical position - ie which last cp, then in order of leaving cp}
{					Timing := GetLastTiming;
					if Timing<>nil then begin
						GetIndexKey := PadZero(N2Str(999-LastCPPos),3)
													+Timing^.TD.ASKey(itHMS); {time of departure}
{						dispose(Timing, done);
					end else
						{no timing, so set according to startpos}
{						if StartPos<>0 then GetIndexKey := '999'+PadZero(N2Str(StartPos),3);
				end;{}

	end;
end;

function TCar.NumopTypes;
begin Numoptypes := 1; end;

{Get items the letter is supposed to hook up to when storing}
procedure TCar.Getop;
begin
	opRec := nil;
	case opType of
		1 : begin opRec := @StartPos; end;
	end;
end;



function TCar.NumHkTypes;
begin Numhktypes := 1; end;

procedure TCar.GetHookOn;
begin
	inherited GetHookOn(hkType, HookRec);

	case hkType of
		hkCarTiming : HookRec := @TimingsHook;
	end;
end;


function TCar.HookingOn;
var Control,LateControl,LastControl : PControl;

begin
	HookingOn := False;
	if (hkType = hkCarTiming) then begin
		HookingOn := True; {store car}
		if HookingJimmy^.srtype=srTiming then	begin
			{add penalty}
			CumulTime.Add(PTiming(HookingJimmy)^.Penalty);

			{check for last cp pos}
			Control := PControl(GetJimmy(PTiming(HookingJimmy)^.CP));
			if Control^.RoutePos>LastCPPos then LastCPPos := Control^.RoutePos;

			{check valid}
			if PTiming(HookingJimmy)^.InValid then
				Status := Status or stInvalidTime
			else
				if (Status and stInvalidTime)>0 then Recalculate;

			{set whenlate - look for next latecontrol}
			LateControl := PControl(GetJimmy(IDStream(fiRouteIDIdx)^.GetIDPtr(Control^.RoutePos+1)));
			WhenBarred.SetTo(PTiming(HookingJimmy)^.TD);
			if LateControl<>nil then begin
				if LateControl^.ETT.Blank then
					WhenBarred.SetToSecs(WhenBarred.Secs - Control^.ETALdr.Secs + LateControl^.ETALdr.Secs)
				else
					WhenBarred.Add(LateControl^.ETT);
			end;

			while (LateControl<>nil) and LateControl^.MaxLate.Blank and (LateControl^.CType<>ctLegFinish) do begin
				LastControl := LateControl;
				LateControl := PControl(GetJimmy(IDStream(fiRouteIDIdx)^.GetIDPtr(LastControl^.RoutePos+1)));
				{work out ETT}
				if LateControl^.ETT.Blank then
					WhenBarred.SetToSecs(WhenBarred.Secs - LastControl^.ETALdr.Secs + LateControl^.ETALdr.Secs)
				else
					WhenBarred.Add(LateControl^.ETT);
				dispose(LastControl, done);
			end;
			if LateControl<>nil then begin
				if not LateControl^.MaxLate.Blank then
					WhenBarred.Add(LateControl^.MaxLate)
				else
					WhenBarred.Clear;
				dispose(LateControl, done);
			end else
				WhenBarred.Clear;

			dispose(Control, done);
		end;
		if HookingJimmy^.srtype=srPenalty then	CumulTime.Add(PPenalty(HookingJimmy)^.Penalty);
	end;
end;

function TCar.UnHooking;
begin
	UnHooking := False;
	if (hkType = hkCarTiming) then begin
		UnHooking := True; {store car}
		Recalculate; {it's not going to happen v. often, and may cause an
									stinvalidtime}
	end;
end;

function TCar.ReHooking;
begin
	ReHooking := False;
	if (hkType = hkCarTiming) then begin
		ReHooking := True; {store car}
		Recalculate; {it's not going to happen v. often, and needs to recalc all timings}
	end;
end;

procedure TCar.PreStoreing;
begin
	inherited PreStoreing(DiskJimmy);

	if StartPos<=0 then begin
		{add to end}
		StartPos := GetNewID(srType);
		SetIDPtr(srCar, StartPos, RecNo); {recno is set before put}
	end;

	Storeop(1); {store pointer}
end;


{******************************************
 ***           PRINTING                 ***
 ******************************************}
function TCar.GetPrintType(var PrintType : TJimmyPrintType; PrintAs : PSItem; PrintAsLink : pointer) : word;
begin
	{$IFDEF RallyPress}
	GetPrintType := cmOK; {auto print}
	{$ELSE}
	GetPrintType := inherited GetPrintType(PrintType, PrintAs, PrintAsLink);
	{$ENDIF}
end;

procedure TCar.PrintFull(const Device : PDeviceStream; const PrintAs : word);

	procedure PrintTIming(Hook : PHook); far;
	var Jimmy : Pjimmy;
	begin
		Jimmy := GetJimmy(Hook^.JimmyID);
		if Jimmy<>nil then begin
			Jimmy^.PrintForm(Device, 'CARTIME.FRM');
			dispose(Jimmy, done);
		end;
	end;


begin
	SetFormCodes(Device^.FormCodes);
	DEvice^.StartPrint('CAR','REPORT');

	{print timings}
	FileAdmin(fiHooks)^.LogOn;
	HookFile^.ForEach(TimingsHook, @PrintTiming);
	FileAdmin(fiHooks)^.LogOff;

	Device^.EndPrint;
end;


procedure TCar.SetFormCodes;
var	Timing : PTiming;
		Control : PControl;
		ST : TTime;
		S : string;
		IDIndex : PIDIndexFile;
		CurrentLeg : PLeg;

begin
	with FormCodes^ do begin
		SetStr('NO', N2Str(Number));
		SetStr('REF', GetName(naRef,0));

		{start stuff}
		SetStr('SP', N2Str(StartPos));{}
		SetStr('ST', '');

		{start time - current leg}
		if RallySetup.CurrentLegID<>-1 then begin
			CurrentLeg := PLeg(GetJimmy(RallySetup.CurrentLegID));
			if StartPos<>-1 then begin
				New(IDIndex, init(srControl, opRoute));
				Control := PControl(GetJimmy(CurrentLeg^.CPStart));
				dispose(IdIndex, done);
				if Control<>nil then begin
					ST.SetToSecs(Control^.ETALdr.Secs + (RallySetup.StartInterval*(StartPos-1)));
					SetStr('ST', ST.Digit5);
					dispose(Control, done);
				end;
			end;
			dispose(CurrentLeg, done);
		end;

		SetStr('ENTRANT', Entrant);
		SetStr('DRIVER', Driver);
		SetStr('DNAT', delspace(DriverNat));
		SetStr('CODRIVER', CoDriver);
		SetStr('CNAT', delspace(CoDriverNat));
		SetStr('MAKE', Make);

		Insert(New(PSCodeFormCode, init('CLASS', Class, scCarClass)));
		Insert(New(PSCodeFormCode, init('EVENT', Event, scCarEvent)));

		SetStr('CL', delspace(Class));
		SetStr('EV', delspace(Event));

		SetStr('PENALTY', CumulTime.Digit8);
		SetStr('WTB', WhenBarred.Digit5);


{		Insert(New(PTimingsFormCode, init('TIMINGS', GetJimmy(RecNo), hkCarTiming, 'CARTIME.FRM',0)));{}

		TIming := GetLastTiming;
		if Timing<>nil then begin
			Insert(New(PJimmyFormCode, init('LASTCP', Timing^.CP)));
			Insert(New(PJimmyFormCode, init('LASTTM', Timing^.RecNo)));
			dispose(Timing, done);
		end else begin
			Insert(New(PJimmyFormCode, init('LASTCP', -1)));
			Insert(New(PJimmyFormCode, init('LASTTM', -1)));
		end;

		{status}
		S := '';
		if Status and stRetired>0 then S :=S + 'R/';
		if Status and stDisqualified>0 then S := S + 'D/';
		{$IFNDEF RallyPress}
			if IsTimeBarred(TimeNow) then S := S + 'TB/';
		{$ENDIF}
		if Status and stInvalidTime>0 then S := S + 'IT/';
		if S<>'' then
			SetStr('STATUS',Copy(S,1,length(S)-1))
		else
			SetStr('STATUS','');

	end;
end;

{*************************************************
 ***           CALCULATE                       ***
 *************************************************}
{recalculates all penalties}
procedure TCar.ReCalculate;
var InvTime : boolean;
		Jimmy : PJimmy;

	procedure AddTimings(Jimmy : PJimmy); far;
	begin
		case Jimmy^.srtype of
			srPenalty : CumulTime.Add(PPenalty(Jimmy)^.Penalty);
			srTiming 	: begin
				PTiming(JImmy)^.Recalculate;
				PutJimmy(Jimmy); {not sure what effects storeself will have though
														as it tries to do hooking ons/offs/etc}
{				Jimmy^.StoreSelf; {need to do a storeself so it re-adjusts in
															control point list if need be}

				if Status and stInvalidTime>0 then begin
					InvTime := True;
					Status := Status and not stInvalidTime; {prevent looping}
				end;
				HookingOn(hkCarTiming, 0, Jimmy); {work out penalty, validation, etc}
			end;
		end;
	end;

begin
	if Recalculating then exit; {so it doesn't loop on doing the timing storeself}

	Recalculating := True;
	{go through and add up all cumulative penalty times}
	CumulTime.Clear;
	InvTime := False;
	LastCPPos := 0;
	Fileadmin(fiHooks)^.LogOn;
	Status := Status and not stInvalidTime;
{	HookFile^.ForEach(TimingsHook, @AddTimings); can't do this as it goes from latest to earliest}

	Jimmy := HookFile^.GetLast(TimingsHook, 0);
	while Jimmy<>nil do begin
		AddTimings(Jimmy);
		dispose(Jimmy, done);
		Jimmy := HookFile^.GetPrevJimmy;
	end;

	Fileadmin(fiHooks)^.LogOff;
	if InvTime then Status := Status or stInvalidTime;

	Recalculating := False;
end;

{procedure TCar.CalcPenaltyTo(const TCNum : word; var Penalty : TTime);
var Timing : PTiming;
		Control : PControl;
begin
	{go through and add up cumulative penalty time to a particular control point}
{	CumulTime.Clear;
	Fileadmin(fiHooks)^.LogOn;
	Timing := PTiming(HookFile^.GetFirst(TimingsHook, srTiming));
	while Timing<>nil do begin
		Control := PControl(GetJimmy(Timing^.CP));
		if Control^.Number<>TCNum then CumulTime.Add(Timing^.Penalty);
		dispose(Control, done);
		dispose(Timing, done);
		Timing := PTiming(HookFile^.GetNextJImmy);
	end;
	Fileadmin(fiHooks)^.LogOff;{}
{end;{}



function TCar.GetLastControl : PControl;
begin
	if LastCPPos <= 0 then
		GetLastControl := nil
	else
		GetLastControl := PControl(getJimmy(IDStream(fiRouteIDIdx)^.GetIDPtr(LastCPPos)));
end;

function TCar.GetLastTiming : PTiming;
begin
	FileAdmin(fiHooks)^.LogOn;
	GetLastTiming := PTiming(HookFile^.GetFirst(TimingsHook, srTiming));
	FileAdmin(fiHooks)^.LogOff;
end;

function TCar.IsTimeBarred(const Time : TTime) : boolean;
begin
	if WhenBarred.Blank then
		IsTimeBarred := False
	else
		IsTimeBarred := (WhenBarred.Secs<Time.Secs);
end;


{**************************************************************
 ***                                                        ***
 ***                       TIMING                           ***
 ***                                                        ***
 **************************************************************}
constructor TTiming.Init(Param : PJimmyInitParam);
begin
	inherited Init;
	CP := -1;
	CarID := -1;
	if Param<>nil then begin
		if Param^.ListView<>nil then begin
			case Param^.ListView^.lstype of
				lsControlTiming : CP := Param^.ForWho;
				lsCarTiming 		: CarID := Param^.ForWho;
			end;
		end;
	end;
	TA.Clear;
	TD.Clear;
	TT.Clear;
	ETT.Clear;
	Penalty.Clear;
	CumTotal.Clear;
	InValid := False;
	Early := False;
	cpType := '';
end;

procedure TTiming.CommonInit;
begin
	inherited CommonInit;
	FileAdmin(fiTimingsTable)^.LogOn;
end;

destructor TTiming.Done;
begin
	FileAdmin(fiTimingsTable)^.LogOff;
	inherited Done;
end;

function TTiming.DisplayLine(ListForWho : longint; lstype : byte; Maxlen : integer; View : word) : string;
var S : string;
begin
	S := '';
	if ListForWho<>CP 	then S := S + SetLength(GetJimmyIDName(CP,naFull,0),19)+' ';
	if ListForWho<>CarID 	then S := S + SetLength(GetJimmyIDName(CarID, naFull, 0),19)+' ';
	if cpType = ctComp then
		S := S+CumTotal.Digit8
	else
		S := S+Penalty.Digit8;{ + ' '+CumTotal.Digit8;{}
	if Maxlen<30 then S := S+#13+'    ';

	if not TA.Blank then begin
		S := S+' '+TA.Digit5;
		if Speed<>0 then S := S+' '+N2Str(Speed)+'kph'
	end else
		S := S+' '+TD.Digit5;

	DisplayLine := S;
end;

type
	PMarkerText = ^TMarkerText;
	TMarkerText = object(TStaticText)
		B : boolean;
		procedure SetData(var Rec); virtual;
		procedure GetData(var Rec); virtual;
		function DataSize : word; virtual;
	end;

	procedure TMarkerText.SetData;
	begin
		B := boolean(Rec);
		if B then Show else Hide;
	end;

	procedure TMarkerText.GetData;
	begin
		boolean(Rec) := B;
	end;

	function TMarkerText.DataSize;
	begin DataSize := sizeof(boolean); end;



type
	PInputTimingControl = ^TInputTimingControl;
	TinputTimingControl = object(TinputCP)
		CarLine : PInputJimmy;
		function Valid(Command : word) : boolean; virtual;
	end;

	function TInputTimingControl.Valid;
	var	Car : PCar;
			V : boolean;
			Control : PControl;

	begin
		V := inherited Valid(Command);

		if not GetState(sfDisabled) and V and DoValidFor(Command) then begin
			{check for already entered}
			if {(Command<>cmForceLink) and{} (CarLine<>nil) then begin
				Car := PCar(CarLine^.GetJimmy);
				Control := PControl(GetJimmy);
				if (Car<>nil) and (Control<>nil) and (TableStream(fiTimingsTable)^.GetPtr(Control^.RoutePos, Car^.Number)<>-1) then begin
					V := False;
					Focus;
					LinkChanged := False; {so doesn't loop on releasing focus-->checklink}
					InputWarning('!! TIMING ALREADY ENTERED !!',hcNoContext);
					Data^ := '';	SetID; Draw;
				end;
			end;
		end;

		Valid := V;
	end;

type
	PInputTimingCar = ^TInputTimingCar;
	TinputTimingCar = object(TinputCar)
		ControlLine : PInputJimmy;
		function Valid(Command : word) : boolean; virtual;
	end;

	function TInputTimingCar.Valid;
	var	Control : PControl;
			V : boolean;
	begin
		V := inherited Valid(Command);

		if not GetState(sfDisabled) and V and DoValidFor(Command) then begin

			{check for non-running car}
			if (GetJimmy<>nil) and ((PCar(GetJimmy)^.Status and stRunning)=0) then begin
				V := False;
				Focus;
				LinkChanged := False;
				InputWarning('Car is no longer running',hcNoContext);
			end;

			{check for already entered}
			if {(Command<>cmForceLink) and{} (ControlLine<>nil) then begin
				Control := PControl(ControlLine^.GetJimmy);
				{control routepos should never be less than 0..., anyway...}
				if (Control<>nil) and (Control^.RoutePos>-1)
					and (TableStream(fiTimingsTable)^.GetPtr(Control^.RoutePos, S2Num(SearchOn))<>-1) then begin
					V := False;
					Focus;
					LinkChanged := False; {so it doesn't loop on releasing focus-->checklink}
					InputWarning('!! TIMING ALREADY ENTERED !!',hcNoContext);
{					Data^ := '';	SetID; Draw;{}
				end;
			end;
		end;

		Valid := V;
	end;


const
	{shared ones}
	svCar = 1;
	svControl =2;
	tvETT = 1;
	tvTD = 2;
	tvInvTimeMarker = 3;

	{autofill (& mustinputlink)}
	tvAFTA = 4;

	tvTB = 5; {time barred marker}
	{penalty}
	svPTA = 3;
{	tvPenalty = 4;
{	tvTT = 5;
	tvEarlyMarker = 6;
	tvSpeed =7;
	tvCumTotal = 8;{}


{automatically fills in time arrival/departure from expected values from
cp/car entered}
procedure AutoFill(const Linker : PInputLinker; const CallingView : PView); far;
var
	Car : PCar;
	Control : PControl;
	PrevTD, ETT,TA,TD : TTime;
	PrevTimingID : longint;
	PrevTiming : PTiming;

begin
	Control := PControl(PInputJimmy(Linker^.SourceView[svControl])^.GetJimmy);
	Car := PCar(PInputJimmy(Linker^.SourceView[svCar])^.GetJimmy);

	if (Control=nil) or (Car=nil) then exit; {not enough info entered yet}
	if (Control^.CType=ctPass) then exit; {no timings required}
	if PJimmyEditBox(CallingView^.Owner)^.Jimmy^.RecNo <> -1 then exit; {only do as a default for new timing}

	{=== Calculate estimated arrival/departure time from prev timing}

	{expected time taken... - take from control point info}
	ETT.SetTo(Control^.ETT);
	Linker^.TargetView[tvETT]^.SetData(ETT);
	Linker^.TargetView[tvETT]^.Draw;

	if (Control^.CType=ctComp) then exit; {no auto filling}

	if Control^.Ctype=ctLegStart then begin
		{first one of leg - normal start cp}
		TA.Clear;
		TD.SetToSecs(Control^.ETALdr.Secs + (RallySetup.StartInterval*(Car^.StartPos-1)))
	end else begin
		{Road section}
		{We need to get the timing of the previous control for this car...}
		{which is the one to the left in the table of this one}
		PrevTimingID 	:= TableStream(fiTimingsTable)^.GetPtr(Control^.RoutePos-1, Car^.Number);
		PrevTiming 		:= PTiming(GetJimmy(PrevTimingID));

		if PrevTiming=nil then begin
			{need to notify user that previous timing is missing?}
			Linker^.TargetView[tvInvTimeMarker]^.Show;
			Linker^.TargetView[tvInvTimeMarker]^.Draw;{}
			TA.Clear;
			TD.Clear;
		end else begin
			PrevTD.SetTo(PrevTiming^.TD);
			PrevTD.Sec := 0;   {round off for early/late penalty calcs}

			{put in default arrival & departure times}
			TA.SetTo(PrevTD);
			TA.Add(ETT);{}

			if (Control^.CType=ctRoad) or (Control^.CType=ctServOut) then
				TD.SetToSecs(TA.Secs+RallySetup.CPGap) {departure time}
			else
				if (Control^.CType=ctLegFinish) then
					TD.Clear
				else
					TD.SetTo(TA);

			dispose(PrevTiming, done);
		end;
	end;

	{put into fields}
	Linker^.TargetView[tvAFTA]^.SetData(TA);
	Linker^.TargetView[tvAFTA]^.Draw;
	Linker^.TargetView[tvTD]^.SetData(TD);
	Linker^.TargetView[tvTD]^.Draw;

	PInputELine(Linker^.TargetView[tvAFTA])^.ForceLink;
end;


{Calculates penalty, speed, etc and early marker}
procedure CalcPenalty(const Linker : PInputLinker; const CallingView : PView); far;
var TA,TD, PrevTD : TTime;
		Control : PControl;
		Car : PCar;
		WorkTiming : PTiming;
		B : boolean;

begin
	{this linker works out the expected arrival times, penalties, etc, from the
		control point}
	Control := PControl(PInputJimmy(Linker^.SourceView[svControl])^.GetJimmy);
	Car := PCar(PInputJimmy(Linker^.SourceView[svCar])^.GetJimmy);
{	Timing := PTiming(PJimmyEditBox(CallingView^.Owner)^.Jimmy); {self}
{	Info := ''; {marks whether early/timing missing}

	if (Control=nil) or (Car=nil) then exit; {not enough info entered yet}
{can't do this as need to keep cum score	if (Control^.CType=ctPass) or (Control^.CType=ctLegStart) then exit;{}

	PInputTime(Linker^.SourceView[svPTA])^.GetData(TA);
	PInputTime(Linker^.TargetView[tvTD])^.GetData(TD);

	{set TD from TA if a road point and came in later than expected}
	if ((Control^.CType=ctRoad) or (Control^.CType=ctServOut))
		 and (CallingView=Linker^.SourceView[svPTA]) and (TA.Secs>TD.Secs) then begin
		TD.SetToSecs(TA.Secs+RallySetup.CPGap);
		Linker^.TargetView[tvTD]^.SetData(TD);
		Linker^.TargetView[tvTD]^.Draw;
	end;

	{if it's a competitive bit, make sure TD=TA (although operator may change
	this if the car is held}
	if (Control^.CType=ctComp) or (Control^.CType=ctServIn) then begin
		TD.SetTo(TA);
		Linker^.TargetView[tvTD]^.SetData(TD);
		Linker^.TargetView[tvTD]^.Draw;
	end;

	{CALCULATE PENALTY, ETC}
	New(WorkTiming, init(nil));
	CallingView^.Owner^.GetData(WorkTiming^);
	WorkTiming^.CalculatePenalty(Control, Car^.Number);{}

	CallingView^.Owner^.SetData(WorkTiming^);
	CallingView^.Owner^.Redraw;

	dispose(WorkTiming, done);

	{Check Time Barring}
	B := Car^.IsTimeBarred(TA);
	Linker^.TargetView[tvTB]^.SetData(B);
	Linker^.TargetView[tvTB]^.Draw;

end;


procedure LinkMustInputs(const Linker : PInputLinker; const CallingView : PView); far;
var Control : PControl;
begin
	Control := PControl(PInputJimmy(Linker^.SourceView[svControl])^.GetJimmy);

	if Control<>nil then begin
		{time arrival}
		PInputELine(Linker^.TargetView[tvAFTA])^.MustInput := (Control^.CType<>ctLegStart);
		Linker^.TargetView[tvAFTA]^.SetState(sfDisabled, Control^.Ctype=ctLegStart);

		{time departure}
		if (Control^.CType = ctLegFinish) {or (Control^.CType=ctServIn){} or (Control^.CType=ctComp) then begin
			PInputELine(Linker^.TargetView[tvTD])^.MustInput	 := False;
			Linker^.TargetView[tvTD]^.SetState(sfDisabled, True);
		end else begin
			PInputELine(Linker^.TargetView[tvTD])^.MustInput	 := False;
			Linker^.TargetView[tvTD]^.SetState(sfDisabled, False);
		end;

		if CallingView^.Owner^.Current^.GetState(sfDisabled) then
			CallingView^.Owner^.SelectNext(False);
	end;
end;

type
	PPenaltyLine = ^TPenaltyLine;
	TPenaltyLine = object(TInputTime)
		HaveWarned : boolean;
		InputCP : PInputCP;
		constructor Init(Bounds : Trect; NInputCP : PView);
		function Valid(Command : word) : boolean; virtual;
	end;

	constructor TPenaltyLine.Init;
	begin
		inherited Init(Bounds, itHMS);
		HaveWarned := False;
		InputCP := PInputCP(NInputCP);
	end;

	function TPenaltyLine.Valid;
	var V : boolean;
			T : Ttime;
			CType : TScode;
	begin
		V := inherited Valid(Command);

		if V and DoValidFor(Command) and not HaveWarned then begin
			if InputCP^.GetJimmy<>nil then begin
				CType := PControl(INputCP^.GetJimmy)^.CType;
				if  (CType= ctRoad) or (CType=ctServIn) or (CType=ctServOut) then begin
					GetData(T);
					if T.Mins>=20 then begin
						MessageBox('TIMING','PENALTY >20m?'#13#10'Please Check!',
						mfWarning+mfPayAttentionBleep+mfOKButton,hcNoContext);
						HaveWarned := True;
						V := False;
					end;
				end;
			end;
		end;

		Valid := V;
	end;




procedure TTiming.MakeEditBox(var EditBox : PEditBox; Caller : PView);
var R : TRect;
		AutoFillLinker, PenaltyLinker,MustInputLinker : PInputLinker;
		ControlLine, CarLine, ETTLine, TTLine, TALine, TDLine : PView;

		EMarker,VMarker : PView;

begin
	R.Assign(0, 0, 37,13); {Size of box}
	CentreOnView(R, Caller);
	EditBox := New(PJimmyEditBox, init(R, 'Timing Entry',Caller, @Self));

	New(AutoFillLinker, init(@AutoFill, EditBox));
	New(PenaltyLinker, init(@CalcPenalty, EditBox));
	New(MustInputLinker, init(@LinkMustInputs, EditBox));
	MustInputLinker^.ForceInitLink := True;

	with EditBox^ do begin

		{--- Set up box interior ---}
		{ X, Y, Boxlen,  FieldLen,     Title}
		Insert(New(PSkipBytes, init(sizeof(TJimmy))));

		ControlLine :=InsTitledField(10, 1, 20, 1, 'Control', New(PInputTimingControl, init(R, 20,'')));
		PinputELine(ControlLine)^.MustInput := True;
		AutoFillLinker^.SetSourceView(ControlLine, svControl);
		PenaltyLinker^.SetSourceView(ControlLine, svControl);
		MustInputLinker^.SetSourceView(ControlLine, svControl);
		if RecNo<>-1 then ControlLine^.SetState(sfDisabled, True); {don't allow edit}

		CarLine := InsTitledField(10, 2, 20, 1, 'Car', 		New(PInputTimingCar, init(R, 20)));
		PinputELine(CarLine)^.MustInput := True;
		AutoFillLinker^.SetSourceView(CarLine, svcar);
		PenaltyLinker^.SetSourceView(CarLine, svcar);
		if RecNo<>-1 then CarLine^.SetState(sfDisabled, True); {don't allow edit}

		TALine := InsTitledField(10, 3,  8, 1, 'TA',		New(PInputTime, init(R,itHMS+itZeroBlank or itTime)));
		AutoFIllLinker^.SetTargetView(Current, tvAFTA);
		MustInputLinker^.SetTargetView(Current, tvAFTA);
		PenaltyLinker^.SetSourceView(Current, svPTA);

		TDLine := InsTitledField(10, 4,  8, 1, 'TD',		New(PInputTime, init(R,itHMS+itZeroBlank or itTime)));
		AutoFillLinker^.SetTargetView(Current, tvTD);
		MustInputLinker^.SetTargetView(Current, tvTD);
		PenaltyLinker^.SetTargetView(Current, tvTD);

		TTline := InsTitledField(10, 6,   8, 1, 'TT', New(PinputTime, init(R, itHMS+itZeroBlank)));
		Current^.SetState(sfDisabled, True);{}

		ETTLine := InsTitledField(10,7,   8, 1, 'ETT', New(PinputTime, init(R, itHMS+itZeroBlank)));
		Current^.SetState(sfDisabled, True);
		AutoFillLinker^.SetTargetView(Current, tvETT);

		InsTitledField(10,8,   8, 1, 'Penalty', New(PPenaltyLine, init(R, ControlLine)));  Current^.SetState(sfDisabled, True);

		InsTitledField(10,9,   8, 1, 'Cumul', New(PinputTime, init(R, itHMS)));  Current^.SetState(sfDisabled, True);

		InsTitledField(10,11,   8, 1, 'Av Speed', New(PinputWord, init(R, 3)));  Current^.SetState(sfDisabled, True);

		{Early marker}
		R.Assign(20,8,26,9);
		EMarker := New(PMarkerText, init(R, 'EARLY!'));
		Insert(EMarker);

		{Valid marker}
		R.XYLD(25,4,7,2);
		VMarker := New(PMarkerText, init(R, 'MISSING TIME!'));
		Insert(VMarker);
		AutoFillLinker^.SetTargetView(VMarker, tvInvTimeMarker);

		{Time Barred marker}
		R.XYLD(25,5,7,2);
		VMarker := New(PMarkerText, init(R, 'TIME BARRED!'));
		Insert(VMarker);
		PenaltyLinker^.SetTargetView(VMarker, tvTB);

		PInputTimingControl(ControlLine)^.CarLine := PINputjImmy(Carline);
		PInputTimingCar(CarLine)^.ControlLine := PINputjImmy(Controlline);

		Insert(New(PJimmyOKButton, init(26,Size.Y-5, @Self)));
		Insert(New(PJimmyCancelButton, init(26,Size.Y-3, @Self)));

		EndInit;

		if (RecNo=-1) and (CP<>-1) then SelectNext(False); {auto move onto car line}
	end;

	{$IFDEF RallyPress}
{		DisableViews;{}
	{$ENDIF}

end;

{*******************************
 ***      FILE               ***
 *******************************}
const
	 {--- Required for Stream ----}
	 RTiming : TStreamRec = (
		 ObjType : srTiming;
		 VmtLink : Ofs(TypeOf(TTiming)^);
		 Load : @TTiming.Load;
		 Store : @TTiming.Store
	 );

	TTimingIdxSize = 80;


function TTiming.RecSize : word;
begin RecSize := 100; end;

function TTiming.srType : word;
begin srType := srTiming; end;

constructor TTiming.Load(var S : TDataStream);
var Ver : byte;
		Control : PControl;

begin
	S.Read(Ver, 1);

	case Ver of
		1 : begin
			inherited Load(S);

			S.Read(CP, 4);
			S.Read(CarID, 4);
			TA.Load(S);
			TD.Load(S);
			TT.Load(S);
			ETT.Load(S);{}
			Penalty.Load(S);
			CumTotal.Load(S);
			S.Read(Speed,2);
			S.Read(Early,1);
			S.Read(InValid,1);
			Control := PControl(GetJimmy(CP));
			if Control<>nil then begin
				cpType := Control^.CType;
				dispose(Control, done);
			end;
		end;
		2 : begin
			inherited Load(S);

			S.Read(CP, 4);
			S.Read(CarID, 4);
			TA.Load(S);
			TD.Load(S);
			TT.Load(S);
			ETT.Load(S);{}
			Penalty.Load(S);
			CumTotal.Load(S);
			S.Read(Speed,2);
			S.Read(Early,1);
			S.Read(InValid,1);
			S.Read(cpType,4);
		end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not recognised'#13#10'TTiming.Load',mfError,hcNoContext);
		fail;
	end; {}
end; {proc}


procedure  TTiming.StoreFields(var S : TDataSTream);
var	Ver : byte;

begin
	Ver := 2; S.Write(Ver, 1);

	inherited StoreFields(S);
	S.Write(CP, 4);
	S.Write(CarID, 4);
	TA.Store(S);
	TD.Store(S);
	TT.Store(S);
	ETT.Store(S);{}
	Penalty.Store(S);
	CumTotal.Store(S);
	S.Write(SPeed, 2);
	S.Write(Early, 1);
	S.wRite(InValid, 1);
	S.Write(CPType,4);
end;


function TTiming.NumHookTo;
begin NumHooKTo := 2; end;

procedure TTiming.GetHookTo;
var Control : PControl;
begin
	inherited GetHookTo(htType, HookToID, SubHookToID, hkType, Key,InsertBias);

	case htType of
		1 : begin
			HookToID := @CP; 		hkType := hkCtrlTiming;
			if cpType=ctComp then Key := TT.Secs else Key := -TD.Secs;
		end;
		2 : begin
			HookToID := @CarID; hkType := hkCarTiming;
{			if TD.BLank then
				Key := -TA.Secs
			else
				Key := -TD.Secs;{doesn't work over several days, as second leg may start earlier than first...}
			Control := PControl(GetJimmy(CP));
			Key := -99999-Control^.RoutePos; {-99999 botch for 1997 safari rally, 2nd leg}
			dispose(Control, done);

		end;{}
	end;
end;


procedure TTiming.PreStoreing;
var Control : PControl;
		Car : PCar;
begin
	if Deleted and not DiskJimmy^.Deleted then begin
		{just been deleted - remove from matrix}
		Control := PControl(GetJimmy(PTiming(DiskJimmy)^.CP));
		Car := PCar(GetJimmy(PTiming(DiskJimmy)^.CarID));
		if (Control=nil) or (Car=nil) then begin
			ProgramError('Timing with no car/control recno '+N2Str(RecNo),hcNoContext);
		end else
			if TableStream(fiTimingsTable)^.GetPtr(Control^.RoutePos, Car^.Number)=RecNo then
				TableStream(fiTimingsTable)^.SetPtr(Control^.RoutePos, Car^.Number, -1);
	end else begin
		Control := PControl(GetJimmy(CP));
		Car := PCar(GetJimmy(CarID));
		if (Control=nil) or (Car=nil) then begin
			ProgramError('Timing with no car/control recno '+N2Str(RecNo),hcNoContext);
		end else
			TableStream(fiTimingsTable)^.SetPtr(Control^.RoutePos, Car^.Number, RecNo);
	end;

	if Control<>nil then cpType := Control^.CType;

	if Car<>nil then dispose(Car, done);
	if Control<>nil then dispose(Control, done);
end;

{*****************************************
 ***          PRINTING                 ***
 *****************************************}
procedure TTiming.SetFormCodes(const FormCodes: PFormCodeCollection);
var Fastest : TTime;
		LdrDiff : TTime;
begin
	with FormCodes^ do begin
		Insert(New(PJimmyFormCode, init('CP', CP)));
		Insert(New(PJimmyFormCode, init('CAR', CarID)));

		SetStr('TA', TA.Digit5);
		SetStr('TA8', TA.Digit8);
		SetStr('TA5', TA.Digit5);
		SetStr('TD', TD.Digit5);
		SetStr('TD8', TD.Digit8);
		SetStr('TD5', TD.Digit5);

		SetStr('TT', TT.Digit5);
		SetStr('TT8', TT.Digit8);
		SetStr('ETT', ETT.Digit5);
		SetStr('PEN', Penalty.Digit8);
		SetStr('CUM', CumTotal.Digit8);

		SetStr('SPEED', N2Str(Speed));
		if Early then SetStr('E','E') else SetStr('E',' ');
		if invalid then SetStr('IT','IT') else SetStr('IT','  ');

		{if <FASTEST> set, then work out difference between it and time taken}
		if QDecode('FASTEST')<>'' then begin
			Fastest.SetToStr(QDecode('FASTEST'));
			LdrDiff.SetToSecs(TT.Secs - Fastest.Secs);
			SetStr('LDRDIFF', LdrDiff.Digit8);
		end else
			SetStr('LDRDIFF','');

		{cumulative penalty}
		if QDecode('CUMTEST')<>'' then begin
			Fastest.SetToStr(QDecode('CUMTEST'));
			LdrDiff.SetToSecs(CumTotal.Secs - Fastest.Secs);
			SetStr('CLDRDIFF', LdrDiff.Digit8);
		end else
			SetStr('CLDRDIFF','');
	end;
end;


{**********************************************
 *** TIMING PENALTY CALCULATION             ***
 **********************************************}
 {So that this function can be called w/o having to reget
 control point and car, pass these as parameters.  Then we
 can use this calculation for the editbox as well as the
 above recalculate}
procedure TTiming.CalculatePenalty(Control : PControl; CarNumber : word);
var
	PrevTimingID : longint;
	PrevTiming : PTiming;
	Secs : longint;
	L : longint;

begin
	{Given this timings TA, and prev timings TD, work out time taken,
	penalty, speed}
	{Note that all calculated fields will be set to blank if there is
	no previous timing}
	{Get previous timing from grid}
	PrevTimingID := TableStream(fiTimingsTable)^.GetPtr(Control^.RoutePos-1, CarNumber);
	PrevTiming := PTiming(GetJimmy(PrevTimingID));

	{start by clearing all}
	Early 	:= False;
	Invalid := False;
	TT.Clear;
	Penalty.Clear;
	Speed := 0;
	CumTotal.Clear;

	if Control^.CType = ctLegStart then begin
		{first one in leg...}
		if PrevTiming <> nil then
			CumTotal.SetTo(PrevTiming^.CumTotal); {first in new leg}

	end else begin
		if PrevTiming=nil then begin
			InValid := True;
		end else begin
			PrevTiming^.TD.Sec := 0; {drop seconds from previous}

			TT.SetToSecs(TA.Secs - PrevTiming^.TD.Secs); {time taken}
			{use L for this calc so it calcs in longint mode}
			if TT.Secs>0 then Speed := (longint(Control^.Dist) * 36 div TT.Secs) else Speed := 0;

			if Control^.CType = ctComp then begin
				Penalty.SetTo(TT);
			end else begin
				{road section - penalise for lateness/earlyness}
				Secs := TT.Secs - Control^.ETT.Secs;
				if Secs>=0 then begin
					{late - 1 min/min late}
					Penalty.SetToSecs((Secs div RallySetup.LatePenaltyPer)*RallySetup.LatePenalty);
				end else begin
					{early}
					Penalty.SetToSecs(-(Secs div RallySetup.EarlyPenaltyPer)*RallySetup.EarlyPenalty);
					if not Penalty.Blank then Early := True;
				end;
			end;
			CumTotal.SetTo(PrevTiming^.CumTotal);
			CumTotal.Add(Penalty);
		end;
	end;

	if PrevTiming<>nil then dispose(PrevTiming, done);
end;

procedure TTiming.Recalculate;
var Control : PControl;
		Car : PCar;
begin
	Control := PControl(GetJimmy(CP));
	Car := PCar(GetJImmy(CarID));
	CalculatePenalty(Control, Car^.Number);
	dispose(Car, done);
	dispose(Control, done);
end;


{**************************************************************
 ***                                                        ***
 ***                       Penalty                           ***
 ***                                                        ***
 **************************************************************}
constructor TPenalty.Init(Param : PJimmyInitParam);
begin
	inherited Init;
	CarID := -1;
	if Param<>nil then begin
		if Param^.ListView<>nil then begin
			case Param^.ListView^.lstype of
				lsCarTiming : CarID := Param^.ForWho;
			end;
		end;
	end;
	TimeGiven.Clear;
	Penalty.Clear;
	Reason := '';
end;

function TPenalty.DisplayLine(ListForWho : longint; lstype : byte; Maxlen : integer; View : word) : string;
var S : string;
begin
	S := '';
	if ListForWho<>CarID then S := S +SetLength(GetJimmyIDName(CarID, naFull, 0),16)+' ';
	DisplayLine := S+'Penalty @'+TimeGiven.Digit5+' '+Reason+' '+Penalty.Digit8;
end;




procedure TPenalty.MakeEditBox(var EditBox : PEditBox; Caller : PView);
var R : TRect;
		CarLine, AtLine : PView;

begin
	R.Assign(0, 0, 35,13); {Size of box}
	CentreOnView(R, Caller);
	EditBox := New(PJimmyEditBox, init(R, 'Penalty Entry',Caller, @Self));

	with EditBox^ do begin

		{--- Set up box interior ---}
		{ X, Y, Boxlen,  FieldLen,     Title}
		Insert(New(PSkipBytes, init(sizeof(TJimmy))));

		CarLine := InsTitledField(10, 1, 20, 1, 'Car', 		New(PInputCar, init(R, 20)));
		PinputELine(Current)^.MustInput := True;

		AtLine := InsTitledField(10, 2,  5, 1, 'Given @',	New(PInputTime, init(R,itHM+itZeroBlank+itTime)));
		PinputELine(Current)^.MustInput := True;

		InsTitledField(10, 3, 20, 1, 'Reason', 	New(PInputELine, init(R, 30)));

		InsTitledField(10, 4,  8, 1, 'Penalty',		New(PInputTime, init(R,itHMS+itZeroBlank)));
		PinputELine(Current)^.MustInput := True;

		Insert(New(PJimmyOKButton, init(5,Size.Y-3, @Self)));
		Insert(New(PJimmyCancelButton, init(16,Size.Y-3, @Self)));

		SelectNext(True);

		EndInit;

		if CarID<>-1 then
			AtLine^.FOcus
		else
			CarLine^.Focus
	end;

	{$IFDEF RallyPress}
{		DisableViews;{}
	{$ENDIF}

end;

{*******************************
 ***      FILE               ***
 *******************************}
const
	 {--- Required for Stream ----}
	 RPenalty : TStreamRec = (
		 ObjType : srPenalty;
		 VmtLink : Ofs(TypeOf(TPenalty)^);
		 Load : @TPenalty.Load;
		 Store : @TPenalty.Store
	 );

	TPenaltyIdxSize = 80;


function TPenalty.RecSize : word;
begin RecSize := 50; end;

function TPenalty.srType : word;
begin srType := srPenalty; end;

constructor TPenalty.Load(var S : TDataStream);
var Ver : byte;

begin
	S.Read(Ver, 1);

	case Ver of
		1 : begin
			inherited Load(S);

			S.Read(CarID, 4);
			TimeGiven.Load(S);
			Penalty.Load(S);
			Reason := S.ReadStr;
		end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not recognised'#13#10'TPenalty.Load',mfError,hcNoContext);
		fail;
	end; {}
end; {proc}


procedure  TPenalty.StoreFields(var S : TDataSTream);
var	Ver : byte;

begin
	Ver := 1; S.Write(Ver, 1);

	inherited StoreFields(S);
	S.Write(CarID, 4);
	TimeGiven.Store(S);
	Penalty.Store(S);
	S.WriteStr(@Reason);
end;


function TPenalty.NumHookTo;
begin NumHooKTo := 1; end;

procedure TPenalty.GetHookTo;
begin
	inherited GetHookTo(htType, HookToID, SubHookToID, hkType, Key,InsertBias);

	Key := -TimeGiven.Secs;

	case htType of
		1 : begin HookToID := @CarID; hkType := hkCarTiming; end;
	end;
end;

{**************************************************************
 ***                                                        ***
 ***                 NAVIGATING CP'S/TIMINGS                ***
 ***                                                        ***
 **************************************************************}
{=== GET PREV CP FROM GIVEN CP =========}
{no check to see if first in leg... should be?}
{function GetPrevCP(Control : PControl) : PControl;
var PrevID : longint;
begin
	if Control^.RoutePos>0 then begin
		PrevID := GetIDPtr(Control^.RoutePos-1); {get one previous in list}
{		GetPrevCP := PControl(GetJimmy(PrevID));
	end else
		GetPrevCP := nil;
end;

{=== GETS PREVIOUS TIMING FOR CAR/TIMING ======}
{Also tests if this is not the first in the leg (ie not LS) and whether
a timing is missing - ie it returns only the previous valid time *if* it was
for the control point before the one passed as a parameter, otherwise returns
nil}
{function GetPrevCarTiming(Car : PCar; Timing : PTiming) : PTiming;
begin
	FileAdmin(fiHooks)^.LogOn;
end;




{***************************************
 ***        CREATORS/ETC             ***
 ***************************************}
{====== JIMMYS ========}
function CreateLeg(P : pointer) : pointer; far;
begin CreateLeg := New(PLeg, init(PJImmyInitParam(P))); end;

function CreateControl(P : pointer) : pointer; far;
begin CreateControl := New(PControl, init(PJImmyInitParam(P))); end;

function CreateCar(P : pointer) : pointer; far;
begin CreateCar := New(PCar, init(PJImmyInitParam(P))); end;

function CreateTiming(P : pointer) : pointer; far;
begin CreateTiming := New(PTiming, init(PJImmyInitParam(P))); end;

function CreatePenalty(P : pointer) : pointer; far;
begin CreatePenalty := New(PPenalty, init(PJImmyInitParam(P))); end;

{====== STREAMS ========}
function NewLegIdxStream : PStream; far;
begin	NewLegIdxStream := New(PIndexedJImmyStream, init('LEG.IDX',TLegIdxSize)); end;

function NewControlIdxStream : PStream; far;
begin	NewControlIdxStream := New(PIndexedJImmyStream, init('CONTROL.IDX',TControlIdxSize)); end;

function NewCarNumberIdxStream : PStream; far;
begin	NewCarNumberIdxStream := New(PIndexedJImmyStream, init('CARS.IDX',TCarIdxSize)); end;

function NewCarPosIdxStream : PStream; far;
var S : PIndexedJimmyStream;
begin
	New(S, init('CARPOS.IDX',TCarIdxSize));
	S^.NonHoleIndex := True;
	NewCarPosIdxStream := S;
end;

function NewCarClassPosIdxStream : PStream; far;
var S : PIndexedJimmyStream;
begin
	New(S, init('CARPOSC.IDX',TCarIdxSize));
	S^.NonHoleIndex := True;
	NewCarClassPosIdxStream := S;
end;

function NewTimingsTableStream : PStream; far;
begin
	NewTimingsTableStream := New(PTableStream, init('TIMINGS.TBL',256));
end;

function NewRouteIDIdxStream : PStream; far;
begin
	NewRouteIDIdxStream := New(PIDIndexFile, init(srControl, opRoute));
end;



var RVUSecs : longint;

procedure RegularViewUpdate; far;
begin
	if (RallySetup.ViewInt<>0) and (TimeNow.Secs>RVUSecs + RallySetup.ViewInt) then begin

		if (ProgramStatus.KickOut<>0) and (ProgramStatus.KickOut<>TerminalNo) then begin
			CloseAllViews;
			NewMessageBox('MAINTENANCE','CLEARING VIEWS AND LEAVING'#13#10'PLEASE WAIT 5 MINUTES BEFORE RE-ENTERING',
										mfWarning,hcNoContext);
			halt(0);
		end;

		Message(Desktop, evBroadCast, cmUpdateAll,nil); {acts as a warning for the cmredraw - ie dialog boxes to cancel}
		Message(Desktop, evBroadCast, cmRedraw,nil);
		RVUSecs := TimeNow.Secs;
	end;
end;

procedure RallyStartup; far;
begin
	FileAdmin(fiRouteIDIdx)^.LogOn;
	FileAdmin(fiTimingsTable)^.LogOn;
end;

procedure RallyShutdown; far;
begin
	FileAdmin(fiRouteIDIdx)^.LogOff;
	FileAdmin(fiTimingsTable)^.LogOff;
end;




{**************************************************************
 ***            INITIALISATION                              ***
 **************************************************************}
{$IFDEF RallyPress}
const lsType = 0;
{$ELSE}
const lsType = lsDesktop;
{$ENDIF}

begin
{$IFDEF fixit} writeln('Rally...'); {$ENDIF}

	NewFileAdmin(fiLegIdx, 				'Leg Index',NewLegIdxStream);
	NewFileAdmin(fiControlIdx, 		'Control Index', NewControlIdxStream);{}
	NewFileAdmin(fiCarNumberIdx, 	'Car By Number Index', NewCarNumberIdxStream);{}
	NewFileAdmin(fiCarRacePosIdx, 'Car By Position Index', NewCarPosIdxStream);{}
	NewFileAdmin(fiTimingsTable,		'Timings Table',		NewTimingsTableStream);
	NewFileAdmin(fiRouteIDIdx,		'Route ID Index',		NewRouteIDIdxStream);

	New(SCodeCollection[scCarClass], Init('CARCLASS.SC', 'Rally Car Classes', StdScodeCreator));
	New(SCodeCollection[scCarEvent], Init('CAREVENT.SC', 'Rally Car Events', StdScodeCreator));
	New(SCodeCollection[scCPType], init('CPTYPE.SC', 'Control point types', StdSCodeCreator));

	RegisterJimmy(RLeg, 		CreateLeg, 			lsLeg, '~L~eg');
	RegisterJimmy(RControl, CreateControl, 	lsControl, '~C~ontrol');
	RegisterJimmy(RCar, 		CreateCar, 			lsCar, '~C~ar');
	RegisterJimmy(RTiming, 	CreateTiming, 	lsType, '~T~iming');
	RegisterJimmy(RPenalty, CreatePenalty, 	lsType, '~P~enalty');

	{set up timing pointer matrix}
	RegisterTask(StartupTasks, 10, @RallyStartup);
	RegisterTask(ShutdownTasks, 10, @RallyShutDown);{}

	RegisterTask(IdleTasks, 0, @RegularViewUpdate);
	RVUSecs := TimeNow.Secs;
end.
