'use strict';

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

/**
 * Licensed Materials - Property of IBM
 * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2013, 2019
 * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *
 * VisRenderState
 * The VisRenderState represents the current state of rendering for a visualization.
 * Rendering involves loading all the requirements to render, then calling the view's render method.
 *
 */
define(['underscore'], function (_) {
	return function () {
		function VisRenderState() {
			_classCallCheck(this, VisRenderState);

			/**
    * _currentContext is a structure representing the current state of the render sequence.
    * Each member of the currentContext represents a 'step of the renderSequence which points
    * to various renderContexts that are responsible for those steps.
    */
			this._currentContext = {};

			/**
    * The render context pool holds the set of 'active renderContexts' that store portions
    * of the data needed to render a visualization.  These are cleaned up when no longer
    * part of the active render context.
    *
    * The renderContextPool is keyed by id which is currently a serial number (_renderId) for ease of debuggins
    * (but could be any unique number).
    */
			this._renderContextPool = {};

			//Data Members...
			this.firstRenderComplete = false;
			this.lastSizeRendered = null;

			this._steps = [];
		}

		VisRenderState.prototype.remove = function remove() {
			this.resetRenderState();
		};

		VisRenderState.prototype.initCurrentContext = function initCurrentContext(stepName) {
			if (!this._currentContext.hasOwnProperty(this._stepToStepContext(stepName))) {
				this._steps.push(stepName);
				this._currentContext[this._stepToStepContext(stepName)] = null;
			}
		};

		/**
   * Given a step name (like 'visView', 'visControl', 'data') return the
   * current rendering context of that step.
   * @returns a RenderContext object that is responsible for that step in the render sequence.
   *          (or that portion of the renderState's currentContext)
   */


		VisRenderState.prototype.getCurrentContext = function getCurrentContext(stepName) {
			return this._currentContext[this._stepToStepContext(stepName)];
		};

		// Main accessor
		// return elements of the renderStates' current context to callers.


		VisRenderState.prototype.getCurrentContextData = function getCurrentContextData(stepName) {
			return this._currentContext[this._stepToStepContext(stepName)] ? this._currentContext[this._stepToStepContext(stepName)][stepName] : null;
		};

		/**
   * Add a new render context which flows through the renderSequence collecting the parts of the render it is responsible for.
   * These parts are defined by the options.
   * If a renderContext completes and all parts are loaded, the visualization can render.
   *
   * @param options Information about what should be refreshed when running through the renderSequence.
   *         (also is 'the steps this renderContext is responsible for')
   * @param {integer} options.renderId - the run id for this context
   * Examples:
   * a resize would use pass no options <= which denotes reRender without refreshing any data.
   * a filter change would use options { refresh: { data: true }},
   * a visualization type change where the renderer type stays the same would use { refresh: { visSpec: true } }
   * Initial render (or changing renderers) would pass { refreshAll: true } meaning all data needs to be loaded.
   *
   * @returns a new RenderContext.
   * @throws Exception when options.renderId is not defined
   */


		VisRenderState.prototype.addRenderContext = function addRenderContext(options) {
			if (_.isUndefined(options) || _.isUndefined(options.renderId)) {
				throw new Error('cannot define a render context: missing renderId');
			}
			var renderContext = {
				id: options.renderId
			};
			_.each(this._steps, function (step) {
				renderContext[step] = null;
			});

			if (_.keys(options).length > 1) {
				renderContext.extraInfo = options.extraInfo;

				if (this._currentContext.dataContext && options.extraInfo && options.extraInfo.sender === 'realtimeRefresh') {
					// If we are in realtime refresh mode, the data request might return nothing if the data didn't change.
					// We need to keep the data the same in case
					renderContext.previousData = this._currentContext.dataContext.data;
				}
			}

			renderContext.resizing = options && options.resizing ? true : false;
			this._renderContextPool[renderContext.id] = renderContext;

			if (_.keys(options).length > 1) {
				if (options.refresh && options.refresh.data && !this.readyToLoadData()) {
					options.refreshAll = true;
				}

				if (options.refresh) {
					options.refresh.render = true;
				} else {
					// For anything else, ensure to do the main render
					options.refresh = {
						render: true
					};
				}

				renderContext.refresh = options.refresh;
				renderContext.refreshAll = options.refreshAll;

				if (options.refreshAll) {
					//Initialize ALL of the _currentContext to point to this renderContext.
					this.reloadAllRenderSteps(); //Clear the loaded steps in the render sequence.
					_.each(_.keys(this._currentContext), function (key) {
						this._currentContext[key] = renderContext;
					}.bind(this));
				} else if (options.refresh) {
					//Initialize the portions of the _currentContext to be refreshed.
					_.each(_.keys(options.refresh), function (stepName) {
						this._currentContext[this._stepToStepContext(stepName)] = renderContext;
					}.bind(this));
					//If calling re-render, at a minumum, we need to ensure the view is loaded.
					if (!this._currentContext.visViewContext) {
						this._currentContext.visViewContext = renderContext;
					}
				}
			}

			return renderContext;
		};

		/**
   * @returns true when all steps of the renderStates' current context prior to loading data are complete (initialized).
   */


		VisRenderState.prototype.readyToLoadData = function readyToLoadData() {
			return !!(this.getCurrentContextData('visView') && this.getCurrentContextData('visControl'));
		};

		/**
   * @returns true when all steps of the renderStates' current context are complete (initialized).
   */


		VisRenderState.prototype.readyToRender = function readyToRender() {
			return !!(this.getCurrentContextData('visView') && this.getCurrentContextData('visControl') && this.getCurrentContextData('data'));
		};

		/**
   * Reset the current context and context Renderer pools back to their initial state.
   */


		VisRenderState.prototype.resetRenderState = function resetRenderState() {
			this.lastSizeRendered = null;
			this._clearCurrentContext();
			this.cleanRenderContextPool( /*removeAll*/true);
		};

		/**
   * @return true if 'thisRenderContext' is obsolete.
   * (ie: no longer being used by any step in the _current context.)
   */


		VisRenderState.prototype._renderContextUnused = function _renderContextUnused(thisRenderContext) {
			var thisRenderContextFound = _.find(_.keys(this._currentContext), function (step) {
				return this._currentContext[step] === thisRenderContext;
			}.bind(this));
			//Don't delete a render context if any step in the render sequence is still using it.
			if (thisRenderContextFound) {
				return false;
			}
			//If no steps use this context, still shouldn't delete a render context until it is complete (ie: view render has/has not been called)..
			return thisRenderContext.renderComplete ? true : false;
		};

		/**
   * Remove unused (or all) entries from the RenderContextPool.
   * @param removeAll - when true removes all entries...otherwise, checks if the element is used.
   */


		VisRenderState.prototype.cleanRenderContextPool = function cleanRenderContextPool(removeAll) {
			_.each(_.keys(this._renderContextPool), function (key) {
				var renderContext = this._renderContextPool[key];
				if (removeAll || this._renderContextUnused(renderContext)) {
					delete this._renderContextPool[key];
				}
			}.bind(this));
		};

		/**
   * Remove all backing data from the currentContext.
   */


		VisRenderState.prototype._clearCurrentContext = function _clearCurrentContext() {
			if (this.getCurrentContextData('visView')) {
				this.getCurrentContextData('visView').remove();
			}
			_.each(_.keys(this._currentContext), function (key) {
				this._currentContext[key] = null;
			}.bind(this));
		};

		/**
   * Force a reload of all rendering steps without removing the RenderInfo assignments.
   * This is done when the renderer changes...or for any reason a full render is being done.
   */


		VisRenderState.prototype.reloadAllRenderSteps = function reloadAllRenderSteps() {
			if (this.getCurrentContextData('visView')) {
				this.getCurrentContextData('visView').remove( /*finalRemove*/false);
			}
			_.each(_.keys(this._currentContext), function (stepContext) {
				//Set the data element within each context step
				//whose name matches the key without 'Context'
				//For example  visViewContext.visView,  visControlContext.visControl, dataContext.data.
				var step = this._stepContextToStep(stepContext);
				if (this._currentContext[stepContext] && this._currentContext[stepContext][step]) {
					this._currentContext[stepContext][step] = null;
				}
			}.bind(this));
		};

		/**
   * @returns The fully populated renderContext that is active for a particular render.
   *			For example: a particular render might have "refresh: render" defined so other contexts would be null in the render context.
   *			But there is an active data context from a previous render.  This would populate that value and all previous render values.
   */


		VisRenderState.prototype.getFullRenderContext = function getFullRenderContext() {
			var fullRenderContext = {};
			for (var stepContext in this._currentContext) {
				//Set the data element within each context step whose name matches the stepContext name.
				var step = this._stepContextToStep(stepContext);
				fullRenderContext[step] = this._currentContext[stepContext] && this._currentContext[stepContext][step] || null;
			}
			return fullRenderContext;
		};

		//Helper function to convert a stepName (like 'data' into a stepContextName like 'dataContext')
		//TODO: Do we really have to make these different?


		VisRenderState.prototype._stepToStepContext = function _stepToStepContext(stepName) {
			return stepName + 'Context';
		};

		//Helper function to convert a contextName (like 'dataContext' into a stepName like 'data')
		//TODO: Do we really have to make these different?


		VisRenderState.prototype._stepContextToStep = function _stepContextToStep(contextName) {
			return contextName.substring(0, contextName.length - 7);
		};

		return VisRenderState;
	}();
});
//# sourceMappingURL=VisRenderState.js.map