'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 Dashboard
 *| (C) Copyright IBM Corp. 2020
 *|
 *| US Government Users Restricted Rights - Use, duplication or disclosure
 *| restricted by GSA ADP Schedule Contract with IBM Corp.
 *+------------------------------------------------------------------------+
 */
define([], function () {
	'use strict';

	/**
  * This object hides the implementation of the pt array and allows a caller to have keyed access to point information.
  * The initial use is as a postProcessor helper.
  * The hope longer term is for DataQueryResultV2 to use this class rather than the pt array.
  * 
  * For any key, all measure/calculation values can be accessed
  * AND, if a point does not exist, a null point will be returned.
  **/

	return function () {
		/**
   * 
   * @param {integer} edgeCount - the count of the number of edges in the result. 
   * @param {integer} measureCount - the count of the number of measures (and calcs) in the result.
   * @param {integer} measureEdgeIdx  - the edge where the measure is
   * @param {*} suppressionEnabled - true if suppression is enabled (used to determine the null value string)
   */
		function QueryResultDataPointsV2(edgeCount, measureCount) {
			var measureEdgeIdx = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : -1;
			var suppressionEnabled = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;

			_classCallCheck(this, QueryResultDataPointsV2);

			this._edgeCount = edgeCount;
			this._measureCount = measureCount;
			this._measureEdgeIdx = measureEdgeIdx; //If we extend to support single edge, this will be -1.
			this.missingValue = suppressionEnabled ? '' : null;

			this._measureMap = {};

			this._createNullValueTemplate();
			this._nullMeasureArray = this._createNullMeasureArray();
		}

		/**
   * Return a point that corresponds to one or two edgeIds in '{ pt: [] }' form.
   * NOTE: This API is not called getPoint() because in future, we should have getPoint()
   * return a QueryResultDataPointAPI.
   * @param {integer} edge1Id 
   * @param {integer=null} [edge2Id] 
   * @returns a dataPoint in "pt form" - { pt: [] }
   */


		QueryResultDataPointsV2.prototype.getPtByEdgeIds = function getPtByEdgeIds(edge1Id) {
			var edge2Id = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;

			var currentCell = this._measureMap[this._getPointKey(edge1Id, edge2Id)];
			//If a pt exists at this coordinate, 'consume it', otherwise, fill with a null.
			if (currentCell && currentCell[0] === edge1Id && (edge2Id === null || currentCell[1] === edge2Id)) {
				return { pt: currentCell };
			}
			return this._newNullValue(edge1Id, edge2Id);
		};

		/**
   * Load points into an internal structure that can be accessed with get methods.
   * This api expects an input value list in the form that is returned in the dss response.
   * (internally, it stores a map of arrays similar to the pt array but this is not necessay longer term)
   * @param {*} valueList - the pt array as returned in the DSS response. 
   */


		QueryResultDataPointsV2.prototype.loadPoints = function loadPoints(valueList) {
			//Read the data and fill values in based on the measure being processed
			var measureMap = {};
			for (var pointIdx = 0; pointIdx < valueList.length; ++pointIdx) {
				var point = valueList[pointIdx];
				if (point.pt) {
					var currentMeasureNum = point.pt[this._measureEdgeIdx]; //Read the measureNum out of the point.
					//To merge measures into 1 pt entry, get the "pointKey"
					//(entries in the point excluding measure. eg 0,1 for col 0, row 1)
					var pointKey = this._buildPointKey(point.pt, this._measureEdgeIdx);
					var valueIndex = point.pt.length - 1;
					var pointValue = point.pt[valueIndex];

					if (measureMap[pointKey]) {
						//This case will occur when there are multiple measures and we need to augment the entry
						//with a new measure value.  Set the value for the nth measure into the existing map.
						var mapEntry = measureMap[pointKey];
						mapEntry[valueIndex + currentMeasureNum] = pointValue;
					} else if (this._measureCount === 1) {
						//1 measure optimization: (and no calculations) case...
						//Since the internal form of a map entry is identical to the content of the pt array
						//and we aren't modifying it, we can simply use the original pt array for this point.
						//NOTE: This optimization would be invalid if the internal map entry format is changed.
						measureMap[pointKey] = point.pt;
					} else {
						//multi-measure case:  create an array with measureCount 'null value' object references,
						//Replace the n'th measure with the non-null value.
						//NOTE: the result can be any combination of nulls and non-nulls depending on which measures have values.
						//[edge#,edge#,measureEdge,{ v:null}, { v:null } ... ]
						var newValue = this._nullMeasureArray.slice(0);
						newValue[currentMeasureNum] = pointValue;
						var newPt = point.pt.slice(0, valueIndex);
						newPt = newPt.concat(newValue);
						measureMap[pointKey] = newPt;
					}
				}
			}
			this._measureMap = measureMap;
		};

		/**
   * @returns a count of the number of measures as passed in the constructor.
   */


		QueryResultDataPointsV2.prototype.getMeasureCount = function getMeasureCount() {
			return this._measureCount;
		};

		/**
   * @returns the edge where the measure is defined (or -1 if there is no measure edge)
   */


		QueryResultDataPointsV2.prototype.getMeasureEdgeIndex = function getMeasureEdgeIndex() {
			return this._measureEdgeIndex;
		};

		/**
   * @returns the size of the first categorical edge
   */


		QueryResultDataPointsV2.prototype.getFirstEdgeSize = function getFirstEdgeSize(edges) {
			return this._getEdgeSize(edges, this._getFirstEdgeIndex());
		};

		/**
   * @returns the size of the second categorical edge (or null)
   */


		QueryResultDataPointsV2.prototype.getSecondEdgeSize = function getSecondEdgeSize(edges) {
			var secondEdgeIndex = this._getSecondEdgeIndex();
			return secondEdgeIndex !== null ? this._getEdgeSize(edges, secondEdgeIndex) : null;
		};

		//Build a "point key" which is the value of a data row minus the measure index and measure value.
		//EXAMPLES: 2-edge crosstab point: [1, 0, { v:x }] ... key = 1
		//			3-edge crosstab point: [1, 3, 0, { v: x }] ... key = 1,3


		QueryResultDataPointsV2.prototype._buildPointKey = function _buildPointKey(pt, measureEdgeIdx) {
			var pointKey = [];
			pt.forEach(function (part, partIdx) {
				//In v2, a pt has a single cell value { v:x } as the last entry in the array. Exclude it from the key.
				if (partIdx !== measureEdgeIdx && partIdx < pt.length - 1) {
					pointKey.push(pt[partIdx]);
				}
			});
			return pointKey.join(',');
		};

		QueryResultDataPointsV2.prototype._getPointKey = function _getPointKey(edge1Id) {
			var edge2Id = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;

			return edge1Id + (edge2Id !== null ? ',' + edge2Id : '');
		};

		//Generate a "null value entry" which is { pt: [ <ptFirstEdgeId>, <ptSecondEdgeId>, 0, { v: null} ]


		QueryResultDataPointsV2.prototype._newNullValue = function _newNullValue(ptFirstEdgeId) {
			var ptSecondEdgeId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;

			var nullPt = this._nullValueTemplate.slice(0);
			//Set firstEdge as specified.
			nullPt[this._getFirstEdgeIndex()] = ptFirstEdgeId;
			if (ptSecondEdgeId !== null) {
				//Set edge1 as specified.
				nullPt[this._getSecondEdgeIndex()] = ptSecondEdgeId;
			}
			return { pt: nullPt };
		};

		//a null value template is a pt entry with 2 edge or 3 edge form and n { v:null } entries...1 per measure.
		//eg: [0, 0, 0, { v: null}, { v: null }] for a 3 edge, 2 measure crosstab.
		//It is used whenever we need to add a null as a starting point (then edgeId's are filled in.)


		QueryResultDataPointsV2.prototype._createNullValueTemplate = function _createNullValueTemplate() {
			this._nullValueTemplate = [];
			for (var i = 0; i < this._edgeCount; ++i) {
				this._nullValueTemplate.push(0);
			}
			this._nullValueTemplate = this._nullValueTemplate.concat(this._createNullMeasureArray());
		};

		QueryResultDataPointsV2.prototype._createNullMeasureArray = function _createNullMeasureArray() {
			var nullMeasureArray = [];
			for (var i = 0; i < this._measureCount; ++i) {
				nullMeasureArray.push({ v: this.missingValue });
			}
			return nullMeasureArray;
		};

		//The edge size is the number of items in the item array for an edge (minus the hasNext item)


		QueryResultDataPointsV2.prototype._getEdgeSize = function _getEdgeSize(edges, edgeIndex) {
			var edgeItems = edges[edgeIndex] && edges[edgeIndex].items;
			var edgeSize = edgeItems && edgeItems.length;
			if (edgeSize && edgeItems[edgeSize - 1].hasNext) {
				edgeSize--;
			}
			return edgeSize;
		};

		QueryResultDataPointsV2.prototype._getFirstEdgeIndex = function _getFirstEdgeIndex() {
			return this.getMeasureEdgeIndex() === 0 ? 1 : 0;
		};

		QueryResultDataPointsV2.prototype._getSecondEdgeIndex = function _getSecondEdgeIndex() {
			return this._edgeCount === 3 ? this._getFirstEdgeIndex() + 1 : null;
		};

		return QueryResultDataPointsV2;
	}();
});
//# sourceMappingURL=QueryResultDataPoints.v2.js.map