/**
 *  ---------
 * |.##> <##.|  SmartCard-HSM Support Scripts
 * |#       #|
 * |#       #|  Copyright (c) 2011-2012 CardContact Software & System Consulting
 * |'##> <##'|  Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
 *  ---------
 *
 * Consult your license package for usage terms and conditions.
 *
 * @fileoverview Personalize Card
 */


function PersoManager(af, cardprofile) {
	this.af = af;
	this.good = 0;
	this.bad = 0;
	this.cardprofile = cardprofile;
}



PersoManager.parseCardCustomizationRequest = function(cardcustomizationrequest) {

	var parser = new GPXML();
	parser.defineArrayElement("/CardCustomization/ModuleIdentifierCode", "ApplicationData");
	parser.defineArrayElement("/CardCustomization/ModuleIdentifierCode/ApplicationData", "ProcessingStep");
	parser.defineArrayElement("/CardCustomization/ModuleIdentifierCode/ApplicationData/ICCData", "DataSet");
	parser.defineArrayElement("/CardCustomization/ModuleIdentifierCode/ApplicationData/ICCData/DataSet", "Data");
	parser.defineScriptElement("//");

	return parser.parse(cardcustomizationrequest);
}



/**
 * Run script from application profile with application data provided
 *
 * @param {ApplicationData} ad the application data to be used when running the script.
 * @param {Card} card the card object to use
 */
PersoManager.prototype.runScript = function (ad, card, ct) {
	// AID associated with application
	var aid = ad.getAID();

	// ID of profile for application instance
	var id = ad.getProfileID();

	var dm = ad.getDataMapper();

	// Create the application object from the profile
	var appl = this.af.getApplicationInstance(dm, aid, card, id);

	var i = 0;
	do	{
		var script = ad.getScriptName(i);
		if (script) {
			ct.sendNotification(1, script);
			appl.run(script);
		}
		i++;
	} while (script);
}



PersoManager.prototype.handleCard = function(readername, job) {
	var card = new Card(readername, this.cardprofile);
	ctr = ctr = Packages.opencard.core.terminal.CardTerminalRegistry.getRegistry();
	ct = ctr.cardTerminalForName(readername);

	try	{
		var adl = job.ccr.ModuleIdentifierCode.ApplicationData;
		for (var i = 0; i < adl.length; i++) {
			var ad = new ApplicationData(adl[i]);
			this.runScript(ad, card, ct);
		}
		ct.sendNotification(0, "OK");
		this.good++;
	}
	catch(e) {
		ct.sendNotification(-1, e);
		this.bad++;
		GPSystem.trace(e);
	}
	finally {
		card.close();
	}
}



/**
 * Create an ApplicationData object containing the application data.
 *
 * @class <p>The ApplicationData class holds the inbound and outbound
 *           application data during script execution.</p>
 * @constructor
 * @param {Object} xml the XML tree of objects from the deserialized request
 */
function ApplicationData (xml) {
	this.xml = xml;
}



/**
 * Gets the AID from application data
 *
 * @return the AID
 * @type ByteString
 */
ApplicationData.prototype.getAID = function() {
	return new ByteString(this.xml.AID.AID, HEX);
}



/**
 * Gets the profile ID from application data
 *
 * @return the Profile ID
 * @type ByteString
 */
ApplicationData.prototype.getProfileID = function() {
	return new ByteString(this.xml.ApplicationProfileUniqueID.elementValue, HEX);
}



/**
 * Get the script name for the specified processing step
 *
 * @param {Number} index the index into the list of processing steps
 * @return the processing steps script name or null
 * @type String
 */
ApplicationData.prototype.getScriptName = function(index) {
	var ps = this.xml.ProcessingStep[index];

	if (!ps) {
		return null;
	}
	return ps.Script.elementValue;
}



/**
 * Create a data mapper for this application data
 *
 * @return the data mapper object
 * @type DataMapper
 */
ApplicationData.prototype.getDataMapper = function() {
	if (!this.dm) {
		this.dm = new DataMapper(this.xml.ICCData);
	}
	return this.dm;
}



/**
 * Create a data mapper object that is used by script execution
 * to retrieve and store data elements
 *
 * @class <p>The DataMapper is a container for data elements using during
 *           the execution of scripts in an application profile.</p>
 *
 * @constructor
 * @param {Object} xml the XML tree containing the DataSet node
 */
function DataMapper(xml) {
	this.xml = xml;
	this.data = new Array();		// Data repository
	this.response = new Array();	// List of updated fields

	for (var i = 0; i < this.xml.DataSet.length; i++) {
		var dataset = this.xml.DataSet[i];
		for (var j = 0; j < dataset.Data.length; j++) {
			this.data[dataset.Data[j].DataElement] = dataset.Data[j].Value;
		}
	}
}



/**
 * Gets a data element from application data.
 *
 * This method is called from the application instance when a script execution
 * requires a data element.
 *
 * @param {String} name the name of the data element
 * @param {Boolean} fixed true if the length of the data element is fixed
 * @param {Number} length the maximum or fixed length of the data element
 * @param {Number} encoding the encoding as per application profile
 * @return the data element
 * @type ByteString
 */
DataMapper.prototype.get = function(name, fixed, length, encoding) {
	var d = this.data[name];
	if (!d) {
		return null;
	}
	if (encoding == -1) {
		encoding = HEX;	// Default encoding is HEX unless specified in profile
	}
	return new ByteString(d, encoding);
}



/**
 * Saves a data element modified or created by script
 *
 * @param {String} name the name of the data element
 * @param {ByteString} value the value of the data element
 * @param {Number} encoding the encoding as per application profile
 */
DataMapper.prototype.put = function(name, value, encoding) {
	if (encoding == -1) {
		encoding = HEX;	// Default encoding is HEX unless specified in profile
	}

	this.data[name] = value.toString(encoding);
	this.response[name] = this.data[name];
}
