/**
 *  ---------
 * |.##> <##.|  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 Trust Center Subject
 */

var SubjectModel			= require('pki-as-a-service/service/SubjectModel').SubjectModel;
var X509CertificateIssuer		= require('scsh/x509/X509CertificateIssuer').X509CertificateIssuer;
var CryptoProvider			= require('scsh/sc-hsm/CryptoProvider').CryptoProvider;



/**
 * Data model for X509 CA Subject
 *
 *
 * @constructor
 */
function TrustCenter(service, bo) {
	SubjectModel.call(this, service, bo);
}

TrustCenter.prototype = Object.create(SubjectModel.prototype);
TrustCenter.constructor = TrustCenter;

exports.TrustCenter = TrustCenter;



TrustCenter.prototype.getRARoleId = function() {
	var camodel = this.bo.getContent();
	return camodel.raRoleId;
}



TrustCenter.prototype.isRegistrationOfficer = function(user) {
	return (typeof(user.roles) != "undefined") && (user.roles.indexOf(this.getRARoleId()) >= 0);
}



TrustCenter.prototype.getCARoleId = function() {
	var camodel = this.bo.getContent();
	return camodel.caRoleId;
}



TrustCenter.prototype.isCertificationOfficer = function(user) {
	return (typeof(user.roles) != "undefined") && (user.roles.indexOf(this.getCARoleId()) >= 0);
}



TrustCenter.prototype.getManagerRoleId = function() {
	var camodel = this.bo.getContent();
	return camodel.managerRoleId;
}



TrustCenter.prototype.canRead = function(user) {
	if (SubjectModel.prototype.canRead.call(this, user)) {
		return true;
	}

	if (this.isRegistrationOfficer(user)) {
		return true;
	}

	if (this.isCertificationOfficer(user)) {
		return true;
	}

	return false;
}



/**
 * Check if this TrustCenter is manager of the given user
 */
TrustCenter.prototype.isManagerOf = function(user) {
	if (this.getRARoleId() == user.managedByRoleId) {
		return true;
	}

	return false;
}



TrustCenter.prototype.getKeyDomain = function() {
	var camodel = this.bo.getContent();
	return new ByteString(camodel.keyDomain, HEX);
}



TrustCenter.prototype.getCryptoProvider = function(keyDomain, requireLogin) {
	GPSystem.log(GPSystem.DEBUG, module.id, "getCryptoProvider(" + keyDomain.toString(HEX) + ")");
	var tokenList = this.getToken();
	for (var i = 0; i < tokenList.length; i++) {
		var token = tokenList[i];
		hsm = this.service.hsmService.getHSMState(token.path);
		if (!hsm || hsm.isOffline()) {
			GPSystem.log(GPSystem.DEBUG, module.id, "Skip offline HSM " + token.path);
			continue;
		}

		if (requireLogin && !hsm.isUserAuthenticated()) {
			GPSystem.log(GPSystem.DEBUG, module.id, "Skip unauthenticated HSM " + hsm);
			continue;
		}

		for (var j = 0; j < hsm.keyDomain.length; j++) {
			var kd = hsm.keyDomain[j];
			GPSystem.log(GPSystem.DEBUG, module.id, "Compare with kd " + kd.toString(HEX));
			if (kd.equals(keyDomain)) {
				var slot = j - 1; // -1 if default domain, otherwise the corresponding slot
				var cp = new CryptoProvider(hsm.sc, hsm.path, slot);
				return cp;
			}
		}
	}
}



TrustCenter.prototype.getX509CertificateIssuer = function(holderId) {
	GPSystem.log(GPSystem.DEBUG, module.id, "getX509CertificateIssuer()");

	var holderDAO = this.service.daof.getHolderDAO();
	var holder = holderDAO.getHolderById(holderId);
	if (!holder) {
		throw new GPError(module.id, GPError.INVALID_DATA, 1, "CA holder " + holderId + " not found");
	}

	var ca = new X509CertificateIssuer(this.service.daof, this, holder);

	var crldp = this.getCRLDistributionPoint(ca);
	if (crldp) {
		ca.addCRLDistributionPoint(crldp);
	}

	return ca;
}



TrustCenter.prototype.getSigner = function(holderId) {
	var holderDAO = this.service.daof.getHolderDAO();
	var certdao = this.service.daof.getCertificateDAO();
	var signerDAO = this.service.daof.getSignerDAO();

	var holder = holderDAO.getHolderById(holderId);
	var certificate = certdao.getCurrentCertificate(holder);

	if (!certificate) {
		throw new GPError(module.id, GPError.INVALID_DATA, 1, "No active signer found for holder " + holder);
	}

	var keyId = certificate.keyId;
	var signer = signerDAO.getSignerByKeyId(holder, keyId);

	return signer;
}



TrustCenter.prototype.getCRLDistributionPoint = function(ca) {
	try {
		var signer = ca.getSigner();
		var crlDP = ApplicationServer.instance.serverURL + "/se/crl/" + signer.id;

		return crlDP;
	} catch (e) {
		return null;
	}
}



TrustCenter.prototype.getSubjectPolicyForRequest = function() {
	GPSystem.log(GPSystem.DEBUG, module.id, "getSubjectPolicyForRequest()");

	var c = this.bo.getContent();

	var dp = new Key();
	if (c.subKeySpec.type == "EC") {
		dp.setComponent(Key.ECC_CURVE_OID, new ByteString(c.subKeySpec.curve, OID));
	} else {
		dp.setSize(c.subKeySpec.keysize);
	}

	var policy = {
		signatureAlgorithm: c.keyspec.sigalg,
		keySpecification: dp
	}

	return policy;
}



TrustCenter.prototype.getToken = function() {
	var tokenDAO = this.service.daof.getTokenDAO();
	var list = tokenDAO.getTokenListBySubjectId(this.bo.id);

	return list;
}



TrustCenter.prototype.canEnroll = function(user) {
	if (this.isRegistrationOfficer(user)) {
		return true;
	}

	return false;
}



TrustCenter.prototype.getHolderList = function() {
	var holderDAO = this.service.daof.getHolderDAO();
	var list = holderDAO.getHolderListBySubject(this.getId());

	return list;
}



/**
 * Get a list of CA holder, i.e. holder with a corresponding signer.
 */
TrustCenter.prototype.getCAList = function() {
	var holderDAO = this.service.daof.getHolderDAO();
	var caList = holderDAO.getHolderListBySubject(this.getId());

	// Filter holder without a signer.
	// This can happen if the corresponding service request has not been finished yet.
	var result = [];
	for (var i = 0; i < caList.length; i++) {
		var holder = caList[i];

		if (holder.certificateType == 2 && holder.certId) {
			result.push(holder);
		}
	}

	return result;
}



/**
 * Get a list of trusted certificate ids
 * which were imported by ImportTrustedCertificatesServiceRequest
 */
TrustCenter.prototype.getTrustedCertificates = function() {
	var camodel = this.bo.getContent();

	if (!camodel.trustedCA) {
		return [];
	}

	return camodel.trustedCA;
}
