const mwStorage = require( 'mediawiki.storage' ).local;

/**
 * @class
 * @constructor
 * @extends OO.ui.Element
 * @param {Object} [config] Configuration options
 * @param {boolean} [config.isEW] Orientation of the drag bar, East-West (true) or North-South (false).
 * @param {boolean} [config.id] The element ID. If supplied, the size of the resized pane will be persisted to localStorage.
 */
function ResizingDragBar( config ) {
	config = Object.assign( {}, {
		isEW: true,
		classes: [ 'ext-WikiEditor-ResizingDragBar' ],
		id: null
	}, config );
	ResizingDragBar.super.call( this, config );

	const widthOrHeight = config.isEW ? 'width' : 'height';

	this.storageKey = null;
	if ( config.id ) {
		// Don't use this.getElementId() here because that sets an autogenerated ID,
		// which we wouldn't want to use in the storage key.
		this.storageKey = 'ext-WikiEditor-ResizingDragBar__' + config.id;
	}

	const classNameDir = 'ext-WikiEditor-ResizingDragBar-' + ( config.isEW ? 'ew' : 'ns' );
	// Possible class names:
	// * ext-WikiEditor-ResizingDragBar-ew
	// * ext-WikiEditor-ResizingDragBar-ns
	this.$element.addClass( classNameDir );

	// Determine the horizontal direction to move (flexbox automatically reverses but the offset direction doesn't).
	const rtlFactor = config.isEW && OO.ui.Element.static.getDir( document ) === 'rtl' ? -1 : 1;
	this.$element.on( 'mousedown', ( eventMousedown ) => {
		if ( eventMousedown.button !== ResizingDragBar.static.MAIN_MOUSE_BUTTON ) {
			// If not the main mouse (e.g. left) button, ignore.
			return;
		}
		// Prevent selecting (or anything else) when dragging over other parts of the page.
		$( document ).on( 'selectstart.' + classNameDir, false );
		// Set up parameter names.
		const xOrY = config.isEW ? 'pageX' : 'pageY';
		let lastOffset = eventMousedown[ xOrY ];
		// Handle the actual dragging.
		$( document ).on( 'mousemove.' + classNameDir, ( eventMousemove ) => {
			// Initial width or height of the pane.
			const startSize = this.getResizedPane()[ widthOrHeight ]();
			// Current position of the mouse (relative to page, not viewport).
			const newOffset = eventMousemove[ xOrY ];
			// Distance the mouse has moved.
			const change = rtlFactor * ( lastOffset - newOffset );
			// Set the new size of the pane, and tell others about it.
			const newSize = Math.max( startSize - change, ResizingDragBar.static.MIN_PANE_SIZE );
			this.resizePane( widthOrHeight, newSize );
			// Save the new starting point of the mouse, from which to calculate the next move.
			lastOffset = newOffset;
		} );
	} );
	// Add a UI affordance within the handle area (CSS gives it its appearance).
	this.$element.append( $( '<span>' ) );
	// Remove the resize event handler when the mouse is released.
	$( document ).on( 'mouseup', () => {
		$( document ).off( 'mousemove.' + classNameDir );
		$( document ).off( 'selectstart.' + classNameDir, false );
	} );

	// Add detector node to delay resizing from saved value until the bar has been attached to the DOM.
	this.connectDetectorNode = document.createElement( 'ooui-connect-detector' );
	this.connectDetectorNode.onConnectOOUI = () => {
		if ( this.isElementAttached() ) {
			this.resizePane( widthOrHeight, mwStorage.get( this.storageKey ) );
		}
	};
	this.$element.append( this.connectDetectorNode );
}

OO.inheritClass( ResizingDragBar, OO.ui.Element );

/**
 * @static
 * @property {number} See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
 */
ResizingDragBar.static.MAIN_MOUSE_BUTTON = 0;

/**
 * @static
 * @property {number} The minimum pane size, in pixels.
 * Should be slightly more than the affordance length.
 * This should match the max-width of the .ext-WikiEditor-twopanes-pane2 element.
 */
ResizingDragBar.static.MIN_PANE_SIZE = 248;

/**
 * Get the pane that is resized by this bar (always the immediate prior sibling).
 *
 * @public
 * @return {jQuery}
 */
ResizingDragBar.prototype.getResizedPane = function () {
	return this.$element.prev();
};

/**
 * @param {string} widthOrHeight The dimension to resizes (width or height).
 * @param {number} [newSize] The new size of the resized pane.
 */
ResizingDragBar.prototype.resizePane = function ( widthOrHeight, newSize ) {
	if ( !newSize || isNaN( parseFloat( newSize ) ) ) {
		return;
	}
	// Resize the pane.
	this.getResizedPane().css( widthOrHeight, newSize );
	// Save the new size to localStorage.
	if ( this.storageKey ) {
		const oneYear = 60 * 60 * 24 * 365;
		mwStorage.set( this.storageKey, newSize, oneYear );
	}
	// Let other scripts do things after the resize.
	mw.hook( 'ext.WikiEditor.realtimepreview.resize' ).fire( this );
};

module.exports = ResizingDragBar;
