vds/VdsClient.js

/**
 * @fileoverview Client layer for VDS. The VDS stack will roughly consist of
 *               Layer 1: GUI, Layer 2: Client, Layer 3: Server
 *
 * NOTE: All public/exported functions in this module should implement runtime
 *       type checking for arguments to allow easier debugging when calling
 *       from uncompiled code.
 *
 * @author anders.rejdebrant@spark-vision.com (Anders Rejdebrant)
 */

goog.provide('spv.vds.VdsClient');

goog.require('spv.assert');
goog.require('spv.ds.assert');
goog.require('spv.vds.assert');
goog.require('spv.ds.DsClient');
goog.require('spv.ds.DsClientSettings');
goog.require('spv.ds.impl.GoalStateResolver');
goog.require('spv.ds.impl.SessionInitData');
goog.require('spv.ds.impl.SessionState');
goog.require('spv.vds.ServiceInfo');
goog.require('spv.vds.CCInputParam');
goog.require('spv.vds.VolvoCarSpec');
goog.require('spv.vds.LeasingPriceResultStatus');
goog.require('spv.vds.impl.IPPNovaAdaptor');



/**
 * VdsClient provides a layer of abstraction between GUI code and
 * session-/protocol-handling.
 *
 * The configurator system is stateful. Session state is shared between server
 * and client, this is a source of complexity and can hopefully be removed at
 * some point in the future. VdsClient provides synchronization of session state
 * between server and client by centralizing management of the shared state
 * inside this class.
 *
 * Member functions are stateful if the member function name begins with
 * "session". Other member functions are state- and session-less and can be
 * called at any time, in any order.
 *
 * Most session dependent functions also require a loaded configuration.
 * Preconditions should be documented for each member function in this class.
 *
 * The configurator system can be simplified as the following three phases:
 * A. The user finds the configurator application on the web or in the showroom.
 * B. The user choose and change the product components (called items) until
 *    the user is satisfied or leaved the application.
 * C. The user saves the configuration for a later time or choose to place an
 *    order or request a sales contact for the built configuration.
 *
 * Technical usage example 1, Starting a new configuration session and
 * interacting with the user. (Step-by-step example of phase B):
 *
 * 1. Construct an instance of spv.vds.Client
 * 2. Call sessionInit
 * 3. Call getIntroPage to retrieve the available start configurations
 * 4. Generate images for the start configurations using getConfigImage
 * 5. Display the available start configuration images to the user using the
 *    appropriate techniques for the GUI implementation.
 * 6. The user chooses a start configuration
 * 7. Send that start configuration to sessionLoadConfig
 * 8. On success, call sessionGetRootMenuItems and sessionGetImage
 * 9. Populate the GUI root menu with menu items and wait for the image response
 * 10. On image success, load and display the configuration image
 * 11. The user makes a choice about which menu to open
 * 12. Call sessionToggleItem for the user chosen MenuItem
 *     (both menus and product components are MenuItems)
 * 13. On success, call sessionGetStateUpdate to populate the currently open
 *     menu with menu items.
 * 14. The user clicks an item in the currently open menu, send that MenuItem to
 *     sessionToggleItem and handle response.
 * 15. On success, update the currently open menu with new MenuItems (most menu
 *     items will usually be unchanged, but some may change state and some may
 *     disappear while others may be added). Call sessionGetImage to get the new
 *     configuration image and wait for response.
 * 16. On image success, load and display the configuration image
 * 17. The user clicks a new item and the sessionToggleItem cycle continues
 *     until the user closes the application or decides to place an order for
 *     the built configuration. (this step is customized action for each
 *     configurator application, eg. cars and bathroom products require
 *     different types of shopping cart solutions)
 *
 *
 *
 *
 * @export
 * @constructor
 * @extends {spv.ds.DsClient}
 * @param {!string} service_url
 * @param {spv.ds.DsClientSettings=} in_settings
 */
spv.vds.VdsClient = function(service_url, in_settings)
{
	var default_settings = new spv.ds.DsClientSettings();
	default_settings.use_jsonp = true;
	default_settings.debug_print = false;

	/** @type !spv.ds.DsClientSettings */
	var settings = in_settings ? default_settings.overloadWith( in_settings ) : default_settings;

	spv.ds.DsClient.call( this, service_url, settings );

	/**
	 * @private
	 * @type {string}
	 */
	this._care_send_customer_order_form_url = service_url + '/api/volvo_care_send_customer_order';

	/**
	 * @private
	 * @type {spv.vds.impl.IPPNovaAdaptor}
	 */
	this._vds_proto = new spv.vds.impl.IPPNovaAdaptor(service_url);
};

goog.inherits(spv.vds.VdsClient, spv.ds.DsClient);


/**
 * This method tries to deduce is there's a local ashx file that could be used
 * instead of calling relay service.
 * It first checks if it's called from localhost/ and then recursively checks
 * the parent paths for the .ashx file.
 * @private
 * @param {function(string?)} on_completed
 */
spv.vds.VdsClient.deduceLocalService = function( on_completed )
{
	var url_check = function ( urls, on_completed )
	{
		if( !urls || urls.length == 0 )
			return on_completed(null);
		var url = urls.shift();
		console.log( "Checking url: " + url );
		goog.net.XhrIo.send( url, function(e)
			{
				e.preventDefault();
				console.log( "Check success: " + e.target.isSuccess() );
				console.log( "Check status: " + e.target.getStatus() );
				if( e.target.getStatus() == 500 || e.target.getStatus() == 200 || e.target.getStatus() == -1 )
					on_completed( url );
				else
					url_check( urls, on_completed );
			},
			"HEAD",
			"", {}, 500);
	}

	var url = window.location.href.split("#")[0].split("?")[0];
	if( url.match( /localhost.*/ ) || url.match( /127\.0\.0\.1/ ) )
	{
		var split_url = url.split("/");
		var service_file = "VolvoBoosterJson.ashx"
		var search_urls = [];

		while( split_url.length > 3 ) /* http:, , host, ... */
		{
			split_url.pop();
			search_urls.push( split_url.join("/") + "/" + service_file );
		}

		url_check( search_urls, on_completed );
	}
	else
		on_completed( null );
};

/**
 * Requests a specific service from a load balancing relay service and
 * returns the url via the on_success callback.
 *
 *
 * @export
 * @param {string} service_group_id
 * @param {function(spv.vds.ServiceInfo)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.requestServiceInfo = function( service_group_id, on_success, on_failure )
{
	var call_relay_service = function( service_group_id, on_success, on_failure )
	{
		var service = "//vds.spark-vision.com/relay/RelayService.ashx/json_api/request_service";
		var req_url = service + "?service_group_id=" + service_group_id;

		goog.net.XhrIo.send( req_url, function(e) {
			var xhr = e.target;
			if( xhr.isSuccess() )
			{
				var obj = xhr.getResponseJson();
				var data = obj["request_service"]["data"];
				var url = data["json_api_url"];
				var message = data["intro_dialog_message"] || null;
				var blocking = data["intro_dialog_blocking"] == true;
				on_success( new spv.vds.ServiceInfo( url, message, blocking ) );
			}
			else
			{
				on_failure( new Error( "Relay service error (" + xhr.getStatusText() + "):" + xhr.getResponseText() ) );
			}
		});
	};

	spv.vds.VdsClient.deduceLocalService( function( found ) {
		if( found )
		{
			console.log( "Found local service. " + found );
			on_success( new spv.vds.ServiceInfo( found, "", false ) );
		}
		else
		{
			console.log( "No local service found; calling relay service." );
			call_relay_service( service_group_id, on_success, on_failure );
		}
	});
};

/** METHODS FOR MANAGING VDS PRICES  */


/**
 * Preconditions: Initialized session.
 *
 * @export
 * @param {function(spv.vds.VdsPriceMode)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionGetCurrentPriceMode = function(
	on_success,
	on_failure)
{
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	var that = this;
	this._requestQueue.push(function(wrap) {
		that._vds_proto.sessionGetCurrentPriceMode(
			that.getSessionId(),
			wrap(on_success),
			wrap(on_failure)
		);
	});
}


/**
 * Preconditions: Initialized session.
 *
 * @export
 * @param {spv.vds.VdsPriceMode} price_mode
 * @param {spv.vds.SendBopUpdateFlag} send_update
 * @param {Array.<string>} optional_items
 * 		  Items that the user have chosen to trigger  with the new price mode
 * 		  that should be deactivated.
 * @param {function(string)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionSetPriceMode = function(
	price_mode,
	send_update,
	optional_items,
	on_success,
	on_failure)
{
	spv.vds.assert.isVdsPriceMode(price_mode, 'price_mode');
	spv.vds.assert.isSendBopUpdateFlag(send_update, 'send_update');
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	var that = this;
	var on_state_update = goog.bind(this._session_state.update, this._session_state);
	this._requestQueue.push(function(wrap) {
		that._vds_proto.sessionSetPriceMode(
			that.getSessionId(),
			price_mode,
			send_update,
			optional_items,
			on_state_update,
			wrap(on_success),
			wrap(on_failure));
	});
}

/**
 * Preconditions: Initialized session.
 * Setting price mode can potentially require changing the configuration since there can
 * be introduced new items and rules applicable only to this price mode and current configuration
 * might become invalid. This method attempts to set a new price mode and can activate the mode directly
 * or return back suggestions that should be applied to the configuration to make it valid and/or
 * any optional changes that were required in the current price mode but will become optional in the
 * new price mode.
 *
 *
 * @export
 * @param {spv.vds.VdsPriceMode} price_mode
 * @param {spv.vds.SendBopUpdateFlag} send_update
 * @param {function(Object)} on_success TODO: Use static typing!
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionAttemptSetPriceMode = function(
	price_mode,
	send_update,
	on_success,
	on_failure)
{
	spv.vds.assert.isVdsPriceMode(price_mode, 'price_mode');
	spv.vds.assert.isSendBopUpdateFlag(send_update, 'send_update');
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	var on_state_update = goog.bind(
		this._session_state.update,
		this._session_state);
	var that = this;
	/** @param {Object} data TODO: Use static typing! */
	var on_success_wrapper = function(data)
	{
		/**
		 * Processing of protocol data is the responsibility of
		 * IPPNovaAdaptor, TODO: Move this transform to IPPNovaAdaptor.
		 */
		if (data.required_changes && data.required_changes.multiple_resolvers) {
			data.required_changes.multiple_resolvers =
				that.constructSuggestions(data.required_changes.multiple_resolvers);
		}
		on_success(data);
	};
	this._requestQueue.push(function(wrap) {
		that._vds_proto.sessionAttemptSetPriceMode(
			that.getSessionId(),
			price_mode,
			send_update,
			on_state_update,
			wrap(on_success_wrapper),
			wrap(on_failure)
		);
	});
}




/**
 * Requests all available retailers
 *
 * Preconditions: None.
 *
 * @export
 * @param {function(Array.<spv.vds.VolvoRetailer>)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.getRetailers = function(on_success, on_failure)
{
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	this._vds_proto.getRetailers(on_success, on_failure);
};




/**
 * Get the VolvoCarSpec for a config.
 *
 * Preconditions: None.
 *
 * @export
 * @param {spv.ds.Config} config
 * @param {string} price_localization_id
 * @param {function(spv.vds.VolvoCarSpec)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.getSpecification = function(
		config,
		price_localization_id,
		on_success,
		on_failure)
{
	spv.ds.assert.isConfig(config, 'config');
	spv.assert.isNonEmptyString(price_localization_id, 'price_localization_id');
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	this._vds_proto.getSpecification(
			config,
			price_localization_id,
			on_success,
			on_failure);
};



/** Updates the given parameters for Company Cars pricing.
 *
 * This method always return the current company car parameter state. You may
 * pass an empty object to retrieve the available parameters and their current
 * value. The value are default initialized. You're only required to send the
 * changed paramaters. Unmentioned parameters will not change. The input
 * parameters are passed as a flat Object, basically a dictionary, with the
 * parameter names and values.
 *
 * The names and types for each parameter can be found in the CCInputParam
 * array returned by this function.
 *
 * Warning: This function may potentially generate very long URL queries if
 * jsonp is activated for the client.
 *
 * Preconditions: sessionEnableCCPrices must be called before using this
 * method.
 *
 * @export
 * @param {Object.<string,Object>} params TODO: Use static typing!
 * @param {spv.vds.SendBopUpdateFlag} send_update
 * @param {function(Object.<string,spv.vds.CCInputParam>)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionUpdateCCInputParams = function(
		params,
		send_update,
		on_success,
		on_failure)
{
	// TODO: Investigate appropriate assertion for the paramater 'params'.
	spv.vds.assert.isSendBopUpdateFlag(send_update, 'send_update');
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	var on_state_update = goog.bind(this._session_state.update, this._session_state);
	var that = this;
	this._requestQueue.push(function(wrap) {
		that._vds_proto.sessionUpdateCCInputParams(
			that.getSessionId(),
			params,
			send_update,
			on_state_update,
			wrap(on_success),
			wrap(on_failure)
		);
	});
};




/**
 * TODO: Write documentation with type annotations.
 */
spv.vds.VdsClient.prototype.sessionEnableCCPrices = function(
		on_success,
		on_failure )
{
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	var that = this;
	this._requestQueue.push(function(wrap) {
		that._vds_proto.sessionEnableCCPrices(
			that.getSessionId(),
			wrap(on_success),
			wrap(on_failure)
		);
	});
};




/**
 * TODO: Write documentation with type annotations.
 */
spv.vds.VdsClient.prototype.sessionDisableCCPrices = function(
		on_success,
		on_failure )
{
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');

	var that = this;
	this._requestQueue.push(function(wrap) {
		that.assertInitializedSession();
		that._vds_proto.sessionDisableCCPrices(
			that.getSessionId(),
			wrap(on_success),
			wrap(on_failure)
		);
	});
};



/**
 * Gets the current company cars information for the active configuration.
 *
 * @export
 * @param {function(Object)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionGetCCInfo = function(
		on_success,
		on_failure)
{
	var aux = this.sessionGetAuxiliaryData();
	var cc_aux = aux.bop_aux['volvo_company_cars'];

	if (cc_aux) {
		on_success( cc_aux );
	}
	else {
		on_failure(new Error('No company cars auxiliary available.'));
	}
};

/**
 * Gets model features for the active configuration.
 *
 * @export
 * @param {function(Object)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionGetModelFeatures = function(
		on_success,
		on_failure)
{
	var aux = this.sessionGetAuxiliaryData();
	var features_aux = aux.bop_aux['volvo_model_features'];

	if (features_aux) {
		on_success(features_aux);
	}
	else {
		on_failure(new Error('No volvo model features auxiliary available.'));
	}
};

/**
 * Gets the current private lease information for the active configuration.
 *
 * @export
 * @param {function(Object)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionGetPrivateLeaseInfo = function(
		on_success,
		on_failure)
{
	var aux = this.sessionGetAuxiliaryData();
	var pl_aux = aux.bop_aux['volvo_leasing'];

	if (pl_aux) {
		on_success( pl_aux );
	}
	else {
		on_failure(new Error('No private leasing parameters available.'));
	}
};

/**
 * Gets the current private leasing information for a MenuItem.
 *
 * @export
 * @param {!string} item_id
 * @param {function(Object)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionGetPrivateLeaseItemInfo = function(
	item_id,
	on_success,
	on_failure )
{
	var aux = this.sessionGetAuxiliaryData();
	var item_aux = aux.item_aux[item_id];

	var pl_aux = item_aux ? item_aux['volvo_leasing'] : null;

	if (pl_aux) {
		on_success(pl_aux);
	}
	else {
		on_failure(new Error('No private lease auxiliary available for item: ' + item_id + '.'));
	}
};

/**
 * Gets volvo item info for a MenuItem.
 *
 * @export
 * @param {!string} item_id
 * @param {function(Object)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionGetVolvoItemInfo = function(
	item_id,
	on_success,
	on_failure )
{
	var aux = this.sessionGetAuxiliaryData();
	var item_aux = aux.item_aux[item_id];
	var vi_aux;
	if( item_aux )
		vi_aux = item_aux[ 'volvo_item_info' ];
	if( vi_aux )
		return on_success( vi_aux );
	else
		on_failure( new Error( 'No volvo item info available.' ) );
};


/**
 * Gets the current company cars information for a MenuItem.
 *
 * @export
 * @param {!string} item_id
 * @param {function(Object)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionGetCCItemInfo = function(
	item_id,
	on_success,
	on_failure )
{
	var aux = this.sessionGetAuxiliaryData();
	var item_aux = aux.item_aux[item_id];

	var cc_aux = item_aux ? item_aux['volvo_company_cars'] : null;

	if (cc_aux) {
		on_success(cc_aux);
	}
	else {
		on_failure(new Error('No company cars auxiliary available for item: ' + item_id + '.'));
	}
};




/**
 * Requests available campaigns using the related feed id.
 *
 * Each campaign item will be test loaded in the configurator server to verify
 * its correctness in combination with the available content data before
 * returning the results to the client. See documentation for
 * spv.vds.CapmaignCollection for more information.
 *
 * Preconditions: None.
 *
 * @export
 * @param {string} feed_id
 * @param {function(spv.vds.CampaignCollection)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.getCampaigns = function(
		feed_id,
		on_success,
		on_failure)
{
	spv.assert.isNonEmptyString(feed_id, 'feed_id');
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	this._vds_proto.getCampaigns(feed_id, on_success, on_failure);
};

spv.vds.VdsClient.prototype.getModelViews = function(
	on_success,
	on_failure) {
		this._vds_proto.getModelViews(on_success, on_failure);
};

/**
 * Returns the PNO12s of all our cars from the database.
 *
 * Preconditions: None.
 *
 * @export
 * @param {string} model_id
 * @param {function(spv.vds.ipprot_nova.volvoutility.CarCollection)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.getAllCars = function(
	model_id,
	on_success,
	on_failure) {
		spv.assert.isFunction(on_success, 'on_success');
		spv.assert.isFunction(on_failure, 'on_failure');
		this._vds_proto.GetAllCars(model_id, on_success, on_failure);
};


/**
 * Get some quick info about a saved configuration, such as model name and engine.
 *
 * Preconditions: Initialized session
 *
 * @export
 * @param {string} config_name
 * @param {function(spv.vds.ConfigQuickInfoResult)} on_success
 *        A callback taking a Loan info object
 *
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionGetConfigQuickInfo = function(
		config_name,
		on_success,
		on_failure)
{
	spv.assert.isString(config_name, 'config_name');
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	var that = this;

	this._requestQueue.push(function(wrap) {
		that.assertInitializedSession();
		that._vds_proto.getConfigQuickInfo(
			that.getSessionId(),
			config_name,
			wrap(on_success),
			wrap(on_failure)
		);
	});
}




/**
 * Get the current loan parameters and calculation result
 *
 * Preconditions: Initialized session
 *
 * @export
 * @param {function(spv.ds.Loan)} on_success
 *        A callback taking a Loan info object
 *
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionGetLoan = function(on_success, on_failure)
{
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	var that = this;
	this._requestQueue.push(function(wrap) {
		that.assertInitializedSession();
		that._vds_proto.sessionGetLoan(
			that.getSessionId(),
			wrap(on_success),
			wrap(on_failure)
		);
	});
};


/**
 * Gets available leasing parameters.
 */
spv.vds.VdsClient.prototype.getAvailableLeasingParams = function(on_success, on_failure)
{
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');

	this._vds_proto.getAvailableLeasingParams(
		on_success,
		on_failure);
};


/**
 * Get the current session leasing parameters
 *
 * Preconditions: Initialized session
 *
 * @export
 * @param {function(Object)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionGetLeasingParams = function(on_success, on_failure)
{
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	var that = this;
	this._requestQueue.push(function(wrap) {
		that.assertInitializedSession();
		that._vds_proto.sessionGetLeasingParams(
			that.getSessionId(),
			wrap(on_success),
			wrap(on_failure)
		);
	});
};


/**
 * Update leasing parameters and get the updated leasing output
 *
 * Preconditions: Initialized session
 *
 * @export
 * @param {number} driving_distance
 * @param {number} duration
 * @param {spv.vds.SendBopUpdateFlag} send_update
 * @param {function(Object)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionUpdateLeasingParams = function(
	driving_distance,
	duration,
	send_update,
	on_success,
	on_failure)
{
	spv.assert.isNumber(driving_distance, 'driving_distance');
	spv.assert.isNumber(duration, 'duration');
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	var that = this;
	var on_state_update = goog.bind(this._session_state.update, this._session_state);
	this._requestQueue.push(function(wrap) {
		that.assertInitializedSession();
		that._vds_proto.sessionUpdateLeasingParams(
			that.getSessionId(),
			driving_distance,
			duration,
			send_update,
			on_state_update,
			wrap(on_success),
			wrap(on_failure)
		);
	});
};



/**
 * Resets the loan parameters used for private loan calculations
 *
 * Preconditions: Initialized session.
 *
 * @export
 * @param {spv.vds.SendBopUpdateFlag} send_update
 * @param {function(spv.ds.Loan)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionResetLoan = function(
		send_update,
		on_success,
		on_failure)
{
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	var that = this;
	var on_state_update = goog.bind(this._session_state.update, this._session_state);
	this._requestQueue.push(function(wrap) {
		that.assertInitializedSession();
		that._vds_proto.sessionResetLoan(
			that.getSessionId(),
			send_update,
			on_state_update,
			wrap(on_success),
			wrap(on_failure)
		);
	});
};




/**
 * Sends a volvo retailer newsletter and/or summary mail to the customer and retailer.
 *
 * @export
 * @param {string} customer_first_name
 * @param {string} customer_surname
 * @param {string} customer_email
 * @param {string} customer_phone
 * @param {string} preferred_contact_time
 * @param {string} preferred_contact_media
 * @param {boolean} want_newsletter
 * @param {boolean} want_summary_pdf
 * @param {string} retailer_id
 * @param {string} mail_link_url
 * @param {string} contract_id
 * @param {string} employee_id
 * @param {string} sales_rep_mail
 * @param {function()} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionSendMailToCustomerAndRetailer = function(
		customer_first_name,
		customer_surname,
		customer_email,
		customer_phone,
		preferred_contact_time,
		preferred_contact_media,
		want_newsletter,
		want_summary_pdf,
		retailer_id,
		mail_link_url,
		contract_id,
		employee_id,
		sales_rep_mail,
		on_success,
		on_failure )
{
	spv.assert.isNonEmptyString(customer_first_name, 'customer_first_name');
	spv.assert.isNonEmptyString(customer_surname, 'customer_surname');
	spv.assert.isNonEmptyString(customer_email, 'customer_email');
	spv.assert.isNonEmptyString(customer_phone, 'customer_phone');
	spv.assert.isNonEmptyString(preferred_contact_time, 'preferred_contact_time');
	spv.assert.isNonEmptyString(preferred_contact_media, 'preferred_contact_media');
	spv.assert.isBoolean(want_newsletter, 'want_newsletter' );
	spv.assert.isBoolean(want_summary_pdf, 'want_summary_pdf' );
	spv.assert.isString(retailer_id, 'retailer_id');
	spv.assert.isString(contract_id, 'contract_id');
	spv.assert.isString(employee_id, 'employee_id');
	spv.assert.isString(sales_rep_mail, 'sales_rep_mail');
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');

	var that = this;
	this._requestQueue.push(function(wrap) {
		that._vds_proto.sessionSendMailToCustomerAndRetailer(
			that.getSessionId(),
			customer_first_name,
			customer_surname,
			customer_email,
			customer_phone,
			preferred_contact_time,
			preferred_contact_media,
			want_newsletter,
			want_summary_pdf,
			retailer_id,
			mail_link_url,
			contract_id,
			employee_id,
			sales_rep_mail,
			wrap(on_success),
			wrap(on_failure) );
	});
}

/**
 * @param {string} customer_first_name
 * @param {string} customer_surname
 * @param {string} customer_email
 * @param {string} customer_phone
 * @param {string} retailer_id
 * @param {function()} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionSendToTacdis = function(
		customer_first_name,
		customer_surname,
		customer_email,
		customer_phone,
		retailer_id,
		on_success,
		on_failure)
{
	// only two mandatory parameters
	spv.assert.isNonEmptyString(customer_email, 'customer_email');
	spv.assert.isNonEmptyString(retailer_id, 'retailer_id');

	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');

	var that = this;
	this._requestQueue.push(function(wrap) {
		that.assertInitializedSession();
		that._vds_proto.sessionSendToTacdis(
			that.getSessionId(),
			customer_first_name,
			customer_surname,
			customer_email,
			customer_phone,
			retailer_id,
			wrap(on_success),
			wrap(on_failure)
		);
	});
}

/**
 * @param {string} customer_first_name
 * @param {string} customer_surname
 * @param {string} customer_email
 * @param {string} customer_phone
 * @param {string} preferred_contact_time
 * @param {string} preferred_contact_media
 * @param {boolean} want_quote
 * @param {boolean} want_newsletter
 * @param {string} mail_link_url
 * @param {string} retailer_id
 * @param {string} contract_id
 * @param {string} employee_id
 * @param {function()} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionSendCustomerMailAndTacdis = function(
	customer_first_name,
	customer_surname,
	customer_email,
	customer_phone,
	preferred_contact_time,
	preferred_contact_media,
	want_quote,
	want_newsletter,
	retailer_id,
	mail_link_url,
	contract_id,
	employee_id,
	sales_rep_mail,
	on_success,
	on_failure)
{
	// only two mandatory parameters
	spv.assert.isNonEmptyString(customer_email, 'customer_email');
	spv.assert.isNonEmptyString(retailer_id, 'retailer_id');

	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');

	var that = this;
	this._requestQueue.push(function(wrap) {
		that.assertInitializedSession();
		that._vds_proto.sessionSendCustomerMailAndTacdis(
			that.getSessionId(),
			customer_first_name,
			customer_surname,
			customer_email,
			customer_phone,
			preferred_contact_time,
			preferred_contact_media,
			want_quote,
			want_newsletter,
			mail_link_url,
			retailer_id,
			contract_id,
			employee_id,
			sales_rep_mail,
			wrap(on_success),
			wrap(on_failure)
		);
	});
}


/**
 * Sends a volvo care mail to retailer.
 *
 * @param {string} customer_first_name
 * @param {string} customer_surname
 * @param {string} customer_address
 * @param {number} customer_zipcode
 * @param {string} customer_city
 * @param {string} customer_email
 * @param {string} customer_phone
 * @param {string} skype_name
 * @param {string} preferred_contact_media
 * @param {string} free_text
 * @param {string} retailer_id
 * @param {string} mail_link_url
 * @param {function()} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionSendVolvoCareMailToRetailer = function(
		customer_first_name,
		customer_surname,
		customer_address,
		customer_zipcode,
		customer_city,
		customer_email,
		customer_phone,
		skype_name,
		preferred_contact_media,
		free_text,
		retailer_id,
		mail_link_url,
		on_success,
		on_failure)
{
	spv.assert.isNonEmptyString(customer_first_name, 'customer_first_name');
	spv.assert.isNonEmptyString(customer_surname, 'customer_surname');

	spv.assert.isNonEmptyString(customer_address, 'customer_address');
	spv.assert.isNumber(customer_zipcode, 'customer_zipcode');
	spv.assert.isNonEmptyString(customer_city, 'customer_city');

	spv.assert.isNonEmptyString(customer_email, 'customer_email');
	spv.assert.isNonEmptyString(customer_phone, 'customer_phone');
	spv.assert.isNonEmptyString(preferred_contact_media, 'preferred_contact_media');

	spv.assert.isNonEmptyString(retailer_id, 'retailer_id');

	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');

	var that = this;
	this._requestQueue.push(function(wrap) {
		that.assertInitializedSession();
		that._vds_proto.sessionSendVolvoCareMailToRetailer(
			that.getSessionId(),
			customer_first_name,
			customer_surname,
			customer_address,
			customer_zipcode,
			customer_city,
			customer_email,
			customer_phone,
			skype_name,
			preferred_contact_media,
			free_text,
			retailer_id,
			mail_link_url,
			wrap(on_success),
			wrap(on_failure)
		);
	});
}



/**
 * Sends a volvo retailer newsletter and/or summary mail to the customer.
 *
 * @export
 * @param {string} customer_email
 * @param {boolean} want_newsletter
 * @param {boolean} want_summary_pdf
 * @param {string} mail_link_url
 * @param {function()} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionSendMailToCustomer = function(
		customer_email,
		want_newsletter,
		want_summary_pdf,
		mail_link_url,
		on_success,
		on_failure )
{
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	var that = this;
	this._requestQueue.push(function(wrap) {
		that._vds_proto.sessionSendMailToCustomer(
			that.getSessionId(),
			customer_email,
			want_newsletter,
			want_summary_pdf,
			mail_link_url,
			wrap(on_success),
			wrap(on_failure)
		);
	});
};



/**
 * Sets the loan parameters used for private loan calculations
 *
 * Preconditions: Initialized session.
 *
 * @export
 * @param {number} down_payment
 * @param {number} interest_rate
 * @param {number} number_of_months
 * @param {spv.vds.SendBopUpdateFlag} send_update
 * @param {function(spv.ds.Loan)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionSetLoan = function(
		down_payment,
		interest_rate,
		number_of_months,
		send_update,
		on_success,
		on_failure)
{
	spv.assert.isNumber(down_payment, 'down_payment');
	spv.assert.isNumber(interest_rate, 'interest_rate');
	spv.assert.isNumber(number_of_months, 'number_of_months');
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	var that = this;
	var on_state_update = goog.bind(this._session_state.update, this._session_state);
	this._requestQueue.push(function(wrap) {
		that.assertInitializedSession();
		that._vds_proto.sessionSetLoan(
			that.getSessionId(),
			down_payment,
			interest_rate,
			number_of_months,
			send_update,
			on_state_update,
			wrap(on_success),
			wrap(on_failure)
		);
	});
};

/**
 * Sets the mode to AccessoryMode (as opposed to the default build-a-car mode)
 *
 * Preconditions: Initialized session.
 *
 * @export
 * @param {Function} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionSetAccessoryMode = function(
	on_success,
	on_failure)
{
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	var that = this;
	this._requestQueue.push(function(wrap) {
		that.assertInitializedSession();
		that._vds_proto.sessionSetAccessoryMode(
			that.getSessionId(),
			wrap(on_success),
			wrap(on_failure)
		);
	});
};

/**
 * Sets the product filter (used in company car scheme mode, for example)
 *
 * Preconditions: Initialized session.
 *
 * @export
 * @param {string} filter_key
 * @param {Function} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionSetVolvoFilter = function(
	filter_key,
	on_success,
	on_failure)
{
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');
	var that = this;
	this._requestQueue.push(function(wrap) {
		that.assertInitializedSession();
		that._vds_proto.sessionSetVolvoFilter(
			that.getSessionId(),
			filter_key,
			wrap(on_success),
			wrap(on_failure)
		);
	});
};

/**
 * Sets retailer business group id and retailer mode for the current session.
 *
 * @export
 * @param {string} business_group_retailer_id
 * @param {Function} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionSetRetailerBusinessGroup = function (
		business_group_retailer_id,
		on_success,
		on_failure )
{
    spv.assert.isNonEmptyString(business_group_retailer_id, 'business_group_retailer_id');
    spv.assert.isFunction(on_success, 'on_success');
    spv.assert.isFunction(on_failure, 'on_failure');

    var that = this;
	this._requestQueue.push(function(wrap) {
		that.assertInitializedSession();
		that._vds_proto.sessionSetRetailerBusinessGroup(
			that.getSessionId(),
			business_group_retailer_id,
			wrap(on_success),
			wrap(on_failure)
		);
	});
};



/**
 * Enables private leasing financing mode for the current session.
 *
 * @export
 * @param {function()} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionEnablePrivateLeasing = function (
		on_success,
		on_failure )
{
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');

	var that = this;
	var on_success_wrapper = function () {
		that.sessionUpdateBop(on_success, on_failure );
	};
	this._requestQueue.push(function(wrap) {
		that.assertInitializedSession();
		that._vds_proto.sessionEnablePrivateLeasing(
			that.getSessionId(),
			wrap(on_success_wrapper),
			wrap(on_failure)
		);
	});
};

/**
 * Disables private leasing financing mode for the current session.
 *
 * @export
 * @param {function()} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionDisablePrivateLeasing = function (
		on_success,
		on_failure )
{
	spv.assert.isFunction(on_success, 'on_success');
	spv.assert.isFunction(on_failure, 'on_failure');

	var that = this;
	var on_success_wrapper = function () {
		that.sessionUpdateBop(on_success, on_failure );
	};
	this._requestQueue.push(function(wrap) {
		that.assertInitializedSession();
		that._vds_proto.sessionDisablePrivateLeasing(
			that.getSessionId(),
			wrap(on_success_wrapper),
			wrap(on_failure)
		);
	});
};




/**
 * @export
 * @param {string} template_name
 * @param {string} retailer_id
 * @param {function(string)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionGenerateRetailerHtmlForPdf = function(
	template_name,
	retailer_id,
	on_success,
	on_failure)
{
	this.assertInitializedSession();
	var that = this;
	this._requestQueue.push(function(wrap) {
		that._vds_proto.sessionGenerateRetailerHtmlForPdf(
			that.getSessionId(),
			template_name,
			retailer_id,
			wrap(on_success),
			wrap(on_failure)
		);
	});
};



/**
 * @export
 * @param {string} retid_delivery
 * @param {string} retid_sales
 * @param {string} customer_first_name
 * @param {string} customer_last_name
 * @param {string} customer_street_address
 * @param {string} customer_city
 * @param {string} customer_postal_code
 * @param {string} customer_phone
 * @param {string} customer_email
 * @param {boolean} redirect
 * @param {boolean} attachment
 * @param {function(spv.vds.CareCustomerOrderResult)} on_success
 * @param {function(Error)} on_failure
 */
spv.vds.VdsClient.prototype.sessionCareSendCustomerOrder = function(
	retid_delivery,
	retid_sales,
	customer_first_name,
	customer_last_name,
	customer_street_address,
	customer_city,
	customer_postal_code,
	customer_phone,
	customer_email,
	redirect,
	attachment,
	on_success,
	on_failure
){
	this.assertInitializedSession();
	var that = this;
	this._requestQueue.push(function(wrap) {
		that._vds_proto.sessionCareSendCustomerOrder(
			that.getSessionId(),
			retid_delivery,
			retid_sales,
			customer_first_name,
			customer_last_name,
			customer_street_address,
			customer_city,
			customer_postal_code,
			customer_phone,
			customer_email,
			redirect,
			attachment,
			wrap(on_success),
			wrap(on_failure)
		);
	});
}


/**
 * Generate the a URL to send customer order form data.
 * @export
 * @return {string}
 */
spv.vds.VdsClient.prototype.sessionCareSendCustomerOrderUrl = function(
) {
	return this._care_send_customer_order_form_url + "?session_url=" + this.getSessionId();
}