window.IspybClassicShippingHandler={
		
		/**
		 * Some services require a "site" parameter to be sent.
		 * This is a mapping of the IceBear shipmentdestination name to the expected "site" value in ISPyB.
		 */
		siteNames:{
			"ESRF":"ESRF",
			"Diamond":"DLS",
			"Diamond ISPyB Test":"DLS",
			"ALBA":"ESRF"
		},
		
		siteName:null,
		
		getSiteName:function(){
			if(IspybClassicShippingHandler.siteName){ return IspybClassicShippingHandler.siteName; }
			var siteName=IspybClassicShippingHandler.siteNames[window.shipmentDestination.name];
			if(siteName){
				IspybClassicShippingHandler.siteName=siteName;
				return siteName;
			}
			return false;
		},
		
		/**
		 * The base URI of the ISPyB instance. 
		 */
		baseUri:null,
		
		/**
		 * The authentication token to be passed to the synchrotron with each request.
		 */
		token:null,
		
		/**
		 * Begins the process of authenticating, choosing a proposal/session, and submitting to 
		 * the synchrotron. It is assumed that pre-flight validation of the shipment has already 
		 * been done!
		 */
		begin:function(){
			if(!window.shipmentDestination){ 
				alert("No shipment destination set - cannot determine shipping API location.");
				return false;
			}
			if(!IspybClassicShippingHandler.getSiteName()){
				alert("Cannot determine ISPyB 'site' parameter value for synchrotron. Shipment submission will not work without it.");
				return false;
			}
			var baseUri=window.shipmentDestination.baseuri;
			if(!baseUri){ 
				alert("No shipping API base URI set for "+window.shipmentDestination.name+".");
				return false;
			}
			IspybClassicShippingHandler.baseUri=baseUri.replace(/\/+$/g,"")+"/";
			ui.modalBox({ title:"Sending shipment to "+window.shipmentDestination.name, content:"Getting list of proposals from "+window.shipmentDestination.name+"..." });
			IspybClassicShippingHandler.getProposals();
		},

		/*
		 * We need a session ID to log in. Request the login form, bin it, and steal the cookie.
		 */
		pingForSessionId:function() {
			AjaxUtils.remoteAjax(
				IspybClassicShippingHandler.baseUri+"",
				"get",
				{},
				IspybClassicShippingHandler.pingForSessionId_onSuccess,
				IspybClassicShippingHandler.pingForSessionId_onFailure
			);
		},
		pingForSessionId_onSuccess:function(transport) {
			IspybClassicShippingHandler.token=null;
			let headers=transport.getAllResponseHeaders().split('\n');
			headers.forEach(function (h) {
				if(h.toLowerCase().indexOf("x-icebear-remote-set-cookie")===0){
					h=h.substr(28);
					parts=h.split(";");
					parts.forEach(function (part) {
						part=part.trim();
						cookie=part.split("=", 2);
						if("JSESSIONID"===cookie[0]){
							IspybClassicShippingHandler.token=cookie[1];
						}
					})
				}
			});
			if(null===IspybClassicShippingHandler.token){
				return IspybClassicShippingHandler.pingForSessionId_onFailure();
			}
			IspybClassicShippingHandler.showLoginForm();
		},
		pingForSessionId_onFailure:function() {
			ui.setModalBoxTitle("Can't send shipment");
			document.getElementById("modalBox").querySelector(".boxbody").innerHTML="Could not retrieve session cookies for logging in";
		},

		/**
		 * Renders the login form into the modal box.
		 */
		showLoginForm:function(){
			document.getElementById("modalBox").querySelector(".boxbody").innerHTML="";
			let f=document.getElementById("modalBox").form({
				action:IspybClassicShippingHandler.baseUri+"authenticate",
				method:"post",
			});
			f.style.maxWidth="800px";
			let h=f.formField({
				label:"Authenticate with your "+window.shipmentDestination.name+" credentials", content:'&nbsp;'
			});
			h.classList.add("radiohead");
			f.textField({
				name:'remoteusername',
				label:window.shipmentDestination.name+" username",
				value:""
			});
			f.passwordField({
				name:'remotepassword',
				label:window.shipmentDestination.name+" password",
				value:""
			});
			fieldValidations.remoteusername="required";
			fieldValidations.remotepassword="required";
	
			f.submitButton({ label:"Authenticate" });
			f.onsubmit=function(){ IspybClassicShippingHandler.authenticate(); return false; }
		},
		
		/**
		 * Validates that both username and password are present, then submits them to the remote API.
		 * Expects a token in return.
		 */
		authenticate:function(){
			var frm=document.getElementById("modalBox").querySelector(".boxbody form");
			var isValid=true;
			frm.querySelectorAll("input").forEach(function(f){
				if(!validator.validate(f)){ isValid=false; }
			});
			if(!isValid){ return false; }
			var username=frm.remoteusername.value;
			var password=frm.remotepassword.value;
			frm.querySelector("input[type=submit]").closest("label").classList.add("updating");
			AjaxUtils.remoteAjax(
				IspybClassicShippingHandler.baseUri+"security/j_security_check",
//				"https://httpbin.org/cookies",
					"post",
					{ j_username:username, j_password:password },
					IspybClassicShippingHandler.authenticate_onSuccess,
					IspybClassicShippingHandler.authenticate_onFailure,
					{
						'X-IceBear-Remote-Origin': IspybClassicShippingHandler.baseUri.replace("/ispyb/",""),
						'X-IceBear-Remote-Cookie': 'path=/ispyb; JSESSIONID='+IspybClassicShippingHandler.token
					}
			);
			return false;
		},
		authenticate_onSuccess:function(transport){

			alert("success")
			return;
			if(!transport.responseJSON || !transport.responseJSON.jwt){
				IspybClassicShippingHandler.showErrorAtAuthenticate("Bad response from remote server. Cannot authenticate");
			}
			IspybClassicShippingHandler.token=transport.responseJSON.jwt;
			window.setTimeout(IspybClassicShippingHandler.getProposals,50);
		},
		authenticate_onFailure:function(transport){
			alert("fail")
			var msg="Could not log you in. The remote server gave no further information.";
			if(transport.responseJSON && transport.responseJSON.error){
				msg=window.shipmentDestination.name+" said: "+transport.responseJSON.error;
			}
			IspybClassicShippingHandler.showErrorAtAuthenticate(msg);
		},
		showErrorAtAuthenticate:function(msg){
			var frm=document.getElementById("modalBox").querySelector(".boxbody").querySelector("form");
			frm.querySelector("input[type=submit]").closest("label").classList.remove("updating");
			if(!document.getElementById("autherror")){
				frm.innerHTML+='<label style="text-align:left" id="autherror"></label>';
			}
			document.getElementById("autherror").innerHTML=msg;
		},
		
		/**
		 * Fetches a list of the user's proposals from the synchrotron.
		 * On failure, triggers authentication.
		 */
		getProposals:function(){
			if(!IspybClassicShippingHandler.baseUri){ 
				alert("No base URI supplied. Call begin() first, with base URI."); 
				return false;
			}
			if(!IspybClassicShippingHandler.token){ 
				IspybClassicShippingHandler.pingForSessionId();
				return false;
			}
			AjaxUtils.remoteAjax(
					IspybClassicShippingHandler.baseUri+IspybClassicShippingHandler.token+'proposal/list',
					'get',
					{},
					IspybClassicShippingHandler.getProposals_onSuccess,
					IspybClassicShippingHandler.getProposals_onFailure
			);
		},
		getProposals_onSuccess:function(transport){
			if(transport.responseJSON.error){
				return IspybClassicShippingHandler.getProposals_onFailure(transport);
			}
			var proposals=transport.responseJSON.data;
			IspybClassicShippingHandler.renderProposalList(proposals);
		},
		getProposals_onFailure:function(transport){
			var msg="Could not get list of proposals from "+window.shipmentDestination.name;
			if(transport.responseJSON && transport.responseJSON.error){
				msg=window.shipmentDestination.name+" said: "+transport.responseJSON.error;
			}
			IspybClassicShippingHandler.showErrorAtAuthenticate(msg);
		},
		
		/**
		 * Given an array of ISPyB proposal objects, renders them as clickable bars.
		 * If the list header is not present, the modal box is cleared (on the basis that it
		 * probably has the login form in it, and if we're here authentication was successful).
		 * 
		 * @param array response An object containing a total and an array of ISPyB proposal objects
		 */
		renderProposalList:function(response){
			var mb=document.getElementById("modalBox").querySelector(".boxbody");
			if(response.total==0){
				ui.setModalBoxTitle("No proposals at "+window.shipmentDestination.name);
				mb.innerHTML='No proposals found. Cannot submit shipment.';
				return false;
			}
			ui.setModalBoxTitle("Your proposals at "+window.shipmentDestination.name);
			mb.innerHTML='';
			response.data.forEach(function(p){
				mb.treeItem({
					record:p,
					id:"proposal_"+p.PROPOSAL,
					header:p.PROPOSAL+": "+p.TITLE,
					url:IspybClassicShippingHandler.baseUri+IspybClassicShippingHandler.token+'/proposal/'+p.PROPOSAL+'/session/list',
					headers:["Visit","Active","Start","End",""],
					cellTemplates:["{{VISIT}}", [ui.checkmark,"ACTIVE", ], "{{ST}}", "{{EN}}", [IspybShippingHandler.getSendButton,"ACTIVE"] ],
				});
			});
		},
		
		/**
		 * 
		 * @param proposalName string The proposal name e.g., "mx415"
		 */
		getProteinsForProposal:function(proposalName){
			AjaxUtils.remoteAjax(
					IspybClassicShippingHandler.baseUri+IspybClassicShippingHandler.token+'/proposal/'+proposalName+'/mx/protein/list',
					"get",
					{},
					function(transport){ IspybClassicShippingHandler.getProteinsForProposal_onSuccess(transport, proposalName); },
					function(transport){ IspybClassicShippingHandler.getProteinsForProposal_onFailure(transport, proposalName); }
			);
		},
		getProteinsForProposal_onSuccess:function(transport, proposalName){
			if(!transport.responseJSON || transport.responseJSON.error){
				return IspybClassicShippingHandler.getProteinsForProposal_onFailure(transport, proposalName);
			}
			document.getElementById("proposal_"+proposalName).proposals=transport.responseJSON.data;
		},
		getProteinsForProposal_onFailure:function(transport,proposalName){
			document.getElementById("proposal_"+proposalName).proposals=false;
			var msg="Could not get list of known proteins for proposal "+proposalName+".";
			if(transport.responseJSON && transport.responseJSON.error){
				msg+="\n\n"+window.shipmentDestination.name+"said:\n\n"+transport.responseJSON.error;
			}
		},
		
		
		/**
		 * Returns the HTML for a "send shipment" button.
		 * @param Object record A visit object from Synchweb.
		 */
		getSendButton:function(record){
			if(1==record.ACTIVE){
				return '<input type="button" value="Send shipment" onclick="IspybClassicShippingHandler.sendShipment(this)" />';
			}
			//Inactive visits - currently allow and let synchrotron barf if unhappy.
			return '<input type="button" value="Send shipment" onclick="IspybClassicShippingHandler.sendShipment(this)" />';
		},
		
		/**
		 * Triggers final submission of the shipment to the synchrotron (along with any preflight validation).
		 * @param DOMElement btn The "Send shipment" button.
		 */
		sendShipment:function(btn){
			var visit=btn.closest("tr").rowData;
			if(!confirm("Really submit shipment to "+window.shipmentDestination.name+", visit "+visit.VISIT+"?")){ return false; }
			btn.closest("tr").classList.add("updating");
			btn.value="Sending..."
			ui.setModalBoxTitle("Submitting your shipment to "+window.shipmentDestination.name+"...");
			document.getElementById("modalBox").querySelectorAll("input[type=button]").forEach(function(b){
				b.disabled=true;
			})
			var proposalName=btn.closest(".treeitem").previousElementSibling().record.PROPOSAL;
			IspybClassicShippingHandler.shipmentProposal=proposalName;
			IspybClassicShippingHandler.shipmentVisit=visit.VISIT;
		},

		shipmentProposal:null,
		shipmentVisit:null,
		
		waitForProteinsOnProposalThenValidate:function(){
			if(undefined===document.getElementById("proposal"+IspybClassicShippingHandler.shipmentProposal)){
				window.setTimeout(IspybClassicShippingHandler.waitForProteinsOnProposalThenValidate, 100);
				return false;
			}
			IspybClassicShippingHandler.validateProteinsInShipment();
		},
		
		validateProteinsInShipment:function(){
			//every sample has a protein acronym
			//every acronym is one that appears in the proposal
			//has been assessed?
			//safety value OK to send?
			//All good? Get shipment encoding from IceBear server
			//Submit via AJAX
			//Store response
			//Server should retrieve shipping label and attach to shipment
			//Mark shipped
			//Reload
		}
		
}