/**
 * EditCheckScrollIntoViewWidget class.
 *
 * @class
 * @extends OO.ui.ButtonGroupWidget
 *
 * @constructor
 * @param {Object} [config] Configuration options
 */
ve.ui.EditCheckScrollIntoViewWidget = function VeUiEditCheckScrollIntoViewWidget( config ) {
	this.showButton = new OO.ui.ButtonWidget( {
		label: ve.msg( 'editcheck-dialog-scroll-into-view' ),
		flags: [ 'progressive' ],
		size: 'large',
		icon: 'arrowDown'
	} );

	this.closeButton = new OO.ui.ButtonWidget( {
		icon: 'close',
		flags: [ 'progressive' ],
		label: mw.msg( 'ooui-popup-widget-close-button-aria-label' ),
		size: 'large',
		invisibleLabel: true
	} );

	config = Object.assign( {
		classes: [ 've-ui-editCheck-scrollIntoView ve-ce-surface-interface' ],
		items: [ this.showButton, this.closeButton ]
	}, config );

	// Parent constructor
	ve.ui.EditCheckScrollIntoViewWidget.super.call( this, config );

	if ( OO.ui.isMobile() ) {
		// Set the position before the first show, so the transition has a start state.
		this.$element.addClass( 've-ui-editCheck-scrollIntoView-bottom' );
	}

	this.target = ve.init.target;

	this.trackedElements = new Map();
	this.fullPageIntersecting = false;

	// Events
	this.showButton.connect( this, { click: [ 'emit', 'showClick' ] } );
	this.closeButton.connect( this, { click: [ 'emit', 'closeClick' ] } );

	this.connect( this, {
		showClick: () => ve.track( 'activity.editCheckScrollIntoView', 'click-show' ),
		closeClick: () => ve.track( 'activity.editCheckScrollIntoView', 'click-close' )
	} );

	this.target.connect( this, { virtualKeyboardChange: 'update' } );

	this.observer = window.IntersectionObserver ? new IntersectionObserver(
		( entries ) => {
			entries.forEach( ( entry ) => {
				this.trackedElements.set( entry.target, entry );
			} );
			this.update();
		},
		{ threshold: [ 0.3 ] }
	) : null;

	this.fullPageButtonObserver = null;
	if ( mw.config.get( 'wgVisualEditorConfig' ).enableVisualSectionEditing ) {
		this.fullPageButtonObserver = window.IntersectionObserver ? new IntersectionObserver(
			( entries ) => {
				// hide View Suggestions button when it conflicts with Edit Full Page
				entries.forEach( ( entry ) => {
					this.fullPageIntersecting = entry.isIntersecting;
				} );
				this.update();
			},
			{ threshold: [ 0 ] }
		) : null;
	}
};

/* Inheritance */

OO.inheritClass( ve.ui.EditCheckScrollIntoViewWidget, OO.ui.ButtonGroupWidget );

/* Methods */

/**
 * Clear tracked elements
 */
ve.ui.EditCheckScrollIntoViewWidget.prototype.clear = function () {
	if ( this.observer ) {
		this.observer.disconnect();
	}
	this.trackedElements.clear();
};

/**
 * Release the widget
 *
 * Use this when you discard the widget. To only stop the current
 * observations, use #clear.
 */
ve.ui.EditCheckScrollIntoViewWidget.prototype.destroy = function () {
	this.target.disconnect( this );
	if ( this.observer ) {
		this.observer.disconnect();
	}
	if ( this.fullPageButtonObserver ) {
		this.fullPageButtonObserver.disconnect();
	}
	this.trackedElements.clear();
};

/**
 * Observe an element for visibility changes
 *
 * @param {Element} element
 */
ve.ui.EditCheckScrollIntoViewWidget.prototype.observe = function ( element ) {
	if ( this.observer ) {
		this.observer.observe( element );
	}
};

/**
 * Observe full page button for visibility changes
 *
 * @param {Element} element
 */
ve.ui.EditCheckScrollIntoViewWidget.prototype.observeFullPageButton = function ( element ) {
	if ( this.fullPageButtonObserver ) {
		this.fullPageButtonObserver.observe( element );
	}
};

/**
 * Set state for suggestions outside current section. For use on mobile.
 *
 * @param {Object|null} state State object
 * @param {boolean} state.enabled Whether to show button (disabled when suggestion exists in current section)
 * @param {boolean} state.hasAbove Suggestions exist above current section
 * @param {boolean} state.hasBelow Suggestions exist below current section
 */
ve.ui.EditCheckScrollIntoViewWidget.prototype.setOutsideSectionState = function ( state ) {
	this.outsideSectionState = state || null;
	this.update();
};

/**
 * Update classes based on the visibility of the tracked elements.
 */
ve.ui.EditCheckScrollIntoViewWidget.prototype.update = function () {
	if ( mw.config.get( 'wgVisualEditorConfig' ).enableVisualSectionEditing ) {
		if ( this.fullPageIntersecting ) {
			this.$element.removeClass( 've-ui-editCheck-scrollIntoView-visible' );
			return;
		}

		const forced = this.outsideSectionState &&
			this.outsideSectionState.enabled &&
			( this.outsideSectionState.hasAbove || this.outsideSectionState.hasBelow );

		if ( forced && !this.target.isVirtualKeyboardOpen() ) {
			this.showButton.setIcon( this.outsideSectionState.hasAbove ? 'arrowUp' : 'arrowDown' );
			this.$element.addClass( 've-ui-editCheck-scrollIntoView-visible' );
			return;
		}
	}

	if ( !this.observer || !this.trackedElements.size || this.target.isVirtualKeyboardOpen() ) {
		this.$element.removeClass( 've-ui-editCheck-scrollIntoView-visible' );
		return;
	}

	const allNotVisible = Array.from( this.trackedElements.values() ).every(
		( entry ) => !entry.isIntersecting
	);
	if ( allNotVisible ) {
		const firstEntry = this.trackedElements.values().next().value;
		const isUp = firstEntry ? firstEntry.boundingClientRect.top < 0 : true;
		this.showButton.setIcon( isUp ? 'arrowUp' : 'arrowDown' );
		if ( !OO.ui.isMobile() ) {
			this.$element.toggleClass( 've-ui-editCheck-scrollIntoView-top', isUp );
			this.$element.toggleClass( 've-ui-editCheck-scrollIntoView-bottom', !isUp );
		}

		requestAnimationFrame( () => {
			this.$element.addClass( 've-ui-editCheck-scrollIntoView-visible' );
		} );

		if ( !this.loggedShown ) {
			ve.track( 'activity.editCheckScrollIntoView', 'shown' );
			this.loggedShown = true;
		}
	} else {
		requestAnimationFrame( () => {
			this.$element.removeClass( 've-ui-editCheck-scrollIntoView-visible' );
		} );
	}
};