++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++
++ This is a non-functional version of the whatif.ws file that
++ is used as meta-file used by wiwsd to generate all web-services
++
++ Comments in ++ boxes are not part of the whatif.ws meta-file
++ but are only added as documentation
++
++ Some HTML has been modified, eg, <BR> has been converted into (BR)
++ to keep this file readable. In the real thing it is <BR>, of course.
++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

MODULE WhatifWS;
//
// Description of WHAT IF web services
// comments start with // and continue until end of line
//
// all WHAT IF scripts will be wrapped inside the following lines:
//
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++
++ The following 7 lines hold WHAT IF commands. The documentation for
++ these commands can be found in the WHAT IF writeup
++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//	iniall
//	setwif 1052 0
//	setwif 1012 0
//	getmol  y y
//	0
//	... the actual commands
//	$touch DONE
//
// The 'touch DONE' is needed to synchronize output with
// the calling application.
//
//
//	Testing this script is done using wiwsd and the -t switch
//	i.e. in the directory with this script run 'wiwsd -t'
//
// ==============================================================
// ==                                                          ==
// ==	Data types                                             ==
// ==                                                          ==
// ==============================================================
//
//	FLOAT			= real
//	INTEGER			= integer (-2e9 .. 2e9)
//	UNSIGNED		= integer 0 .. 4e9
//	STRING			= series of characters
//	BOOLEAN			= TRUE of FALSE
//
//
// ==============================================================
// ==                                                          ==
// ==	Explanation                                            ==
// ==                                                          ==
// ==============================================================
//
// Every server must return a thing for which the TYPE has been 
// defined up-front. The PROCEDURE name will in the WSDL be the 
// thing the outside world will see as a 
//
//TYPE
//	to_be_returned = RECORD
//		residue:          Residue;
//		rotamer_table:    ARRAY [20] OF FLOAT;
//		quality_estimate: FLOAT;
//	END;
// Inside to_be_returned we need to define what in the BEGIN-END of the 
// PROCEDURE is added to info.
// These are mainly the data-types of what the web-services can return.
// They 'contain' either intrinsic types (FLOAT, BOOLEAN, STRING, UNSIGNED,
// INTEGER) or reusable TYPEs (Residue, Atom, Coordinate, Dot, Line, TorsAng)
//
//PROCEDURE Name_of_the_service(id: STRING; VAR response: ARRAY OF to_be_returned);
//
//VAR
//	line: 	STRING;
//	fields:	ARRAY OF STRING;
//	likely: ARRAY OF STRING;
//	i:	INTEGER;
//	info: 	to_be_returned;
//	whatif:	FILE;
//
//BEGIN
//	whatif := WHATIF("wsvrot", id);
//	WHILE READLINE(whatif, line) DO
//		SPLIT(line, '|', fields);                              ! Split WHAT IF line on |
//		ReadResidue(fields[1], info.residue);
//		SPLIT(fields[2], ';', likely);
//		FOR i := 1 TO 20 DO	
//			info.rotamer_table[i] := CAST(FLOAT, likely[i]);
//		END;
//		info.quality_estimate := CAST(FLOAT, fields[3]);
//		PUSH(response, info);
//	END;
//END ShowLikelyRotamers;
//
//
// ==============================================================
// ==                                                          ==
// ==	RECORDs                                                ==
// ==                                                          ==
// ==============================================================
//
// RECORDs are used in composite records that are the data-types 
// of the web-serices, or directly as data-types in the web-serices.
//

TYPE
//
// Residue and Atom are biologist-friendly indicators of which residue
// or atom is being used, while FullResidue and FullAtom are records
// from which ATOM cards in a PDB file can be reconstructed.
//
	Residue	= RECORD
		number: 	INTEGER;
		type: 		STRING;
		pdb_number: 	INTEGER;
		insertion_code: STRING;
		chain: 		STRING;
		model_number: 	INTEGER;
	END;
	
	Atom = RECORD
		residue: 	Residue;
		atom_type: 	STRING;
		alternate_atom: STRING;
	END;
	
	FullAtom = RECORD
		PDBx_atom_site: 		INTEGER;
		PDBx_Cartn_x: 			FLOAT;
		PDBx_Cartn_y: 			FLOAT;
		PDBx_Cartn_z: 			FLOAT;
		PDBx_B_iso_or_equiv: 		FLOAT;
		PDBx_auth_asym_id: 		STRING;
		PDBx_auth_atom_id: 		STRING;
		PDBx_occupancy: 		FLOAT;
		PDBx_type_symbol: 		STRING;
	END;

	FullResidue = RECORD
		number: 	INTEGER;
		type: 		STRING;
		pdb_number: 	INTEGER;
		insertion_code: STRING;
		chain: 		STRING;
		model_number: 	INTEGER;
		atoms:		ARRAY OF FullAtom;
	END;

	Empty = RECORD
		nothing:	STRING;
	END;
//
// Dot and Line are records used by WHAT IF to present the user of
// the web-service with graphical objects. Dots are positions in space
// that have a colour; lines are two positions in space and a colour 
// for the line between them.
// Colours are on the Hue-wheel:
//   0 = Blue
//  60 = Purple
// 120 = Red
// 150 = Orange
// 180 = Yellow
// 240 = Green
// 300 = Light Green
// 360 = Blue (is zero again)
//
	Coordinate = RECORD
		x, y, z: 	FLOAT;
	END;

	Dot = RECORD
		coordinate: 	Coordinate;
		hue: 		UNSIGNED;
	END;
	
	Line = RECORD
		coordinate_from, coordinate_to: Coordinate;
		hue: 		UNSIGNED;
	END;

	SurfaceDot =  RECORD
		atom: 		Atom;
		dots: 		ARRAY OF Dot;
	END;

	Rotamer = RECORD
		lines:		ARRAY OF Line;
		hue: 		UNSIGNED;
	END;
//
// WHAT IF returns angles in degrees. Torsion angles are defined using the
// IUPAC IUB nomenclature rules that are now used in nearly all molecular
// graphics packages.
//
	TorsAng = 	RECORD
		phi,psi,omega: 	FLOAT;
		chi: 		ARRAY [4] OF FLOAT;
	END;

	TorsAngBB = 	RECORD
		phi,psi,omega: 	FLOAT;
	END;

	ResidueTorsionInfo = RECORD
		residue: 	Residue;
		angles: 	TorsAng;
	END;

	ResidueTorsionInfoBB = RECORD
		residue: 	Residue;
		angles: 	TorsAngBB;
	END;

	ResidueAngleInfoTau = RECORD
		residue: 	Residue;
		tau: 		FLOAT;
	END;
//
// WHAT IF has extensive symmetry facilities available. Some return symmetry 
// related copies of things in the PDB file. Others, like this one, just count
// things or give a yes-or-no like answer to the has-symmetry-contact question
//

	SymmetryContactInfo = RECORD
		residue: 	Residue;
		contact_count: 	INTEGER;
	END;

//
// In WHAT IF solvent accessibilities are calculated on a per-atom basis. The
// accessibility of a whole ligand or residue is than the sum of the atomic
// accessibility values.
// Sometimes it is useful to know if an atom is OK or not. An atom is called
// not OK if, for example, the occupancy value is too low. How low is too low
// should then be checked per option.
//
	AtomAccessibilityInfo =	RECORD
		atom: 		Atom;
		accessibility: 	FLOAT;
	END;

	AtomAccessibilityInfoPlus = RECORD
		atom: 		Atom;
		accessibility: 	FLOAT;
		status: 	BOOLEAN;
	END;

	ResidueAccessibilityInfo = RECORD
		residue: 	Residue;
		access_bb:   	FLOAT;
		access_sch:  	FLOAT;
		access_tot:  	FLOAT;
		status:      	BOOLEAN;
	END;

	EntityAccessibilityInfo = RECORD
		residue: 	Residue;
		access:   	FLOAT;
		status:      	BOOLEAN;
	END;

	VacuumAccessibilityInfo = RECORD
		residue: 	Residue;
		NormalAcc:   	FLOAT;
		VacuumAcc:  	FLOAT;
	END;
//
// Cysteine bridges always are something special. So too for WHAT IF. Therefore 
// a series of records allows you to obtain information about cysteines and
// cysteine bridges.
//
	CysteineBridgeInfo = RECORD
		cysteine: 	ARRAY[2] OF Residue;
	END;

	CysteineFreeInfo = RECORD
		cysteine: 	Residue;
	END;

	PDBXMLout = RECORD
		residue: 	FullResidue;
	END;

	CysteineMetalInfo = RECORD
		cysteine: ARRAY[2] OF Residue;
	END;

	CysteineTorsionsInfo = RECORD
		cysteine: ARRAY[2] OF Residue;
		angles: ARRAY [5] OF FLOAT;
	END;
//
// Hydrogenbond related
//
	HydrogenBondInfo = RECORD
		atom:                   ARRAY [2] OF Atom;
		hydrogenbond_parameter: ARRAY [4] OF FLOAT;
	END;
//
// Atomic contact related
//
	BumpInfo = RECORD
		atom: ARRAY [2] OF Atom;
		bump_severity: FLOAT;
	END;

	LigandContactInfo = RECORD
		atom: ARRAY [2] OF Atom;
		contact_distance: FLOAT;
	END;

	NucleicAcidContactInfo = RECORD
		atom: ARRAY [2] OF Atom;
		contact_distance: FLOAT;
	END;

	SaltBridgeInfo = RECORD
		atom: ARRAY [2] OF Atom;
		distance: FLOAT;
	END;
//
// Very special things
//
	LikelyRotamerInfo = RECORD
		residue: Residue;
		rotamer_table: ARRAY [20] OF FLOAT;
		quality_estimate: FLOAT;
	END;
//
// A series of options works highly symmetric from an output 
// point of view, and return the user a residue and a value.
// These types are used for those web-services.
//
	ResiduePlusValue = RECORD
		residue:  	Residue;
		value  :  	FLOAT;
	END;

	ResiduePlusCount = RECORD
		residue:  	Residue;
		count  :  	INTEGER;
	END;

	ResiduePlusValuePlusAtoms = RECORD
		residue:  	Residue;
		value  :  	FLOAT;
		atom   :  	ARRAY OF Atom;
	END;

	ResiduePlusValuePlusResidues = RECORD
		residue:  	Residue;
		value  :  	FLOAT;
		residues:  	ARRAY OF Residue;
	END;
//
// Some options have a more global nature. The SequenceInfo record is mainly
// needed for people who call WHAT IF web-services and get sequence harmonisation
// problems because WHAT IF treats this one N atom at the C-terminal end
// of the protein different from your software.
// If you first use the service that returns the SequenceInfo, you can know
// in advance what future WHAT IF service calls will return for this same
// PDB file.
//
	SequenceInfo = RECORD
		residue:        Residue;
	END;

	DSSPvalue = RECORD
		residue: 	Residue;
		value: 		STRING;
	END;

	OutStrings2 = RECORD
		strings: 	ARRAY[2] OF STRING;
	END;

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++
++ TestEmpty is a special web-service that returns no results, except
++ that it tells the user that it ran happily (if all is OK). This 
++ service can be used to see if the services are up and running. 
++
++ TestEmpty will become more important if, one day, multiple copies
++ of these web-services are available in the internet. In that case
++ 'your' software can first poll the web-services and than skip one 
++ set that is down at that moment.
++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


PROCEDURE TestEmpty(id: STRING; VAR response: ARRAY OF Empty);
DOCUMENTATION
	command = 	"WEMPTY";
	summary = 	"Should return (very quickly) and nil-response";
	description = 	"This web-service calls all functions and routines that all WHAT IF web-services have in common, but  " +
			"otherwise does nothing. So it is a good test to see if the web-services machine, and WHAT IF, are up and running(BR). " +
			"If any web-service fails then please try to run this TestEmpty web-service before mailing us. The correct response " +
			"for this service is that it just gives you a TestEmptyResponse. If TestEmpty fails then " +
			"most likely all services are down, or worse, the whole computer; and, most likely, we are already working " +
			"at a solution. If this TestEmpty service fails, please try again the next " +
			"(European) morning and only mail us if it still is down by that time.(BR) " +
			"If this TestEmpty service happily returns an empty response, while some other service fails, then please " +
			"mail Gert Vriend (vriend@cmbi.ru.nl) the name of the failing service, the " +
			"corresponding WHAT IF command, and the PDB file that failed. (Preferably the 4-letter PDB identifier, " +
			"but if the file is not in the PDB, mail the whole PDB file as an attachement.(BR) " +
			"As this is the first service, which means that this documentation is on top of the help page, the general info " +
			"for all services will be placed here...(BR) ";
VAR
	info:		Empty;
	whatif:		FILE;
BEGIN
	whatif := WHATIF("wempty", id);
END TestEmpty;
	
//
// ==============================================================
// ==                                                          ==
// ==	Reusable PROCEDUREs                                    ==
// ==                                                          ==
// ==============================================================
//

PROCEDURE ReadResidue(line: STRING; VAR residue: Residue);
VAR
	fields: ARRAY OF STRING;
BEGIN
	SPLIT(line, ';', fields);
	residue.number	 		:= CAST(INTEGER, fields[1]);
	residue.type 			:= fields[2];
	residue.pdb_number  		:= CAST(INTEGER, fields[3]);
	residue.insertion_code  	:= fields[4];
	residue.chain	 		:= fields[5];
	residue.model_number		:= CAST(INTEGER, fields[6]);
END ReadResidue;

PROCEDURE ResidueToFullResidue(residue: Residue; VAR fullResidue: FullResidue);
BEGIN
	fullResidue.number := residue.number;
	fullResidue.type := residue.type;
	fullResidue.pdb_number := residue.pdb_number;
	fullResidue.insertion_code := residue.insertion_code;
	fullResidue.chain := residue.chain;
	fullResidue.model_number := residue.model_number;
END ResidueToFullResidue;

PROCEDURE ReadAtom(line: STRING; VAR atom: Atom);
VAR
	fields:	ARRAY OF STRING;
BEGIN
	SPLIT(line, ';', fields);
	atom.residue.number		:= CAST(INTEGER, fields[1]);
	atom.residue.type 		:= fields[2];
	atom.residue.pdb_number  	:= CAST(INTEGER, fields[3]);
	atom.residue.insertion_code  	:= fields[4];
	atom.residue.chain	 	:= fields[5];
	atom.residue.model_number	:= CAST(INTEGER, fields[6]);

	atom.atom_type			:= fields[7];
	atom.alternate_atom		:= fields[8];
END ReadAtom;

PROCEDURE ReadCoordinate(line: STRING; VAR coordinate: Coordinate);
VAR
	fields: ARRAY OF STRING;
BEGIN
	SPLIT(line, ',', fields);
	coordinate.x 			:= CAST(FLOAT, fields[1]);
	coordinate.y 			:= CAST(FLOAT, fields[2]);
	coordinate.z 			:= CAST(FLOAT, fields[3]);
END ReadCoordinate;

PROCEDURE ReadDot(line: STRING; VAR dot: Dot);
VAR
	fields: ARRAY OF STRING;
BEGIN
	SPLIT(line, ';', fields);
	dot.coordinate.x 		:= CAST(FLOAT, fields[1]);
	dot.coordinate.y 		:= CAST(FLOAT, fields[2]);
	dot.coordinate.z 		:= CAST(FLOAT, fields[3]);
	dot.hue 			:= CAST(UNSIGNED, fields[4]);
END ReadDot;

//PROCEDURE ReadLine(line: STRING; VAR line: Line);
//VAR
//	fields: ARRAY OF STRING;
//BEGIN
//	SPLIT(line, ';', fields);
//	line.coordinate_from.x 		:= CAST(FLOAT, fields[1]);
//	line.coordinate_from.y 		:= CAST(FLOAT, fields[2]);
//	line.coordinate_from.z 		:= CAST(FLOAT, fields[3]);
//	line.coordinate_to.x 		:= CAST(FLOAT, fields[4]);
//	line.coordinate_to.y 		:= CAST(FLOAT, fields[5]);
//	line.coordinate_to.z 		:= CAST(FLOAT, fields[6]);
//	line.hue 			:= CAST(UNSIGNED, fields[7]);
//END ReadLine;
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++
++ UploadPDB and DownloadPDB are not 'independent' WHAT IF webservices,
++ but they are needed to allow 'your' software to communicate with
++ the other web-services.
++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//
// ==============================================================
// ==                                                          ==
// ==	UploadPDB.                                             ==
// ==                                                          ==
// ==============================================================
//
// Saves the uploaded PDB file and returns a new, unique ID for use in the other functions.
//

PROCEDURE UploadPDB(pdb: STRING; VAR response: STRING);
DOCUMENTATION
	command = 	"na";
	summary =  	"Uploading / using PDB files";
	description = 	"To use WHAT-IF functions on your own PDB files, you have to upload " +
			"them first. The result of this function is an ID that you can use in " +
			"further calls to wiwsd services.";

BEGIN
	response := SAVEFILE(pdb);
END UploadPDB;

//
// ==============================================================
// ==                                                          ==
// ==	DownloadPDB.                                           ==
// ==                                                          ==
// ==============================================================
//
// Retrieves a PDB file by ID.
//

PROCEDURE DownloadPDB(id: STRING; VAR response: STRING);
DOCUMENTATION
	command = 	"na";
	summary =  	"Downloading PDB files";
	description = 	"You can use this function to download PDB files. If you use the regular " +
			"four letter code, you can retrieve standard PDB files, if you use the ID as " +
			"returned by UploadPDB you can download a previously uploaded PDB file.";
BEGIN
	response := LOADFILE(id);
END DownloadPDB;

//
// ==============================================================
// ==                                                          ==
// ==	A few selected web-service meta entries:               ==
// ==                                                          ==
// ==============================================================
//

PROCEDURE GetSurfaceDots(id: STRING; VAR response: ARRAY OF SurfaceDot);
DOCUMENTATION
	command = 	"WSVDOT";
	summary = 	"Surface accessibility dots for graphics";
	description = 	"Calculates the positions of dots that are homogeneously distributed over the surface " +
			"of the molecule (waters are not incorporated in the calculation).(BR) " +
++ 			some documentation removed here.
VAR
	line:		STRING;
	fields:		ARRAY OF STRING;
	dot:		Dot;
	dots:		ARRAY OF Dot;
	sd:		SurfaceDot;
	atom:		Atom;
	lastResidueNr:	INTEGER;
	whatif:		FILE;

BEGIN
	lastResidueNr := 0;
	whatif := WHATIF("wsvdot", id);
	WHILE READLINE(whatif, line) DO
		SPLIT(line, '|', fields);
		ReadAtom(fields[1], atom);
		ReadDot(fields[2], dot);
		IF (atom.residue.number # lastResidueNr) THEN
			IF lastResidueNr # 0 THEN PUSH(response, sd) END;
			sd.atom := atom;
			sd.dots := dots;	// initialize dots array 
			lastResidueNr := atom.residue.number
		END;
		PUSH(sd.dots, dot);
	END;
	PUSH(response, sd);
END GetSurfaceDots;



PROCEDURE AtomAccessibilitySolvent(id: STRING; VAR response: ARRAY OF AtomAccessibilityInfo);
DOCUMENTATION
	command = 	"WSVACC";
	summary = 	"Surface accessibility";
	description = 	"Returns for each atom in the input file its solvent accessibility " +
			"in Ångstrøm2. " +
			"Waters are neglected by this service.(BR) " +
			"This service returns the "accessible surface". The " +
			"AtomAccessibilityMolecular service, that runs the " +
			"WHAT IF option WSVACM, will calculate the "accessible molecular surface". " ;
VAR
	line:		STRING;
	fields:		ARRAY OF STRING;
	info:		AtomAccessibilityInfo;
	whatif:		FILE;
BEGIN
	whatif := WHATIF("wsvacc", id);
	WHILE READLINE(whatif, line) DO
		SPLIT(line, '|', fields);
		ReadAtom(fields[1], info.atom);
		info.accessibility := CAST(FLOAT, fields[2]);
		PUSH(response, info);
	END;
END AtomAccessibilitySolvent;



PROCEDURE AtomAccessibilityMolecular(id: STRING; VAR response: ARRAY OF AtomAccessibilityInfo);
DOCUMENTATION
	command = 	"WSVACM";
	summary = 	"Accessibile molecular service";
	description = 	"Returns for each atom in the input file its accessibile molecular surface in Ångstrøm2. " +
			"Waters are neglected by this service.(BR) " +
			"This service returns the "accessible molecular surface". The AtomAccessibilityMolecular service, that runs the " +
			"WHAT IF option WSVACC, will calculate the "accessible surface".(BR) " +
			"The computation method is described in: " ;
VAR
	line:		STRING;
	fields:		ARRAY OF STRING;
	info:		AtomAccessibilityInfo;
	whatif:		FILE;
BEGIN
	whatif := WHATIF("wsvacm", id);
	WHILE READLINE(whatif, line) DO
		SPLIT(line, '|', fields);
		ReadAtom(fields[1], info.atom);
		info.accessibility := CAST(FLOAT, fields[2]);
		PUSH(response, info);
	END;
END AtomAccessibilityMolecular;



PROCEDURE SymShellOneXML (id: STRING; VAR response: ARRAY OF FullResidue);
DOCUMENTATION
	command     = 	"WSVSS1";
	summary     = 	"Produces a shell of symmetry contacting residues.";
	description = 	"All residues that can be generated from the assymmetric unit by " +
			"applying the space group symmetry matrices and whole cell translations " +
			"and that make a contact with any residue in the asymmetric unit are " +
			"calculated and returned to the caller.(BR) " +
			"Two residues are said to have a contact if any of their atoms make a " +
			"contact. Two atoms are said to make a contact if the shortest distance " +
			"between their Van der Waals radii is less than the cutoff.(BR) " +
			"For this server the cutoff is 1.0 Ånstrøm.";
VAR
	line: 		STRING;
	word: 		ARRAY OF STRING;
	fields:		ARRAY OF STRING;
	atom_reset:	ARRAY OF FullAtom;
	info: 		FullResidue;
	N_atom,i:	INTEGER;
	atom:		FullAtom;
	whatif:		FILE;
	residue:	Residue;

BEGIN
	whatif := WHATIF("wsvss1", id);
	WHILE READLINE(whatif, line) DO
		SPLIT(line, '|', fields);
		ReadResidue(fields[1], residue);
		ResidueToFullResidue(residue, info);
		N_atom := CAST(INTEGER, fields[2]);
		info.atoms := atom_reset;
		FOR i := 1 TO N_atom DO
			SPLIT(fields[i+2], ';', word);
			atom.PDBx_atom_site := 		CAST(INTEGER, word[1]);
			atom.PDBx_Cartn_x := 		CAST(FLOAT,   word[2]);
			atom.PDBx_Cartn_y := 		CAST(FLOAT,   word[3]);
			atom.PDBx_Cartn_z := 		CAST(FLOAT,   word[4]);
			atom.PDBx_B_iso_or_equiv := 	CAST(FLOAT,   word[5]);
			atom.PDBx_auth_asym_id := 	CAST(STRING,  word[6]);
			atom.PDBx_auth_atom_id := 	CAST(STRING,  word[7]);
			atom.PDBx_occupancy := 		CAST(FLOAT,   word[8]);
			atom.PDBx_type_symbol := 	CAST(STRING,  word[9]);
			PUSH(info.atoms,atom);
		END;
		PUSH(response, info);
	END;
END SymShellOneXML;

END WhatifWS.