/**
 *  ---------
 * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
 * |#       #|
 * |#       #|  Copyright (c) 1999-2017 CardContact Software & System Consulting
 * |'##> <##'|  Andreas Schwier, 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 Service request model for enrolling persons and systems
 */

var ServiceRequestModel		= require('pki-as-a-service/service/ServiceRequestModel').ServiceRequestModel;
var IssueTokenRequestModel	= require('pki-as-a-service/processes/IssueTokenRequestModel').IssueTokenRequestModel;
var SmartCardHSM		= require('scsh/sc-hsm/SmartCardHSM').SmartCardHSM;
var CryptoProvider		= require('scsh/sc-hsm/CryptoProvider').CryptoProvider;



/**
 * Data model for enrolling a person or system
 *
 *
 * @constructor
 */
function EnrollServiceRequestModel(service, bo) {
	IssueTokenRequestModel.call(this, service, bo);
	this.validationResult = [];
}

EnrollServiceRequestModel.prototype = Object.create(IssueTokenRequestModel.prototype);
EnrollServiceRequestModel.constructor = EnrollServiceRequestModel;

exports.EnrollServiceRequestModel = EnrollServiceRequestModel;



EnrollServiceRequestModel.ERROR_DUPLICATE_NAME = "Error: Email address already asigned to another person";
EnrollServiceRequestModel.ERROR_DUPLICATE_EMAIL = "Error: Name address already asigned to another system";



EnrollServiceRequestModel.prototype.getForm = function(user) {
	if (this.form == undefined) {
		this.form = [];

		if (this.bo.lifecycle < ServiceRequestModel.LIFECYCLE_COMPLETE) {
			var editable = ((user.id == this.getOriginatorId()) && (this.bo.lifecycle <= ServiceRequestModel.LIFECYCLE_EDIT));
			var emaileditable = editable && (this.model.registerTokenSRID == undefined);

			var fields = [];
			fields.push({ id: "name", label: "msg.name", type: "text", editable: editable, required: false, value: (this.model.name ? this.model.name : "")});
			if (this.model.type == "System") {
				fields[0].required = true;
			}

			if (this.model.type == "Person") {
				fields.push({ id: "email", label: "msg.email", type: "email", editable: emaileditable, required: true, value: (this.model.email ? this.model.email : "")});
			}

			this.form.push( {
				id: "subject",
				legend: "msg.subject",
				fields: fields
				}
			);


			var issuerList = [];

			issuerList.push( { id: 0, value: "No certificate issued", selected: !this.model.issuer });

			var trustCenter = this.getRecipient();
			var issuerModel = {
				optgroup: true,
				label: trustCenter.getName(),
				value: []
			};
			var caList = trustCenter.getCAList(user);
			for (var i = 0; i < caList.length; i++) {
				var ca = caList[i];

				issuerModel.value.push( { id: ca.id, value: ca.getReadablePath(), selected: ca.id == this.model.issuer } ) ;
			}
			issuerList.push(issuerModel);

			this.form.push( {
				id: "issuer",
				legend: "msg.it.issuer",
				fields: [
					{ id: "issuer", label: "msg.it.ca", type: "select", editable: editable, value: issuerList },
				]
			});

		}

		if (this.bo.lifecycle >= ServiceRequestModel.LIFECYCLE_EDIT) {
			this.form.push(this.getTokenInfoSection());
		}

		if (this.getInitializationFieldset()) {
			this.form.push(this.getInitializationFieldset());
		}
	}

	return this.form;
}



EnrollServiceRequestModel.prototype.getActionList = function(user) {
	if (this.actionList != undefined) {
		return this.actionList;
	}

	var superActionList = IssueTokenRequestModel.prototype.getActionList.call(this, user);
	GPSystem.log(GPSystem.DEBUG, module.id, "### superActionList : " + superActionList);

	this.actionList = [];

	if (user.id == this.getOriginatorId()) {
		if (this.bo.lifecycle == ServiceRequestModel.LIFECYCLE_NEW) {
			this.actionList.push("action.create");
		}
	}

	GPSystem.log(GPSystem.DEBUG, module.id, "### actionList : " + this.actionList);

	this.actionList = this.actionList.concat(superActionList);
	GPSystem.log(GPSystem.DEBUG, module.id, "### concatenate : " + this.actionList);

	return this.actionList;
}



EnrollServiceRequestModel.prototype.checkEmail = function(fld, email) {
	GPSystem.log(GPSystem.DEBUG, module.id, "checkEmail( " + email + " )");

	if (this.model.email == email) {
		// Return true to allow modifications of the name
		return true;
	}

	if (email.length <= 0 ||
		(email.match(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+/) == null) ||
		(email.match(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+/)[0] != email)) {
			this.addMessage("Failed - Invalid e-mail address " + email);
			fld.message = "Invalid e-mail address " + email;
			GPSystem.log(GPSystem.DEBUG, module.id, "Invalid e-mail address " + email);
			return false;
	}

	var subjectDAO = this.service.daof.getSubjectDAO();
	var subject = subjectDAO.getSubjectByEmail(email);
	if (subject) {
		fld.message = "The email " + email + " was already assigned. Please choose a different email.";
		return false;
	}

	this.model.email = email;

	return true;
}



EnrollServiceRequestModel.prototype.checkName = function(fld, name) {
	GPSystem.log(GPSystem.DEBUG, module.id, "checkName( " + name + " )");

	name = name.trim();

	if (!name) {
		if (this.model.type == "System") {
			fld.message = "The name must not be empty"
			return false;
		} else {
			this.model.name = "";
			return true;
		}
	}

	if (this.model.name == name) {
		if (this.model.type == "System") {
			/*
			 * The system subject is identified only by name.
			 * If the name doesn't change then no database
			 * update is necessary.
			 */
			return false;
		} else {
			/*
			 * Return true to allow modifications of the email
			 */
			return true;
		}
	}

	var subjectDAO = this.service.daof.getSubjectDAO();
	var subject = subjectDAO.getSubjectByTypeAndName(this.model.type, name);
	if (subject) {
		fld.message = "The name " + name + " was already assigned. Please choose another name.";
		return false;
	}

	this.model.name = name;

	return true;
}



EnrollServiceRequestModel.prototype.validateField = function(fld, val) {
	switch(fld.id) {
		case "name":
			return this.checkName(fld, val);
		case "email":
			return this.checkEmail(fld, val);
		default:
			return this.validateFieldDefault(fld, val);
	}
	return true;
}



EnrollServiceRequestModel.prototype.perform = function(user, action) {
	GPSystem.log(GPSystem.DEBUG, module.id, "perform(" + user.id + "," + action + ")");

	var actionList = this.getActionList(user);

	if (actionList.indexOf(action) == -1) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Action " + action + " unknown");
	}

	switch(action) {
		case "action.create":
			this.createSubject()
			this.updateDetails();
			this.setLifeCycle(ServiceRequestModel.LIFECYCLE_EDIT);
			this.setStatusInfo("Request data entered");
			break;
		case "action.save":
			var editable = ((user.id == this.getOriginatorId()) && (this.bo.lifecycle <= ServiceRequestModel.LIFECYCLE_EDIT));
			if (editable) {
				this.updateSubject();
			}
			break;
	}

	// commit is performed in super class
	IssueTokenRequestModel.prototype.perform.call(this, user, action);

	return true;
}



/**
 *
 * @param {String} email
 */
EnrollServiceRequestModel.prototype.createSubject = function() {
	GPSystem.log(GPSystem.DEBUG, module.id, "createSubject(" + this.model.email + "," + this.model.name + ")");

	if (!this.model.email & !this.model.name) {
		throw new GPError(module.id, GPError.INVALID_DATA, 0, "Either a name or an email is required");
	}

	var template = {
		type: this.model.type,
		managedByRoleId: this.getRecipient().getRARoleId(),
		serviceRequestId: this.getId()
	};
	if (this.model.email) {
		template.email = this.model.email;
	}
	if (this.model.name) {
		template.name = this.model.name;
	}

	// A person enrolled by a trust center has a higher assurance than
	// a self-registered user
	if (this.model.type == "Person") {
		template.assuranceLevel = 1;
	}

	var subjectDAO = this.service.daof.getSubjectDAO();
	var subject = subjectDAO.newSubject(template);

	this.addMessage("Create new subject " + subject);

	this.model.subjectId = subject.id;
}



EnrollServiceRequestModel.prototype.updateDetails = function() {
	if (this.model.name) {
		var details = this.model.name;
	} else if (this.model.email) {
		var details = this.model.email;
	} else {
		return;
	}

	if (details == this.bo.details) {
		return;
	}

	var srDAO = this.service.daof.getServiceRequestDAO();
	srDAO.updateDetails(this.bo, details);
}



/**
 *
 * @param {String} email
 */
EnrollServiceRequestModel.prototype.updateSubject = function() {
	GPSystem.log(GPSystem.DEBUG, module.id, "updateSubject(" + this.model.email + "," + this.model.name + ")");

	var subjectDAO = this.service.daof.getSubjectDAO();
	var enrollSubject = subjectDAO.getSubject(this.model.subjectId);

	if (typeof(this.model.email) != "undefined") {
		subjectDAO.updateEmail(enrollSubject, this.model.email);
	}

	subjectDAO.updateName(enrollSubject, this.model.name);

	this.addMessage("Updated subject " + enrollSubject);
}
