'use strict';

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

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

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

/**
 * Licensed Materials - Property of IBM
 * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2018, 2020
 * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 */

/**
*
* View to swap the Live Widget Visualization through the ODT
* Options
* {
*    showTitles: boolean,  // Whether to show the titles for the available/recommended visualizations
*    visualizations: {
*      recommended: [],     // Array of visualizations to place in recommended section
*      other: []           // Array of visualizations to put in other section
*    },
*    currentVis: String,   // The id of the currently selected visualization
*    widget: Live Widget   // The widget that is creating the vischanger view
* }
*
**/

define(['../../../lib/@waca/core-client/js/core-client/ui/core/View', '../../../lib/@waca/core-client/js/core-client/utils/Utils', '../nls/StringResources', 'jquery', 'underscore', 'react-dom', 'react', 'ca-ui-toolkit', 'dashboard-analytics/DynamicFileLoader', 'gemini/app/util/ErrorUtils'], function (BaseView, Utils, resources, $, _, ReactDOM, React, Toolkit, DynamicFileLoader, ErrorUtils) {
	/* eslint react/prop-types: 0 */ // TODO: Remove once we have prop-types brought in from glass.
	var Accordion = Toolkit.Accordion;
	var AccordionItem = Toolkit.AccordionItem;
	var SVGIcon = Toolkit.SVGIcon;
	var Label = Toolkit.Label;
	var SVGIconDecoration = Toolkit.SVGIconDecoration;
	var ProgressIndicator = Toolkit.ProgressIndicator;

	// Limit the number of recommended visualizations to show in the recommended section.

	var MaxRecommendedVisualizationsToShow = 6;
	var AUTO_ID = 'auto';

	/******************************************************
  * Recommended Accordion Section
  *	AccordionItem
  *		[PropItems] - SVG (with possible deco) and Label
  * All visualizations Accordion Section
  * 	AccordionItem
  *		[PropItems] - SVG (with possible deco) and Label
 ********************************************************/
	/*
 * Class to render the react flyout components
 */

	var ReactComponents = function (_React$Component) {
		_inherits(ReactComponents, _React$Component);

		/*
  * The props hold a state that should be used as this classes state.
  * When the state changes the component gets re-rendered.
  */
		function ReactComponents(props) {
			_classCallCheck(this, ReactComponents);

			var _this = _possibleConstructorReturn(this, _React$Component.call(this, props));

			_this.state = _extends({}, props.state);
			_this.state.checkMarkIcon = props.state.iconsFeature.getIcon('CheckmarkSVG');
			return _this;
		}

		/**
  * @param {string} id of the selected visualization
  * @return {React Component} the decoration if the specified id matched the selected id.
  */


		ReactComponents.prototype.getSelectedDecoration = function getSelectedDecoration(id) {
			// The decoration is basically a checkmark to show the selected vis.
			if (this.state.selectedId === id) {
				return React.createElement(SVGIconDecoration, {
					iconId: this.state.checkMarkIcon.id,
					location: 'bottomRight',
					style: { right: '-4px' } });
			}
		};

		/**
  * @param {object} item describing all the info needed to create a prop item
  * @param {String} dataType either 'Recommended' or 'Other'
  * @return {React Object}
  */


		ReactComponents.prototype.renderPropItem = function renderPropItem(item, dataType) {
			var decoration = this.getSelectedDecoration(item.id);
			var pressed = this.state.selectedId === item.id ? 'true' : 'false';
			return React.createElement(
				'div',
				{ role: 'group', className: 'prop-item' },
				React.createElement(
					'div',
					{ tabIndex: 0,
						'data-type': dataType,
						className: 'prop-icon vis',
						role: 'button',
						title: item.name,
						'data-id': item.id,
						'appcues-data-id': item.id,
						'aria-label': item.name,
						'aria-pressed': pressed },
					React.createElement(
						SVGIcon,
						{
							iconId: this.state.svgsMap[item.id].id,
							height: 42,
							width: 42 },
						decoration
					),
					React.createElement(Label, { className: 'prop-icon-label', label: item.name })
				)
			);
		};

		/**
  * @param {Array} items all the visualizations to add to the row
  * @param {Boolean} isRecommended true iff the Vis Row is the recommended row.
  * @returns {Array} of React components for each Vis in the Vis Row.
  */


		ReactComponents.prototype.loadVisRowItems = function loadVisRowItems(items, isRecommended) {
			var _this2 = this;

			var svgs = [];
			var dataType = isRecommended ? 'Recommended' : 'Other';
			items.forEach(function (item) {
				if (item.id && _this2.state.svgsMap[item.id]) {
					svgs.push(_this2.renderPropItem(item, dataType));
				}
			});

			return svgs;
		};

		/**
  * Render the progress indicator to show we are loading.
  */


		ReactComponents.prototype._renderLoading = function _renderLoading() {
			return React.createElement(
				'div',
				{ className: 'vis-changer-progressIndicator' },
				React.createElement(ProgressIndicator, { size: 'large', variant: 'circle' })
			);
		};

		ReactComponents.prototype._getVisRowItems = function _getVisRowItems(options) {
			var items = options.items,
			    isRecommended = options.isRecommended;

			var loadedSvgs = this.loadVisRowItems(items, isRecommended);
			return React.createElement(
				'div',
				{ className: 'visChangerRow' },
				loadedSvgs
			);
		};

		ReactComponents.prototype._getRecommendedAccordionItem = function _getRecommendedAccordionItem() {
			var recommendedOptions = {
				items: this.state.recommended.items,
				isRecommended: true
			};
			return this.state.recommended.isLoading ? this._renderLoading() : this._getVisRowItems(recommendedOptions);
		};

		ReactComponents.prototype._getAllVisAccordionItem = function _getAllVisAccordionItem() {
			var allVisOptions = {
				items: this.state.all.items,
				isRecommended: false
			};
			return this.state.all.isLoading ? this._renderLoading() : this._getVisRowItems(allVisOptions);
		};

		/**
  * Render the collapsible sections (accordions)
  * @return {React Object} representing the Accordion and its sections.
  */


		ReactComponents.prototype._renderAccordions = function _renderAccordions() {
			var recommendedItem = this._getRecommendedAccordionItem();
			var isConsumer = this.state.isConsumer;
			if (!isConsumer) {
				return React.createElement(
					Accordion,
					null,
					React.createElement(
						AccordionItem,
						{ itemName: resources.get('recommended_visualizations'), icon: 'left', open: true },
						recommendedItem
					),
					React.createElement(
						AccordionItem,
						{ itemName: resources.get('all_visualizations'), icon: 'left', open: false, onChange: this.state.onItemChange },
						this._getAllVisAccordionItem()
					)
				);
			} else {
				return React.createElement(
					Accordion,
					null,
					React.createElement(
						AccordionItem,
						{ itemName: resources.get('recommended_visualizations'), icon: 'left', open: true },
						recommendedItem
					)
				);
			}
		};

		/**
  * @returns {React Object} progress indicator if we are loading, accordions otherwise.
  */


		ReactComponents.prototype.render = function render() {
			return this._renderAccordions();
		};

		return ReactComponents;
	}(React.Component);

	/**
 * The following view is the actual ODT Vis Changer view. It will handle
 * selection of the react components to change vis type, rendering the different
 * choices into two collapsible sections as well as showing a loading indicator
 * until all the SVGs are loaded.
 */


	var View = BaseView.extend({

		events: {
			'primaryaction .vis': '_selectVis'
		},

		/**
  * Load the SVG files representing all the visualizations
  */
		_loadSVGFileMap: function _loadSVGFileMap() {
			var _this3 = this;

			// If we've already loaded the SVG files, return the map
			if (this.viewState.svgsMap && _.keys(this.viewState.svgsMap).length > 0) {
				return Promise.resolve(this.viewState.svgsMap);
			} else {
				this.viewState.svgsMap = {};
				this._getAllAvailableVisualizations();
				if (this.viewState.all && this.viewState.all.items) {
					var iconsFeature = this.viewState.iconsFeature;
					this.viewState.all.items.forEach(function (item) {
						var visIcon = iconsFeature.getIcon(item.visId);
						_this3.viewState.svgsMap[item.visId] = visIcon;
					});
					if (!(Object.keys(this.viewState.svgsMap).indexOf('auto') !== -1)) {
						var autoIcon = iconsFeature.getIcon('auto');
						this.viewState.svgsMap['auto'] = autoIcon;
					}
					return Promise.resolve(this.viewState.svgsMap);
				}
			}
		},


		init: function init(options) {
			// Provide the basic element.
			this.el = $('<div class=\'visChangerContainer\'/>');
			View.inherited('init', this, arguments);

			this.applyAction = options && options.actions && options.actions.apply;
			var finalOptions = options && options.state ? options.state : options;
			_.extend(this, finalOptions || {});
			this.iconsFeature = this.dashboard.getFeature('Icons');
			this._initializeViewState();
		},

		_elementExists: function _elementExists() {
			var hasLength = false,
			    hasKids = false,
			    el = this.el;
			if (el) {
				if (el.length) {
					hasLength = el.length > 0;
				}
				if (el.children) {
					hasKids = el.children !== 0;
				}
			}
			return el && (hasKids || hasLength);
		},

		remove: function remove() {
			if (this._elementExists()) {
				ReactDOM.unmountComponentAtNode(this.el);
			}
			this.viewState = null;
			return View.inherited('remove', this, arguments);
		},

		setFocus: function setFocus() {
			this.$('.vis').first().focus();
		},

		_processVisualizations: function _processVisualizations(visualizations) {
			var _this4 = this;

			var markupPayload = [];
			_.each(visualizations, function (vis) {
				_this4.viewState.visualizationMap[vis.id || vis.getId()] = vis;
				markupPayload.push({
					id: vis.id || vis.getId(),
					name: vis.caption || vis.getLabel(),
					visId: vis.id || vis.getId(),
					caption: vis.caption || vis.getLabel(),
					iconUri: vis.icon || vis.getIcon()
				});
			});
			return markupPayload;
		},


		/**
  * We want to dynamically load all the SVGs (why load them if we never need them)
  * This method loads the SVG files into a map, asks the widget for a list
  * of recommended and non-recommended visualizations, and adds the 'Auto'
  * choice as well.
  */
		_preload: function _preload() {
			// Load the file map
			this.viewState.visualizationMap = this.viewState.visualizationMap || {};
			return this._loadSVGFileMap();
		},

		_getAllAvailableVisualizations: function _getAllAvailableVisualizations() {
			var allVisualizations = this.dashboard.getFeature('VisDefinitions').getList();
			var processedItems = this._processVisualizations(allVisualizations);
			this.viewState.all.items = _.sortBy(processedItems, function (vis) {
				return vis.name;
			});
			this.viewState.all.isLoading = false;
		},
		_getRecommendedVisualizations: function _getRecommendedVisualizations() {
			var _this5 = this;

			return this.content.getFeature('Visualization.SmartsRecommender').getRecommendedVisualizations().then(function (recommended) {
				if (_this5.viewState === null) {
					return; //Remove has already been called, stop processing
				}
				var processedVisualizations = _this5._processVisualizations(recommended);
				processedVisualizations.unshift({
					id: AUTO_ID,
					name: resources.get('automaticTypeCaption'),
					visId: AUTO_ID,
					caption: resources.get('automaticTypeCaption'),
					iconUri: 'visualizations-changeVisualization',
					disabled: processedVisualizations.length === 0
				});
				var maxVisualizationsToShow = MaxRecommendedVisualizationsToShow;
				if (_this5.viewState.selectedId) {
					// Don't include Auto in the limit
					maxVisualizationsToShow++;
				}
				_this5.viewState.recommended.items = processedVisualizations.slice(0, maxVisualizationsToShow);
				_this5.viewState.recommended.isLoading = false;
			});
		},
		_initializeViewState: function _initializeViewState() {
			this.viewState = {
				recommended: {
					items: [],
					isLoading: true
				},
				all: {
					items: [],
					isLoading: true
				},
				svgsMap: null,
				selectedId: this._getSelectedVizId(),
				visualizationMap: null,
				iconsFeature: this.iconsFeature,
				onItemChange: this._onItemChange.bind(this)
			};
		},


		//called when an item in the accordian is changed
		_onItemChange: function _onItemChange() {
			//ensure this flyout view is not off the screen
			var diff = this._getOffScreenHeight();
			if (diff > 0) {
				//TODO: This is not a great solution, especially if we change the way popovers are handled.
				// in the future, any resizing should be handled by the popover class.
				var popover = $(this.el).parents('.popover');
				var top = Math.max(0, popover.position().top - diff);
				popover && popover.css({ top: top });
			}
		},


		/**
   * Checks to see if this View is off the visible screen.
   * @returns the number of pixels this view is off the screen, or -1 if it is not off the screen
   */
		_getOffScreenHeight: function _getOffScreenHeight() {
			var htmlScrollHeight = this._getDocumentElement().scrollHeight;
			var innerHeight = Utils.getCurrentWindow().innerHeight;
			if (htmlScrollHeight > innerHeight) {
				return htmlScrollHeight - innerHeight;
			}
			return -1;
		},


		/**
   * @returns The document.documentElement.
   *  This is here to more easily mock for tests.
   */
		_getDocumentElement: function _getDocumentElement() {
			return document.documentElement;
		},
		_getSelectedVizId: function _getSelectedVizId() {
			var visualization = this.content.getFeature('Visualization');
			if (this.currentVis && this.currentVis === AUTO_ID || visualization && !visualization.isTypeLocked()) {
				return AUTO_ID;
			} else {
				return visualization.getDefinition().getId();
			}
		},
		_resetRecommendedViewState: function _resetRecommendedViewState() {
			this.viewState.recommended = {
				items: [],
				isLoading: true
			};
			if (this._elementExists() && this.reactComponents) {
				ReactDOM.unmountComponentAtNode(this.el);
			}
		},


		/**
  * Create the react components with an initial loading state.
  */
		_showLoading: function _showLoading() {
			this._resetRecommendedViewState();
			// Render the react components
			this.reactComponents = ReactDOM.render(React.createElement(ReactComponents, { state: this.viewState }), this.el);
		},


		/**
  * Render the react components with a state to show the accordions.
  */
		_showReactSections: function _showReactSections() {
			this.reactComponents.setState(JSON.parse(JSON.stringify(this.viewState)));
		},
		_render: function _render(options) {
			var _this6 = this;

			_.extend(this, options || {});
			this._isConsumer();
			this._showLoading();
			return this._preload().then(function () {
				_this6._showReactSections();
			}).then(this._getRecommendedVisualizations.bind(this)).then(function () {
				_this6._showReactSections();
			});
		},
		_isConsumer: function _isConsumer() {
			var currentContentView = this.dashboard && this.dashboard.getCurrentContentView();
			var glassContext = currentContentView && currentContentView.glassContext;
			if (this.viewState && glassContext) {
				this.viewState.isConsumer = !ErrorUtils.hasCapability(glassContext, 'canAuthorDashboard');
			}
		},


		/**
  * Method to render the view. This includes creating the react components to
  * initially show a progress indicator, preload all the svg icons, divide the
  * choices into sections, then update the react components to show the accordions.
  */
		render: function render(options) {
			this._render(options);
			return Promise.resolve();
		},

		_getTargetFromEvent: function _getTargetFromEvent(event) {
			return $(event.currentTarget);
		},


		// when id auto we highlight auto too
		/**
   * Event handler for a .vis button is clicked.
   * Send event to be handled by Live Widget and updates its view with current selection
   */
		_selectVis: function _selectVis(event) {
			var $currentTarget = this._getTargetFromEvent(event);
			var id = $currentTarget.attr('data-id');
			var recommendedCategory = $currentTarget.attr('data-type');

			// If selected visualization target is same as current visualization target
			if (this.viewState.selectedId === id) {
				return;
			}
			this.viewState.selectedId = id;

			this.reactComponents.setState({ selectedId: id });

			var visDefinition = this.dashboard.getFeature('VisDefinitions').getById(id);
			var visType = visDefinition && visDefinition.getType() || id;

			this.applyAction(visType, { recommendedCategory: recommendedCategory });

			if (this.selectVisCB) {
				this.selectVisCB();
			}
		}
	});

	return View;
});
//# sourceMappingURL=VisChangerFlyoutView.js.map