{*********************************************************************
 ***                                                               ***
 ***                       FAX DEVICES                             ***
 ***                                                               ***
 *********************************************************************}
{$i COMPFLGS}
{There are two ways of faxing something.  Some fax systems take data from
an lpt port as a TSR as if it were a printer.  Others you write an ascii file, then
fax the file complete using a specific sending program.  Some may (?) want
both, though I doubt it a bit... So provisions are made for all.  There is
a faxstream to use instead of the printstream, which will write to the appropriate
device (or a file called KAMELEON in the outbox path if no device name is
specified), then it calls the sender program (if any specified) on EndPrint}

unit faxes;

INTERFACE

uses
{$IFDEF WINDOWS}
	wui,	{windows}
{$ELSE}
	tuiedit, {text}
{$ENDIF}
	devices, objects, files;

const
	FaxDeviceExt = 'FXD';
	siFaxDeviceName = 'FAX';

type
	{--- Fax Device ----------------------}
	{At the mo', just provides a typecast for global var below}
	PFaxDevice = ^TFaxDevice;
	TFaxDevice = object(TDeviceStream)
		procedure SetFaxNumber; virtual;
	end;

	{--- Fax To Port ---------------------}
	{Simple derivitive of TDeviceStream that uses the DosFIleName, etc to	output}
	PFaxToPortDevice = ^TFaxToPortDevice;
	TFaxToPortDevice = object(TFaxDevice)

		DefFilter : string[8];
		DefPaper : string[8];
		BracketCommands : boolean; {whether this device can use >> commands, eg >>TO}
		GGSFIleName : PString;

		constructor Init;
		destructor Done; virtual;
		procedure SetDefaultCodes;virtual;

		procedure SetFaxNumber; virtual;

		{Overriders}
		procedure StartPrint(NHeaderName1,NHeaderName2 : FNameStr); virtual; {sends telephone number}
		procedure EndPrint; virtual; {End ordinary print - closes file}
		procedure AddEditFields(EditBox : PEditBox); virtual;
		procedure Send; virtual;

		{load/store}
		constructor Load(var S : TDataStream);
		procedure Store(var S : TDataStream);
	end;


{General purpose Just Do It procedure}
procedure FaxFile(FileName,FaxNumber : string);

{for maintenance/editing}
function EditFaxDEvice(var FullFileName : FNameStr; const Ext : string) : word;

var
	Fax : PFaxDevice;
	FaxName : string[8];

IMPLEMENTATION

uses  tuimsgs,
			printers,
			dosutils,
			kamsetup,
			tui,
			help,
			minilib,
			autodial,
			tasks, global, inpfname; {setup}


{***************************************************************
 ***                                                         ***
 ***                  FAX ROOT DEVICE                        ***
 ***                                                         ***
 ***************************************************************}
procedure TFaxDevice.SetFaxNumber;
begin end;


{***************************************************************
 ***                                                         ***
 ***               FAX-TO-PORT DEVICE                        ***
 ***                                                         ***
 ***************************************************************}
constructor TFaxToPortDevice.init;
begin
	inherited Init('Fax Device','LPT3');
	BracketCommands := True;
	GGSFileName := nil;
end;

destructor TFaxToPortDevice.Done;
begin
	if GGSFileName<>nil then DisposeStr(GGSFileName);
	inherited Done;
end;


procedure TFaxToPortDevice.AddEditFields(EditBox : PEditBox);
var R : TRect;
begin
	with EditBox^ do begin
		HelpCtx := hcEditFax;
		GrowTo(40, 14);

		{skip fields to end of devicestream}
		InsTitledField(12,  2, 8, 1, '~P~ort', New(PInputELine, init(R,8)));

		Insert(New(PSkipBytes, init(sizeof(TDeviceStream)-DataSize)));

		InsTitledField(12,  4, 8, 1, '~F~ilter',
															New(PinputFName, Init(R, 8, PrinterFilterExt, PrintersPath, True, EditPrinterFilter)));
		InsTitledField(12,  5, 8, 1, '~P~aper', 	New(PinputFName, Init(R, 8, PaperExt, PrintersPath, True, EditPaper)));

		R.XYLD(12, 7, 3, 1); Insert(New(PInputBoolean, init(R))); AddLabel('>>TO',Current);

		R.XYLD(12, 9,25, 1); Insert(New(PInputPStr, init(R,70))); AddLabel('GGS File',Current);
	end;
end;

{==== START PRINT =====================}
procedure TFaxToPortDevice.StartPrint;
begin
	if not IsOpen then Open;

	{set fax number}
	if  BracketCommands then SetFaxNumber;

	inherited StartPrint(NHeaderName1, NHeaderName2);
end;

{*************************************************************************
 ***                      SET FAX NUMBER                               ***
 *************************************************************************}
{Informs the (3rd party) fax device of the telephone number to dial.  Two
>> commands are used:

>>TO name, company, fax number
>>INFO comment, heading, log notes, bitmap, size

Be aware that specifying name/company implies a cover sheet (so they are
left blank below), and I think the >>INFO command is ignored in Faxworks
lite.  Really, the above two lines ought to be user configurable, so that
any kind of preparation file can be made up, rather than just the GGS-specific
one for faxworks}

procedure TFaxToPortDevice.SetFaxNumber;
var WasOpen : boolean;
		FaxNumber : string;
		Country : string;
		GGSFile : text;

begin
	if not BracketCommands then begin
		ProgramWarning('Fax Device '+Name+' does not take >>TO commands'#13#10
											+'Cannot set fax number',hcSetFax);
		exit;
	end;

	{work on fax number}
	FaxNumber := FormCodes^.QDecodeStr('<FAXTO.NUM>'); {use qdecodestr as there may be other <> codes inside this one}

	if (FaxNumber = '') or (FaxNumber='<FAXTO.NUM>') then begin
		ProgramWarning('Fax number unknown, could not inform fax/modem', hcSetFax);
		exit;
	end else begin
		Country := FormCodes^.QDecodeStr('<FAXTO.CNTRY>');

		FaxNumber := GetLine(SplitTelNums(FaxNumber),1); {get first of split - should really present menu...}
		FaxNumber := MakeStdTelNum(FaxNumber, Country);
		FaxNUmber := MakeLocalTelNum(FaxNUmber);
		FaxNumber := MakeModemString(FaxNUmber);

		if (GGSFileName<>nil) and (delspaceR(GGSFileName^)<>'') then begin
			{--- Faxworks GGS File ---------------}
			{like a script file}
			{write commands to a GGS FIle, eg FaxWorks, ie a file that contains
			the >>TO commands}
			Assign(GGSFIle, GGSFIleName^);
			{$I-}
			Rewrite(GGSFIle);
			if IOResult=0 then begin
				System.Writeln(GGSFIle, FormCodes^.QDecodeStr('>>TO=,,'+FaxNumber));
				System.Writeln(GGSFile, FormCodes^.QDecodeStr('>>INFO=,,<FAXTO.NAME> (from Kameleon),,2'));
				System.Close(GGSFIle);
				if IOResult<>0 then
					ProgramWarning('Could not write to GGS File '+GGSFIleName^+CRLF
												+'Could not inform fax/modem of fax number', hcSetFax);
			end else
				ProgramWarning('Could not create GGS File '+GGSFileName^+CRLF
												+'Could not inform fax/modem of fax number', hcSetFax);
			{$I+}

		end else begin
			{write commands to port}
			WasOPen := IsOpen;
			if not IsOpen then Open;

			writeCodedStr('>>TO=,,'+FaxNumber+CRLF);
			if not WasOpen then Close;
		end;
	end;

end;




{==== END PRINT =====================}
procedure TFaxToPortDevice.EndPrint;
{ar F : text;{}
begin
	{Can we think of a way of exporting the fax number to clipboard/whatever
	for 3rd party fax program to use?  For now just write to c:\faxnum.$$$}
{	Assign(F, 'C:\FAXNUM.$$$');
	Rewrite(F);
	System.Writeln(F, FaxToNumber);
	System.Close(F);{}

	inherited EndPrint;
	SetPaper(GetPaper(PrintersPath+DefPaper+'.'+PaperExt));
			{Do on .init and at endprint, so that it can be overriden before startprint}
	Close; {close channel to release for capturer}
end;

{Automatic - by the time send is called, the info is already into the port}
procedure TFaxToPortDevice.Send;
begin end;


constructor TFaxToPortDevice.Load(var S : TDataStream);
var Ver,B : byte;
begin
	S.Read(Ver, 1);
	case Ver of
		1 : begin
			{pre v4.3 - v4.2?}
			inherited Load(S);
			DefFilter := S.ReadStr;
			DefPaper := S.ReadStr;
			BracketCommands := True;
			GGSFIleName := nil;
		end;
		2 : begin
			{v4.3}
			inherited Load(S);
			DefFilter := S.ReadStr;
			DefPaper := S.ReadStr;
			S.Read(B, 1);
			BracketCommands := (B and $01)>0;
			GGSFileName := nil;
		end;
		3 : begin
			{v4.3 - added GGSFileName}
			inherited Load(S);
			DefFilter := S.ReadStr;
			DefPaper := S.ReadStr;
			S.Read(B, 1);
			BracketCommands := (B and $01)>0;
			GGSFileName := NewStr(S.ReadStr);
		end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not understood'#13#10'FaxToPortDevice.Load',mfError,hcNoContext);
		fail;
	end;

	SetFilter(GetFilter(PrintersPath+DefFilter+'.'+PrinterFilterExt));
	SetPaper(GetPaper(PrintersPath+DefPaper+'.'+PaperExt));
end;

procedure TFaxToPortDevice.Store(var S : TDataStream);
var Ver,B : byte;
begin
	Ver := 3; S.Write(Ver, 1);
	inherited Store(S);
	S.WriteStr(@DefFilter);
	S.WriteStr(@DefPaper);
	if BracketCommands then B := 1 else B := 0;
	S.Write(B,1);
	S.WriteStr(GGSFIleName);
end;

procedure TFaxToPortDevice.SetDefaultCodes;
begin
	inherited SetDefaultCodes;
	with FormCodes^ do begin
		{set to asterisks, which tells the fax device via >>TO to use defaults}
		SetStr('FAXTO.NAME','*');
		SetStr('FAXTO.NUM','*');
		SetStr('FAXTO.CNTRY',ProgramSetup.Get(siCountry,'UK'));
	end;
end;

{***************************************************************
 ***                                                         ***
 ***               JUST DO IT AND FAX IT                     ***
 ***                                                         ***
 ***************************************************************}

procedure FaxFile(FileName,FaxNumber : string);
begin
	if Fax=nil then begin
		PauseMessage('Faxing File','No Fax defined!'#13#10'Check Maintenance, Setup',hcSetFax);
		exit;
	end;

	with Fax^ do begin
		with FormCodes^ do begin
			SetPrefix('');
			SetStr('FAXTONUM', FaxNumber);{}
		end;
		AutoSend := True;
		StartPrint('',''); {start with no headers}
		PrintForm(FileName);
		EndPrint;
	end;

end;


{************************************
 ***       STREAMS                ***
 ************************************}
const
	RFaxToPortDevice : TStreamRec = (
		ObjType : srFaxToPortDevice;
		VmtLink : Ofs(TypeOf(TFaxToPortDevice)^);
		Load : @TFaxToPortDevice.Load;
		Store : @TFaxToPortDevice.Store
	);

{===== Fax DEVICE =============}
function CreateFax(P : pointer) : pointer; far;
begin CreateFax := New(PFaxToPortDevice, init); end;

function EditFaxDEvice(var FullFileName : FNameStr; const Ext : string) : word;
var	Control : word;
		P : Boolean;

begin
	Control := EditDevice(FullFileName, Ext, CreateFax);

	if Control = cmOK then begin
		{check if we're editing the actual current driver, and re-set if so}
		if GetFileName(FullFileName) = FaxName+'.'+FaxDeviceExt then begin
			P := Fax^.Permanant; {store...}
			Fax^.Permanant := False;
			dispose(Fax, done);
			Fax := PFaxToPortDevice(GetObjFromFile(FullFIleName)); {so it reloads filters, etc}
			Fax^.Permanant := P; {...restore, jic not permanant before}
		end;
	end;

	EditFaxDevice := Control;
end;



{************************************
 ***       INIT/SHUTDOWN          ***
 ************************************}

procedure StartUpFax; far;
var Device : PDeviceStream;
begin
	FaxName := ProgramSetup.Get(siFaxDeviceName,'');
	Fax := New(PFaxToPortDevice, init);
	if FaxName<>'' then begin
		Device := GetDevice(PrintersPath+FaxName+'.'+FaxDeviceExt);
		if Device<>nil then begin
			dispose(Fax, done);
			Fax := PFaxDevice(Device);
		end else begin
			ProgramWarning('Could not load Fax Driver'#13
											+PrintersPath+FaxName+'.'+FaxDeviceExt+#13
											+'Check Fax Setup in Maintenance',
											hcFileNotFoundMsg);
		end;
	end;
	Fax^.Permanant := True;
end;

procedure ShutDownFax; far;
begin
	if Fax<>nil then begin
		Fax^.Permanant := False;
		dispose(Fax, Done);
		Fax := nil;
	end;
end;  {logs off user codes, etc}


procedure SetFax; far;
begin
	Fax^.Permanant := False;
	SetDevice(PDeviceStream(Fax), 'SELECT FAX', FaxDeviceExt, siFaxDeviceName, EditFaxDevice);
	Fax^.Permanant := True;
end;


begin
{$IFDEF fixit} writeln('Initialising fax unit'); {$ENDIF}

	RegisterType(RFaxToPortDevice);

	Fax := nil;
	FaxName := '';

	RegisterTask(DesktopTasks, cmSetFax, @SetFax);{}

	{don't do if install program as it might get upset if .cfg exists}
	{$IFNDEF Install}
		RegisterTask(StartUpTAsks, 100, @STartupFax);
		RegisterTask(ShutDownTAsks, 0, 	@ShutDownFax);
	{$ENDIF}
end.


