/**
 *  ---------
 * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
 * |#       #|
 * |#       #|  Copyright (c) 2016 CardContact Systems GmbH
 * |'##> <##'|  Schuelerweg 38, 32429 Minden, Germany (www.cardcontact.de)
 *  ---------
 *
 *  This file is part of OpenSCDP.
 *
 *  OpenSCDP is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  OpenSCDP is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with OpenSCDP; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @fileoverview Controller for Service Requests
 */

var CommonUI = require('scsh/srv-cc1/CommonUI').CommonUI;
var Form = require('scsh/srv-cc1/Form').Form;
var ServiceRequestModel = require('pki-as-a-service/service/ServiceRequestModel').ServiceRequestModel;
var PKIXCommon = require("scsh/x509/PKIXCommon").PKIXCommon;
var I18N = require('scsh/srv-cc1/I18N').I18N;
var ServiceRequestBrowser = require('pki-as-a-service/ui/ServiceRequestBrowser').ServiceRequestBrowser;



function ServiceRequestController(ui) {
	this.ui = ui;
	this.service = ui.service;
}

exports.ServiceRequestController = ServiceRequestController;



ServiceRequestController.prototype.i18n = function(key) {
	return I18N.t(this.ui.session.user.lang, key);
}



ServiceRequestController.prototype.renderForm = function(req, res, sr) {
	var lc = sr.getLifeCycle();

	var form = sr.getForm(this.ui.session.user);

	if (!(form instanceof Form)) {
		form = new Form(form);
	}

	form.setI18NHandler(this.ui);
	form.setCSRFToken(this.ui.generateSessionToken().toString(HEX));

	var actionList = sr.getActionList(this.ui.session.user);
	form.setSaveEnabled(actionList.indexOf("action.save") >= 0);

	var form = form.render();

	form.appendChild( <br/> );

	var actionInfo = sr.getActionInfo();
	if (actionInfo) {
		form.appendChild( <p>&#9888; { actionInfo }</p> );
	}

	for (var i = 0; i < actionList.length; i++) {
		var actionid = actionList[i];
		var action = actionid;

		var ofs = actionid.indexOf("@");
		if (ofs > 0) {
			action = actionid.substr(0, ofs);
		}

		var button = <button type="submit" name="action" value={actionid} class="pure-button">{ this.ui.i18n(action) }</button>

		var ttid = action + ".tooltip";
		var ttstr = this.ui.i18n(ttid);
		if (ttstr != ttid) {
			button.@title = ttstr;
		}

		form.appendChild( button );
	}

	var formDiv = <div class="card" />;
	formDiv.appendChild(form);

	return formDiv;
}



ServiceRequestController.prototype.renderCertificateList = function(req, res, sr) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	var certDAO = this.service.daof.getCertificateDAO();
	var certs = certDAO.enumerateCertificatesByServiceRequestId(sr.getId());

	var t = <table class="pure-table pure-table-horizontal"/>;
	t.thead += <thead><tr><th>Subject</th><th>Issuer</th><th>Serial</th></tr></thead>;
	t.tbody += <tbody/>;

	for (var i = 0; i < certs.length; i++) {
		var cert = certs[i];
		var xc = new X509(cert.bytes);
		var dn = xc.getSubjectDNString();
		var rdn = PKIXCommon.parseDN(dn);
		var scn = PKIXCommon.findRDN(rdn, "CN");

		if (!scn) {
			scn = dn;
		}

		var dn = xc.getIssuerDNString();
		var rdn = PKIXCommon.parseDN(dn);
		var icn = PKIXCommon.findRDN(rdn, "CN");

		if (!icn) {
			icn = dn;
		}

		var url = this.ui.homeURL(req.url) + "/cert/" + cert.id;

		var tr =
		<tr>
			<td>
				<a href={ url }>{ scn }</a>
			</td>
			<td>{ icn }</td>
			<td>{ xc.getSerialNumberString() }</td>
		</tr>

		t.tbody.appendChild(tr);
	}

	if (certs.length > 0) {
		return t;
	}

	return null;
}



/**
 * Create a new Service Request
 *
 * @param {Object} template the service requests template object
 * @type Object
 * @return the new service request value object
 */
ServiceRequestController.prototype.createServiceRequest = function(template) {
	assert(typeof(template.process) == "string", "Member process must be a string");
	assert(typeof(template.title) == "string", "Member title must be a string");
	assert(typeof(template.originatorId) == "undefined" || typeof(template.originatorId) == "number", "Member originatorId must be a number");
	assert(typeof(template.recipientId) == "number", "Member recipientId must be a number");
	assert(typeof(template.state) == "undefined" || typeof(template.state) == "string", "Member state must be a string");
	assert(typeof(template.content) == "object", "Member content must be an object");

	var srDAO = this.service.daof.getServiceRequestDAO();

	if (typeof(template.originatorId) == "undefined") {
		template.originatorId = this.ui.session.user.id;
	}

	if (typeof(template.state) == "undefined") {
		template.state = "Created";
	}

	var vo = srDAO.newServiceRequest(template);
	return vo;
}



ServiceRequestController.prototype.getServiceRequest = function(req, res) {
	try {
		var id = parseInt(req.url[2]);
		var sr = this.service.getServiceRequestById(id);
	} catch (e) {
		if (e.error == GPError.OBJECT_NOT_FOUND) {
			res.setStatus(HttpResponse.SC_NOT_FOUND);
		} else {
			res.setStatus(HttpResponse.SC_BAD_REQUEST);
		}
		return;
	}

	return sr;
}



ServiceRequestController.prototype.renderRelated = function(req, res, sr) {
	GPSystem.log(GPSystem.DEBUG, module.id, "renderRelated()");

	var srdao = this.service.daof.getServiceRequestDAO();
	var list = srdao.listRelatedServiceRequests(sr.bo.id);

	if (list.length == 0) {
		return <div/>;
	}

	var div = <div>
			<h2>Related</h2>
			<table class="pure-table pure-table-horizontal">
				<thead>
				<tr>
					<th style="width:9%">Id</th>
					<th style="width:37%">Type</th>
					<th>Status</th>
				</tr>
				</thead>
				<tbody>
				</tbody>
			</table>
		</div>

	for (var i = 0; i < list.length; i++) {
		var e = list[i];
		var url = this.ui.homeURL(req.url) + "/sr/" + e.id;

		var title = e.title;
		if (e.details) {
			title += " (" + e.details + ")";
		}

		var tr = <tr>
				<td><a href={url}>{ e.id }</a></td>
				<td>{ title }</td>
				<td>{ e.state }</td>
			 </tr>
		div.table.tbody.appendChild(tr);
	}

	return div;
}



ServiceRequestController.prototype.renderHistory = function(req, res, sr, expand) {
	GPSystem.log(GPSystem.DEBUG, module.id, "renderHistory()");

	default xml namespace = "http://www.w3.org/1999/xhtml";

	var srdao = this.service.daof.getServiceRequestDAO();
	var srStates = srdao.listServiceRequestStates(sr.getId());

	if (srStates.length == 0) {
		return <div/>;
	}

	if (expand != "ALL") {
		var toggleExpand = "ALL";
	} else {
		var toggleExpand = "";
	}

	var d =
		<div>
			<h2>History</h2>
			<table class="pure-table pure-table-horizontal">
				<thead>
				<tr>
					<th style="width:12%">Who</th>
					<th style="width:40%">When</th>
					<th>What</th>
				</tr>
				</thead>
				<tbody>
				</tbody>
			</table>
			<p/>
		</div>;

	if (sr.bo.parentServiceRequestId) {
		var url = this.ui.homeURL(req.url) + "/sr/" + sr.bo.parentServiceRequestId;
		var p = <p>Created {sr.getCreationTime()} in service request <a href={ url }>{ sr.bo.parentServiceRequestId }</a></p>
	} else {
		var p = <p>Created {sr.getCreationTime()}</p>
	}
	d.p = p;

	var hasDetails = false;
	for (var i = srStates.length - 1; i >= 0; i--) {
		var lc = ServiceRequestModel.LIFECYCLE_STATES[srStates[i].lifecycle];
		var tr = <tr/>;

		// Who

		var caseHandlerId = "System";
		if (srStates[i].caseHandlerId) {
			caseHandlerId = srStates[i].caseHandlerId;
		}
		if (typeof(caseHandlerId) == "number") {
			var td = <td>
					<a href={ "../subject/" + caseHandlerId }>
						{ caseHandlerId }
					</a>
				</td>;
			if (srStates[i].caseHandlerRole) {
				td.appendChild(<small>(<a href={ "../role/" + srStates[i].caseHandlerRole}>{ srStates[i].caseHandlerRole }</a>)</small>);
			}
			tr.appendChild(td);
		} else {
			tr.appendChild(<td>{caseHandlerId}</td>);
		}

		// When

		var date = srStates[i].transitionTime;
		var when = date.toLocaleString();
		tr.appendChild(<td>{when}</td>);

		// What
		var message = undefined;
		if (srStates[i].content) {
			var content = JSON.parse(srStates[i].content);
			if (content.message) {
				message = content.message;
				if (message.message) {
					message = message.message;
				}
				hasDetails = true;
			}
		}

		if (message) {
			if ((expand != srStates[i].id) && (expand != "ALL") || expand == "ALL") {
				var expid = srStates[i].id;
			} else {
				var expid = "";
			}

			tr.appendChild(
				<td>
					<a class="dropdown-arrow" href={ "?op=expand&state=" + expid }>
						{srStates[i].state + " (" + lc + ")" }
					</a>
				</td>);
			d.table.tbody.appendChild(tr);

			if (!expid || expand == "ALL") {
				var ul = <ul style="padding-left:0;"/>;
				ul.li += <li style="list-style-type: none;">{ message }</li>;

				var pre = <pre>{ message }</pre>;

				tr = <tr><td colspan="3">{ pre }</td></tr>;


				d.table.tbody.appendChild(tr);
			}
		} else {
			var td = <td>{srStates[i].state + " (" + lc + ")" }</td>
			tr.appendChild(td);
			d.table.tbody.appendChild(tr);
		}
	}

	if (hasDetails) {
		d..th[2] = <th><a class="dropdown-arrow" href={ "?op=expand&state=" + toggleExpand}>What</a></th>;
	}

	return d;
}



ServiceRequestController.prototype.postCreateAction = function(req, res) {
	var op = CommonUI.parseQueryString(req.queryString);
	var factory = this.service.pluginRegistry.getFactoryForProcess(op.process);
	if (!factory) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Can not find factory for process " + op.process);
	}

	if (typeof(op.for) != "undefined") {
		var id = parseInt(op.for);
		if (isNaN(id) || id == 0) {
			throw new GPError(module.id, GPError.INVALID_DATA, 0, "Argument 'for' not valid in URL");
		}

		var subject = this.service.getSubject(id);

		if (typeof(op.holder) != "undefined") {
			var holderId = parseInt(op.holder);
			if (isNaN(holderId) || holderId == 0) {
				throw new GPError(module.id, GPError.INVALID_DATA, 0, "Argument 'holder' not valid in URL");
			}
			var holderDAO = this.service.daof.getHolderDAO();
			var holder = holderDAO.getHolderById(holderId);
			if (!holder || holder.subjectId != subject.getId()) {
				throw new GPError(module.id, GPError.INVALID_DATA, 0, "Holder referred to by argument 'holder' does not belong to subject referred to by argument 'for'");
			}
		}

		if (!this.hasAccessToSubject(op.process, subject, holder)) {
			GPSystem.log(GPSystem.WARN, module.id, "Not authorized to start process for subject " + op.for);
			return;
		}
	}

	var ctrl = factory.getControllerForProcess(op.process);

	if (ctrl.postCreateAction(req, res, this, req.parameters.action, subject, holder)) {
		return;
	}

	res.setStatus(HttpResponse.SC_SEE_OTHER);
	var url = req.nativeRequest.getContextPath() + req.servletPath + req.pathInfo;
	if (req.queryString) {
		url += "?" + req.queryString;
	}
	res.addHeader("Location", url);
}



ServiceRequestController.prototype.postProcessAction = function(req, res, sr) {
	GPSystem.log(GPSystem.INFO, module.id, "postProcessAction " + req.parameters.action);

	var factory = this.service.pluginRegistry.getFactoryForProcess(sr.bo.process);
	if (!factory) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Can not find factory for process " + sr.bo.process);
	}

	var ctrl = factory.getControllerForProcess(sr.bo.process);

	try	{
		if (ctrl.postProcessAction(req, res, this, sr, req.parameters.action)) {
			// Processing produced a page
			return;
		}
	}

	catch(e) {
		GPSystem.log(GPSystem.ERROR, module.id, e.message + "\n" + e.stack);
		if (!this.ui.handleSmartCardHSMError(e, true)) {
			this.ui.setErrorMessage("internal_error", "Internal error '" + e.message + "' at " + e.fileName + "#" + e.lineNumber + ". See log for details");
		}
	}

	// GET after POST
	res.setStatus(HttpResponse.SC_SEE_OTHER);
	res.addHeader("Location", req.nativeRequest.getContextPath() + req.servletPath + req.pathInfo);
}



/**
 * Check if the current user is allowed to create a service request for
 * another subject based on the list of processes the plug-in contributes
 * to the subject menu item
 */
ServiceRequestController.prototype.hasAccessToSubject = function(proc, subject, holder) {
	if (holder != null) {
		var processes = this.service.pluginRegistry.enumerateRunnableProcessesForHolderType(this.ui.session.user, subject, holder.type);
	} else {
		var processes = this.service.pluginRegistry.enumerateRunnableProcessesForSubject(this.ui.session.user, subject);
	}
	for (var i = 0; i < processes.length; i++) {
//		print("Checking " + processes[i].process);
		if (processes[i].process == proc) {
			return true;
		}
	}
	return false;
}



ServiceRequestController.prototype.getCreateView = function(req, res) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	var page = null;

	var op = CommonUI.parseQueryString(req.queryString);
	var factory = this.service.pluginRegistry.getFactoryForProcess(op.process);
	if (!factory) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Can not find factory for process " + op.process);
	}

	if (typeof(op.for) != "undefined") {
		var id = parseInt(op.for);
		if (isNaN(id) || id == 0) {
			throw new GPError(module.id, GPError.INVALID_DATA, 0, "Argument id not valid in URL");
		}

		var subject = this.service.getSubject(id);

		if (typeof(op.holder) != "undefined") {
			var id = parseInt(op.holder);
			if (isNaN(id) || id == 0) {
				throw new GPError(module.id, GPError.INVALID_DATA, 0, "Argument 'holder' not valid in URL");
			}

			var holderDAO = this.service.daof.getHolderDAO();
			var holder = holderDAO.getHolderById(id);
			if (!holder || holder.subjectId != subject.getId()) {
				throw new GPError(module.id, GPError.INVALID_DATA, 0, "Holder referred to by argument 'holder' does not belong to subject referred to by argument 'for'");
			}
		}

		if (!this.hasAccessToSubject(op.process, subject, holder)) {
			var page = <p>You are not authorized to do that</p>
		}
	}

	if (!page) {
		var ctrl = factory.getControllerForProcess(op.process);

		if (ctrl) {
			var page = ctrl.getCreateView(req, res, this, subject, holder);
		} else {
			var page = <p>No viewer found for { op.process } </p>
		}
	}

	var t = this.ui.generateTemplate(req.url);
 	var c = t..div.(@id == "content");
 	c.div = page;

	return t;
}



ServiceRequestController.prototype.getSubjectInfo = function(subjectId) {
	if (subjectId) {
		var subject = this.service.getSubject(subjectId);
		if (subject.getName()) {
			var value = subject.getName();
		} else {
			var value = subject.getEmail();
		}

		return value;
	}

	return "";
}



ServiceRequestController.prototype.getSubjectLink = function(url, subjectId) {
	if (subjectId) {
		var value = this.getSubjectInfo(subjectId);
		return <a href={url + "/subject/" + subjectId}>{value}</a>;
	}

	return "";
}



ServiceRequestController.prototype.getProcessView = function(req, res, sr) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	var title = sr.bo.title;
	if (sr.bo.details) {
		title += " (" + sr.bo.details + ")";
	}

	var page =	<div>
				<h1>{ title }</h1>
				<div id="fromto" />
				<div id="form" />
				<br/>
			</div>;

	var factory = this.service.pluginRegistry.getFactoryForProcess(sr.bo.process);
	if (!factory) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Can not find factory for process " + sr.bo.process);
	}

	var originatorId = sr.getOriginatorId();
	var recipientId = sr.getRecipientId();

	var url = this.ui.homeURL(req.url);
	var fromto;

	if (originatorId) {
		if (recipientId) {
			var fromto = <p class="fromto">From {this.getSubjectLink(url, originatorId)} to {this.getSubjectLink(url, recipientId)}</p>;
		} else {
			var fromto = <p class="fromto">From {this.getSubjectLink(url, originatorId)}</p>;
		}
	} else {
		if (recipientId) {
			var fromto = <p class="fromto">{this.getSubjectLink(url, recipientId)}</p>;
		}
	}

	if (fromto) {
		var div = page..div.(@id == "fromto");
		div.appendChild(fromto);
	}

	var ctrl = factory.getControllerForProcess(sr.bo.process);

	if (ctrl) {
		var form = ctrl.getProcessView(req, res, this, sr);
	} else {
		var form = <p>No viewer found for { sr.bo.process } </p>
	}
	var div = page..div.(@id == "form");
	div.appendChild(form);

	var operation = CommonUI.parseQueryString(req.queryString);
	var expand = "";
	if (operation.state) {
		expand = operation.state;
	}

	var related = this.renderRelated(req, res, sr);
	div.appendChild(related);

	var history = this.renderHistory(req, res, sr, expand);
	div.appendChild(history);

	var t = this.ui.generateTemplate(req.url);
 	var c = t..div.(@id == "content");
 	c.div = page;

	return t;
}



ServiceRequestController.prototype.reportError = function(req, res, msg) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	var page = <p> { msg }</p>
	this.ui.sendPage(req, res, req.url, page);
}



ServiceRequestController.prototype.handleRequest = function(req, res) {
	GPSystem.log(GPSystem.DEBUG, module.id, "handleRequest()");
	GPSystem.log(GPSystem.DEBUG, module.id, "req.method:" + req.method);
	GPSystem.log(GPSystem.DEBUG, module.id, "req.queryString:" + req.queryString);

	if (req.url.length < 3) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Invalid URL");
	}

	if (req.url[2] == "new") {
		if (req.method == "POST") {
			this.postCreateAction(req, res);
		} else {
			var page = this.getCreateView(req, res);
			this.ui.sendPage(req, res, req.url, page);
		}
	} else {
		var sr = this.getServiceRequest(req, res);
		if (!sr) {
			this.reportError(req, res, "No factory found for service request");
			return;
		}

		if (req.method == "POST") {
			if (!this.ui.verifySessionToken(new ByteString(req.parameters._csrf, HEX))) {
				res.setStatus(HttpResponse.SC_FORBIDDEN);
				this.reportError(req, res, "The form has already been posted and is expired");
				return;
			}

			if (!sr.canRead(this.ui.session.user)) {
				res.setStatus(HttpResponse.SC_FORBIDDEN);
				this.reportError(req, res, "You are not authorized to access this service request");
				return;
			}
			this.postProcessAction(req, res, sr);
		} else {
			if (!sr.canRead(this.ui.session.user)) {
				this.ui.serveNotAuthorizedPage(req, res, req.url);
				return;
			}

			var page = this.getProcessView(req, res, sr);
			this.ui.sendPage(req, res, req.url, page);
		}
	}
}



ServiceRequestController.prototype.restfulGET = function(req, res, sr) {
	var form = sr.getForm(this.ui.session.user);
	var actionList = sr.getActionList(this.ui.session.user);

	var srdao = this.service.daof.getServiceRequestDAO();
	var srStates = srdao.listServiceRequestStates(sr.getId());
	var history = [];
	for (var i = 0; i < srStates.length; i++) {
		var state = {};
		state.caseHandlerId = srStates[i].caseHandlerId;
		state.transitionTime = srStates[i].transitionTime;
		state.lifecycle = srStates[i].lifecycle;
		state.state = srStates[i].state;
		state.content = srStates[i].serialize();

		history.push(state);
	}

	var o = {
		  id: sr.getId(),
		  creationTime: sr.getCreationTime(),
		  process: sr.getProcess(),
		  statusInfo: sr.getStatusInfo(),
		  lifeCycle: sr.getLifeCycle(),
		  originatorId: sr.getOriginatorId(),
		  recipientId: sr.getRecipientId(),
		  actionList: actionList,
		  form: form,
		  model: sr.model,
		  history: history
	};

	if (sr.bo.details) {
		o.details = sr.bo.details;
	}

	var str = JSON.stringify(o, null, "\t");

	res.setContentType("application/json");
	res.setStatus(HttpResponse.SC_OK);
	res.println(str);
}



ServiceRequestController.prototype.restfulGETServiceRequestList = function(req, res, selector) {
	GPSystem.log(GPSystem.DEBUG, module.id, "restfulGETServiceRequestList( selector=" + selector + ")");
	var offset = undefined;
	var pagesize = undefined;

	var srlist;
	if (selector == ServiceRequestBrowser.MYREQUESTS) {
		srlist = this.ui.myServiceRequestBrowser.listRequests(offset, pagesize);
	} else if (selector == ServiceRequestBrowser.REQUESTSFORME) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Not implemented");
	} else if (selector == ServiceRequestBrowser.ASSIGNEDTOME) {
		srlist = this.ui.inboundServiceRequestBrowser.listRequests(offset, pagesize);
	} else if (selector == ServiceRequestBrowser.INVOLVESME) {
		srlist = this.ui.involvedServiceRequestBrowser.listRequests(offset, pagesize);
	} else if (selector == ServiceRequestBrowser.PRINCIPALOUTREQUESTS) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Not implemented");
	} else if (selector == ServiceRequestBrowser.PRINCIPALINREQUESTS) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Not implemented");
	} else {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Selector not specified");
	}

	var idlist = [];
	for (var i = 0; i < srlist.length; i++) {
		idlist.push(srlist[i].id);
	}

	var o = {
		  serviceRequests: idlist
	};

	var str = JSON.stringify(o, null, "\t");

	res.setContentType("application/json");
	res.setStatus(HttpResponse.SC_OK);
	res.println(str);
}



ServiceRequestController.prototype.restfulPostCreateAction = function(req, res) {
	var op = CommonUI.parseQueryString(req.queryString);
	var factory = this.service.pluginRegistry.getFactoryForProcess(op.process);
	if (!factory) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Can not find factory for process " + op.process);
	}

	var ctrl = factory.getControllerForProcess(op.process);

	if (ctrl.restfulPostCreateAction) {
		var obj = ctrl.restfulPostCreateAction(req, res, this, req.parameters.action);

		res.setContentType("application/json");
		if (obj) {
			var str = JSON.stringify(obj, null, "\t");
			res.setStatus(HttpResponse.SC_CREATED);
		} else {
			var str = "";
			res.setStatus(HttpResponse.SC_UNAUTHORIZED);
		}

		res.println(str);
	} else {
		res.setContentType("application/json");
		res.setStatus(HttpResponse.SC_NOT_IMPLEMENTED);
	}
}



ServiceRequestController.prototype.restfulPostProcessAction = function(req, res, sr) {
	GPSystem.log(GPSystem.DEBUG, module.id, "restfulPostProcessAction");
	var factory = this.service.pluginRegistry.getFactoryForProcess(sr.bo.process);
	if (!factory) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Can not find factory for process " + sr.bo.process);
	}

	var ctrl = factory.getControllerForProcess(sr.bo.process);

	if (ctrl.restfulPostProcessAction &&
		ctrl.restfulPostProcessAction(req, res, this, sr, req.parameters.action)) {
		return;
	}

	res.setStatus(HttpResponse.SC_SEE_OTHER);
	res.addHeader("Location", req.nativeRequest.getContextPath() + req.servletPath + req.pathInfo);
}



ServiceRequestController.prototype.handleRestRequest = function(req, res) {
	GPSystem.log(GPSystem.DEBUG, module.id, "handleRestRequest()");

	if (req.url.length < 3) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Invalid URL");
	}

	if (req.url[2] == "new") {
		if (req.method == "POST") {
			this.restfulPostCreateAction(req, res);
		}
	} else if (req.url[2] == "list") {
		res.setContentType("application/json");
		res.setStatus(HttpResponse.SC_NOT_IMPLEMENTED);
// 		var operation = CommonUI.parseQueryString(req.queryString);
// 		var selector = parseInt(operation.selector);
// 		this.restfulGETServiceRequestList(req, res, selector);
	} else {
		var sr = this.getServiceRequest(req, res);
		if (!sr) {
			return;
		}

		if (!sr.canRead(this.ui.session.user)) {
			res.setStatus(HttpResponse.SC_FORBIDDEN);
			return;
		}

		if (req.method == "POST") {
			this.restfulPostProcessAction(req, res, sr);
		} else {
			this.restfulGET(req, res, sr);
		}
	}
}
