const util = require( './util' );

let browser;

/**
 * Memoize a class method. Caches the result of the method based on the
 * arguments. Instances do not share a cache.
 *
 * @private
 * @param {Function} method Method to be memoized
 * @return {Function}
 */
function memoize( method ) {
	/**
	 * Memoized version of the method
	 *
	 * @return {Function}
	 */
	const memoized = function () {
		const cache = this[ '__cache' + memoized.cacheId ] ||
			( this[ '__cache' + memoized.cacheId ] = {} ),
			key = [].join.call( arguments, '|' );
		if ( Object.prototype.hasOwnProperty.call( cache, key ) ) {
			return cache[ key ];
		}
		return ( cache[ key ] = method.apply( this, arguments ) );
	};
	memoized.cacheId = Date.now().toString() + Math.random().toString();
	return memoized;
}

/**
 * Representation of user's current browser
 *
 * @class Browser
 * @private
 * @param {string} ua the user agent of the current browser
 * @param {jQuery.Object} $container an element to associate with the Browser object
 */
function Browser( ua, $container ) {
	this.userAgent = ua;
	this.$el = $container;
}

Browser.prototype = {
	/**
	 * Returns whether the current browser is an ios device.
	 * FIXME: jquery.client does not support iPad detection so we cannot use it.
	 *
	 * @memberof Browser
	 * @instance
	 * @param {number} [version] integer describing a specific version you want to test against.
	 * @return {boolean}
	 */
	isIos: memoize( function ( version ) {
		const ua = this.userAgent,
			ios = /ipad|iphone|ipod/i.test( ua );

		if ( ios && version ) {
			switch ( version ) {
				case 8:
					// Test UA for iOS8. Or for simulator look for Version 8
					// In the iOS simulator the OS is the host machine OS version
					// This makes testing in iOS8 simulator work as expected
					return /OS 8_/.test( ua ) || /Version\/8/.test( ua );
				case 4:
					return /OS 4_/.test( ua );
				case 5:
					return /OS 5_/.test( ua );
				default:
					return false;
			}
		} else {
			return ios;
		}
	} ),
	/**
	 * Determine if a device has a widescreen.
	 *
	 * @memberof Browser
	 * @instance
	 * @return {boolean}
	 */
	isWideScreen: memoize( () => {
		const val = parseInt( mw.config.get( 'wgMFDeviceWidthTablet' ), 10 );
		// Check viewport width to determine mobile vs tablet.
		// Note: Mobile devices held in landscape mode might receive tablet treatment.
		return window.innerWidth >= val;
	} ),
	/**
	 * Whether touchstart and other touch events are supported by the current browser.
	 *
	 * @memberof Browser
	 * @instance
	 * @return {boolean}
	 */
	supportsTouchEvents: memoize( () => 'ontouchstart' in window )
};

/**
 * @memberof Browser
 * @return {Browser}
 */
Browser.getSingleton = function () {
	let $html;
	if ( !browser ) {
		$html = util.getDocument();
		browser = new Browser( window.navigator.userAgent, $html );
	}
	return browser;
};

module.exports = Browser;
