window.DemoShippingHandler={
		
		/**
		 * The base URI of the SynchWeb instance. 
		 * Populated from the shipmentdestination's baseuri property.
		 */
		baseUri:null,
		
		/**
		 * The authentication token to be passed to the synchrotron with each request after authentication.
		 * Received from the synchrotron on authentication.
		 */
		token:null,

		safetyLevels:[
			{ "code":"Green", "risk":"Low", "background":"#cfc", "color":"#060"},
			{ "code":"Yellow", "risk":"Medium", "background":"#ffc", "color":"#660"},
			{ "code":"Red", "risk":"High", "background":"#fcc", "color":"#600"}
		],

		/*
		 * These are set and used during shipment submission
		 */
		shipmentIdAtFacility:"",
		safetyLevel:null,
		labContact:null,
		proteins:null,
		proposal:null,
		visit:null,

		UNATTENDED:"Unattended",
		
		/**
		 * Returns the URL of this shipment at the remote synchrotron, or false if the shipment has not been submitted.
		 * Strips /api(/) from base API and appends /shipment/sid/[REMOTE ID].
		 */
		getShipmentUrlAtFacility:function(shipmentDestination, shipmentIdAtFacility){
			if(!shipmentIdAtFacility &&!data.idatremotefacility){
				return false;
			} else if(!shipmentIdAtFacility){
				shipmentIdAtFacility=data.idatremotefacility;
			}
			let baseUri=shipmentDestination["baseuri"];
			if(!baseUri){ 
				alert("Cannot get shipment URL. No shipping API base URI set for "+shipmentDestination.name+".");
				return false;
			}
			baseUri=baseUri.replace(/[/]api[/]?$/g,"");
			return baseUri+"/shipments/sid/"+shipmentIdAtFacility;
		},
		
		/**
		 * Returns the URL of this crystal at the remote synchrotron, or false if the crystal does not have a remote ID.
		 * Strips /api(/) from base API and appends /samples/sid/[REMOTE ID].
		 *
		 */
		getCrystalUrlAtFacility:function(shipmentDestination, crystalIdAtFacility){
			if(!crystalIdAtFacility || ""===crystalIdAtFacility){
				return false;
			}
			let baseUri=shipmentDestination["baseuri"];
			if(!baseUri){ 
				alert("Cannot get crystal URL. No shipping API base URI set for "+shipmentDestination.name+".");
				return false;
			}
			baseUri=baseUri.replace(/[/]api[/]?$/g,"");
			return baseUri+"/samples/sid/"+crystalIdAtFacility;
		},
		
		/**
		 * Begins the process of authenticating, choosing a proposal/session, and submitting to 
		 * the synchrotron. 
		 * 
		 * It is assumed that local validation of the shipment has already been done! This includes 
		 * basic checks such as: at least one dewar; each dewar has at least one puck; all crystals
		 * have protein acronyms. Some validation can only be done at the synchrotron, but we should
		 * not be sending them shipments that fail these basic checks.
		 */
		begin:function(){
			if(!window.shipmentDestination){
				alert("No shipment destination set - cannot determine shipping API location.");
				return false;
			}
			//Check still logged into IceBear, reset session clock - don't want to fail at the end.
			new AjaxUtils.Request("/api/homepagebrick", {
				"method":"get",
				"onSuccess":DemoShippingHandler.openShippingDialog,
				"onFailure":function(transport){
					if(401===transport.status){
						//IceBear session timed out. Reload page to show login form.
						ui.forceReload();
					} else {
						ui.keepAlive();
						DemoShippingHandler.openShippingDialog();
					}
				}
			});
			
		},

		openShippingDialog:function(){
			ui.modalBox({ title:"Demonstration of sending shipment", content:"..." });
			DemoShippingHandler.showLoginForm();
		},

		/**
		 * Renders the login form into the modal box.
		 */
		showLoginForm:function(){
			let mb=document.getElementById("modalBox");
			mb.querySelector(".boxbody").innerHTML="";
			ui.setModalBoxTitle("Authenticate with the synchrotron");
			let f=mb.form({
				action:DemoShippingHandler.baseUri+"authenticate",
				method:"post",
			});
			f.style.maxWidth="800px";
			let h=f.formField({
				label:"Synchrotron credentials", content:'&nbsp;'
			});
			h.classList.add("radiohead");
			let blurb=f.formField({"content":"<p>At this point, you would provide your synchrotron (ISPyB) username "+
					"and password. We are not sending an actual shipment, so we don't need them here.</p>"+
					"<p>This demo process is modelled on shipments to Diamond Light Source. The exact steps vary "+
					"between synchrotrons, but typically you will select proposal and "+
					"visit/session, and a lab contact, before submitting the shipment.</p>"});
			blurb.querySelector("span.label").remove();
			blurb.style.textAlign="left";

			f.submitButton({ label:"Continue" });
			f.onsubmit=function(){ DemoShippingHandler.getProposals(); return false; };
		},
		

		getProposals:function() {
			DemoShippingHandler.renderProposalList({
				"total":2,
				"data":[
					{ "PROPOSAL":"mx1234","TITLE":"Demo proposal 1" },
					{ "PROPOSAL":"mx2345","TITLE":"Demo proposal 2" }
				]
			});
		},
		renderProposalList:function(response){
			let mb=document.getElementById("modalBox").querySelector(".boxbody");
			if(parseInt(response.total)===0){
				ui.setModalBoxTitle("No proposals at Demo Synchrotron");
				mb.innerHTML='No proposals found. Cannot submit shipment.';
				return false;
			}
			ui.setModalBoxTitle("Your proposals at Demo Synchrotron");
			mb.innerHTML='';
			response.data.forEach(function(p){
				let prop=mb.treeItem({
					record: p,
					header: p["PROPOSAL"] + ": " + p["TITLE"],
					content: ""
				});

				prop.table({
						headers:["Visit","Beamline","Active","Start","End",""],
						cellTemplates:["{{VISIT}}", "{{BL}}", [ui.checkmark,"ACTIVE", ], "{{ST}}", "{{EN}}", [DemoShippingHandler.getSendButton,"ACTIVE"] ],
					},
					{
						"total":3,
						"data":[
							{
								"VISIT":p["PROPOSAL"]+"-1",
								"SESSIONID":"", "BL":"BL1", "ST":"2019-08-20", "EN":"2019-08-20", "ACTIVE":"0"
							},
							{
								"VISIT":p["PROPOSAL"]+"-2",
								"SESSIONID":"", "BL":"BL1", "ST":"2020-09-14", "EN":"2030-09-14", "ACTIVE":"1"
							},
							{
								"VISIT":DemoShippingHandler.UNATTENDED,
								"SESSIONID":"", "BL":"", "ST":"When available", "EN":"", "ACTIVE":"1"
							}
						]
					}
				);

				DemoShippingHandler.getProposalLabContacts(prop);

			});
		},
		
		getProposalLabContacts:function(elem) {
			elem.labContacts = [
				{'CARDNAME': 'Bob Shipper', 'LABNAME': 'Demo Lab'},
				{'CARDNAME': 'Julie Scientist', 'LABNAME': 'Demo Lab'}
			];
		},

		/**
		 * Returns the HTML for a "send shipment" button.
		 * @param {Object} record A visit object from Synchweb.
		 */
		getSendButton:function(record){
			if(DemoShippingHandler.UNATTENDED===record["VISIT"]){
				return '<input type="button" value="Unattended collection" onclick="DemoShippingHandler.chooseLabContact(this)" />';
			} else if(1===parseInt(record["ACTIVE"])){
				return '<input type="button" value="Use this visit" onclick="DemoShippingHandler.chooseLabContact(this)" />';
			}
			//Inactive visits - currently allow and let synchrotron barf if unhappy.
			return '<input type="button" value="Use this visit" onclick="DemoShippingHandler.chooseLabContact(this)" />';
		},
		
		/**
		 * Triggers final submission of the shipment to the synchrotron (along with any preflight validation).
		 * @param {Element} btn The "Send shipment" button.
		 */
		chooseLabContact:function(btn){
			let visit=btn.closest("tr").rowData;
			let proposalElement=btn.closest("div.treeitem");
			let proposal=proposalElement.record;
			let labContacts=proposalElement.labContacts;
			DemoShippingHandler.proposal=proposal;
			DemoShippingHandler.visit=visit;
			let box=document.getElementById("modalBox").querySelector(".boxbody");
			box.innerHTML='';
			box.table({
				headers:[ 'Name','Organisation',''],
				cellTemplates:[ '{{CARDNAME}}','{{LABNAME}}','<input type="button" value="Choose" onclick="DemoShippingHandler.setLabContact(this)" />' ],
				contentBefore:'Choose the lab contact for the shipment:'
			}, {'total':labContacts.length, 'rows':labContacts });
			ui.setModalBoxTitle("Choose the lab contact for this shipment");
		},
		setLabContact:function(btn){
			DemoShippingHandler.labContact=btn.closest("tr").rowData;
			DemoShippingHandler.chooseSafetyLevel();
		},

		chooseSafetyLevel:function(){
			ui.setModalBoxTitle("Choose the safety level for this shipment");
			let box=document.getElementById("modalBox").querySelector(".boxbody");
			box.innerHTML='';
			let t=box.table({
				headers:[ 'Colour code','Risk level',''],
				cellTemplates:[ '{{code}}','{{risk}}','<input type="button" value="Choose" onclick="DemoShippingHandler.setSafetyLevel(this)" />' ],
				contentBefore:'Choose the safety level for the shipment:'
			}, {'total':3, 'rows':DemoShippingHandler.safetyLevels });
			t.querySelectorAll("tr").forEach(function(tr){
				if(!tr.rowData){ return; }
				tr.style.backgroundColor=tr.rowData.background;
				tr.querySelector("td").style.color=tr.rowData.color;
				tr.querySelector("td").style.fontWeight="bold";
			});
		},
		setSafetyLevel:function(btn){
			let tr=btn.closest("tr");
			DemoShippingHandler.safetyLevel=tr.rowData["code"];
			DemoShippingHandler.showFinalConfirmation();
		},
		
		showFinalConfirmation:function(){
			let box=document.getElementById("modalBox").querySelector(".boxbody");
			let proposal=DemoShippingHandler.proposal;
			let visit=DemoShippingHandler.visit;
			let labContact=DemoShippingHandler.labContact;
			box.innerHTML='';
			ui.setModalBoxTitle("Check the details before sending the shipment");
			let f=box.form({ action:'#', method:'post' });
			f.style.width="48%";
			f.style.position="absolute";
			f.style.left="1%";
			f.style.top="1%";
			f.textField({ readonly:true, label:'Destination', value:window.shipmentDestination.name });
			f.textField({ readonly:true, label:'Proposal', value:proposal["PROPOSAL"] });
			f.textField({ readonly:true, label:'Visit', value:visit["VISIT"] });
			f.textField({ readonly:true, label:'Home lab contact', value:labContact["CARDNAME"] });
			
			DemoShippingHandler.safetyLevels.forEach(function(sl){
				if(sl.code.toLowerCase()!==DemoShippingHandler.safetyLevel.toLowerCase()){ return; }
				let ff=f.textField({ readonly:true, label:'Safety level', value:sl.code });
				ff.style.backgroundColor=sl.background;
			});

			let f2=box.form({ action:'#', method:'post' });
			f2.style.position="absolute";
			f2.style.top="1%";
			f2.style.right="1%";
			f2.style.width="48%";
			f2.style.marginTop="0";
			f2.style.paddingTop="0";
			let b=f2.formField({ readonly:true, label:'Review this information to make sure that it is correct, then click "Send shipment".<br/>No data will actually be sent.', content:'<input type="button" value="Send shipment" onclick="DemoShippingHandler.sendShipment()" />' });
			b.style.height=f.offsetHeight+"px";
			b.style.textAlign="left";

			let inp=b.querySelector("input");
			inp.style.position="absolute";
			inp.style.bottom="0.5em";
			inp.style.right="0.5em";

			box.innerHTML+='<div style="position:absolute;bottom:10%; left:1%; width:98%; text-align:center"><a href="#" onclick="ui.closeModalBox();return false">Cancel - don\'t send this shipment</a></div>';
		},
		
		sendShipment:function(){
			ui.setModalBoxTitle("Pretending to submit your shipment to "+window.shipmentDestination.name+"...");
			document.getElementById("modalBox").querySelector(".boxbody").innerHTML="";
			ui.logToDialog('Pretending to send shipment...');
			DemoShippingHandler.getProteins();
		},
		
		getProteins:function(){
			let proposal=DemoShippingHandler.proposal;
			ui.logToDialog("Getting proteins for proposal "+proposal["PROPOSAL"]+"...");
			window.setTimeout(DemoShippingHandler.doPreflight, 200);
		},

		preflightError:false,

		doPreflight:function(){
			ui.logToDialog("...done. Pre-flight checks: ");
			ui.logToDialog("- ensure there is at least one dewar, puck and crystal");
			let dewars=document.querySelectorAll(".containertab");
			if(!dewars || 0===dewars.length){
				ui.logToDialog("The shipment must contain at least one dewar", "error");
				DemoShippingHandler.preflightError=true;
				return;
			}
			dewars.forEach(function (elem) {
				let pucks=elem.nextElementSibling.querySelectorAll(".treeitem");
				if(!pucks || 0===pucks.length){
					DemoShippingHandler.preflightError=true;
					ui.logToDialog("Dewar "+elem.dataset.containername+" contains no pucks","error");
				} else {
					pucks.forEach(function (ti) {
							let pinCount=0;
							ti.querySelectorAll("tr.datarow").forEach(function(tr){
								if(tr.rowData && tr.rowData["containercategoryname"]){
									pinCount++;
									if(!tr.rowData["crystalid"]){
										DemoShippingHandler.preflightError=true;
										ui.logToDialog("Pin in dewar "+elem.dataset.containername+" puck "+ti.record.name+" position "+tr.rowData["position"]+" has no crystal","error");
									}
								}
							});
							//validate >0 pins with crystals
					});
				}
			});
			ui.logToDialog("- ensure that all crystals have a protein acronym that is on the proposal");
			ui.logToDialog("(not checking - dummy shipment)");
			ui.logToDialog("...done.");
			if(DemoShippingHandler.preflightError){
				ui.logToDialog("Pre-flight validation failed - can't send the shipment.","error");
				return;
			}

			ui.logToDialog("Pre-flight validation OK. Pretending to create shipment in ISPyB...", "success");
			DemoShippingHandler.remoteErrors=[];
			window.setTimeout(DemoShippingHandler.createEmptyShipment, 200);
		},


		/**
		 * Creates a shipment at the synchrotron, with the empty dewars attached.
		 * Dewars that are not registered at the synchrotron are automatically created.
		 * We only receive a shipment ID in return, so the next stage retrieves all dewars for the shipment.
		 */
		createEmptyShipment:function() {
			ui.logToDialog("Submitting basic shipment details");
			let dewars=document.querySelectorAll(".containertab");
			dewars.forEach(function (elem) {
					DemoShippingHandler.createDewarAndContents(elem);
			});
			DemoShippingHandler.completeShipment();
		},

		createDewarAndContents:function(elem){
			ui.logToDialog("Creating dewar "+elem.dataset.containername+" at synchrotron");
			elem.nextElementSibling.querySelectorAll(".treeitem").forEach(function (ti) {
				ui.logToDialog("Adding puck "+ti.record.name+" to dewar "+ti.dewar.name+"");
				ti.querySelectorAll("tr.datarow").forEach(function(tr){
					if(tr.rowData && tr.rowData["containercategoryname"]){
						ui.logToDialog("Adding pin to dewar "+elem.dataset.containername+" puck "+ti.record.name+" position "+tr.rowData["position"]+"");
						DemoShippingHandler.addShippedNote(tr.rowData.childitems[1]);
					}
				});
			})
		},
		

		addShippedNote:function(localCrystal){
			//TODO Move this to server-side, shipment::update - eliminate duplication in future handlers
			let noteText='Crystal shipped to '+window.shipmentDestination.name;
			new AjaxUtils.Request('/api/note',{
				method:'post',
				parameters:{
					csrfToken:csrfToken,
					parentid:localCrystal.id,
					text:noteText
				},
//				onSuccess:function(transport){ if(successCallback){ successCallback(transport.responseJSON); } },
				onFailure:AjaxUtils.checkResponse
			});
		},

		completeShipment:function(){
			ui.logToDialog("Shipment was submitted to "+window.shipmentDestination.name+".","success");			
			ui.logToDialog("Updating IceBear and creating shipment manifest...");
			let shippedDate=new Date().toISOString().split("T")[0];
			new AjaxUtils.Request('/api/shipment/'+data.id,{
				'method':'patch',
				'parameters':{
					'csrfToken':csrfToken,
					'dateshipped':shippedDate,
					'proposalname':DemoShippingHandler.proposal["PROPOSAL"],
					'sessionname':DemoShippingHandler.visit["VISIT"],
				},
				onSuccess:DemoShippingHandler.completeShipment_onSuccess,
				onFailure:DemoShippingHandler.completeShipment_onFailure,
			});
		},
		completeShipment_onSuccess:function(){
			ui.logToDialog("IceBear updated successfully.","success");
			ui.logToDialog("The page will reload in a moment. Please wait...");
			window.setTimeout(ui.forceReload, 2500);
		},
		completeShipment_onFailure:function(transport){
			ui.logToDialog("The shipment submission to "+window.shipmentDestination.name+" succeeded, but IceBear was not updated ","error");
			if(transport.responseJSON && transport.responseJSON.error){				
				ui.logToDialog(transport.responseJSON.error, "error");
			}			
			ui.logToDialog("The page will reload in a moment. Please wait...");
			window.setTimeout(ui.forceReload, 5000);
		},
		
};