'use strict';

import {config} from '../player';
import MediaElementPlayer from '../player';
import i18n from '../core/i18n';
import {IS_ANDROID, IS_IOS} from '../utils/constants';

/**
 * Volume button
 *
 * This feature enables the displaying of a Volume button in the control bar, and also contains logic to manipulate its
 * events, such as sliding up/down (or left/right, if vertical), muting/unmuting media, etc.
 */


// Feature configuration
Object.assign(config, {
	/**
	 * @type {String}
	 */
	muteText: '',
	/**
	 * @type {String}
	 */
	allyVolumeControlText: '',
	/**
	 * @type {Boolean}
	 */
	hideVolumeOnTouchDevices: true,
	/**
	 * @type {String}
	 */
	audioVolume: 'horizontal',
	/**
	 * @type {String}
	 */
	videoVolume: 'vertical'
});

Object.assign(MediaElementPlayer.prototype, {

	/**
	 * Feature constructor.
	 *
	 * Always has to be prefixed with `build` and the name that will be used in MepDefaults.features list
	 * @param {MediaElementPlayer} player
	 * @param {$} controls
	 * @param {$} layers
	 * @param {HTMLElement} media
	 * @public
	 */
	buildvolume: function (player, controls, layers, media)  {

		// Android and iOS don't support volume controls
		if ((IS_ANDROID || IS_IOS) && this.options.hideVolumeOnTouchDevices) {
			return;
		}

		let
			t = this,
			mode = (t.isVideo) ? t.options.videoVolume : t.options.audioVolume,
			muteText = t.options.muteText ? t.options.muteText : i18n.t('mejs.mute-toggle'),
			volumeControlText = t.options.allyVolumeControlText ? t.options.allyVolumeControlText : i18n.t('mejs.volume-help-text'),
			mute = (mode === 'horizontal') ?

				// horizontal version
				$(`<div class="${t.options.classPrefix}button ${t.options.classPrefix}volume-button ${t.options.classPrefix}mute">` +
					`<button type="button" aria-controls="${t.id}" title="${muteText}" aria-label="${muteText}"></button>` +
				`</div>` +
				`<a href="javascript:void(0);" class="${t.options.classPrefix}horizontal-volume-slider">` +
					`<span class="${t.options.classPrefix}offscreen">${volumeControlText}</span>` +
					`<div class="${t.options.classPrefix}horizontal-volume-total">` +
						`<div class="${t.options.classPrefix}horizontal-volume-current"></div>` +
						`<div class="${t.options.classPrefix}horizontal-volume-handle"></div>` +
					`</div>` +
				`</a>`)
				.appendTo(controls) :

				// vertical version
				$(`<div class="${t.options.classPrefix}button ${t.options.classPrefix}volume-button ${t.options.classPrefix}mute">` +
					`<button type="button" aria-controls="${t.id}" title="${muteText}" aria-label="${muteText}"></button>` +
					`<a href="javascript:void(0);" class="${t.options.classPrefix}volume-slider">` +
						`<span class="${t.options.classPrefix}offscreen">${volumeControlText}</span>` +
						`<div class="${t.options.classPrefix}volume-total">` +
							`<div class="${t.options.classPrefix}volume-current"></div>` +
							`<div class="${t.options.classPrefix}volume-handle"></div>` +
						`</div>` +
					`</a>` +
				`</div>`)
				.appendTo(controls),
			volumeSlider = t.container.find(`.${t.options.classPrefix}volume-slider, 
				.${t.options.classPrefix}horizontal-volume-slider`),
			volumeTotal = t.container.find(`.${t.options.classPrefix}volume-total, 
				.${t.options.classPrefix}horizontal-volume-total`),
			volumeCurrent = t.container.find(`.${t.options.classPrefix}volume-current, 
				.${t.options.classPrefix}horizontal-volume-current`),
			volumeHandle = t.container.find(`.${t.options.classPrefix}volume-handle, 
				.${t.options.classPrefix}horizontal-volume-handle`),

			/**
			 * @private
			 * @param {Number} volume
			 */
			positionVolumeHandle = (volume) => {

				// correct to 0-1
				volume = Math.max(0, volume);
				volume = Math.min(volume, 1);

				// adjust mute button style
				if (volume === 0) {
					mute.removeClass(`${t.options.classPrefix}mute`).addClass(`${t.options.classPrefix}unmute`);
					mute.children('button').attr({
						title: i18n.t('mejs.unmute'),
						'aria-label': i18n.t('mejs.unmute')
					});
				} else {
					mute.removeClass(`${t.options.classPrefix}unmute`).addClass(`${t.options.classPrefix}mute`);
					mute.children('button').attr({
						title: i18n.t('mejs.mute'),
						'aria-label': i18n.t('mejs.mute')
					});
				}

				let volumePercentage = `${(volume * 100)}%`;

				// position slider
				if (mode === 'vertical') {
					volumeCurrent.css({
						bottom: '0',
						height: volumePercentage
					});
					volumeHandle.css({
						bottom: volumePercentage,
						marginBottom: `${(-volumeHandle.height() / 2)}px`
					});
				} else {
					volumeCurrent.css({
						left: '0',
						width: volumePercentage
					});
					volumeHandle.css({
						left: volumePercentage,
						marginLeft: `${(-volumeHandle.width() / 2)}px`
					});
				}
			},
			/**
			 * @private
			 */
			handleVolumeMove = (e) => {

				let
					volume = null,
					totalOffset = volumeTotal.offset()
				;

				// calculate the new volume based on the most recent position
				if (mode === 'vertical') {

					let
						railHeight = volumeTotal.height(),
						newY = e.pageY - totalOffset.top
					;

					volume = (railHeight - newY) / railHeight;

					// the controls just hide themselves (usually when mouse moves too far up)
					if (totalOffset.top === 0 || totalOffset.left === 0) {
						return;
					}

				} else {
					let
						railWidth = volumeTotal.width(),
						newX = e.pageX - totalOffset.left
					;

					volume = newX / railWidth;
				}

				// ensure the volume isn't outside 0-1
				volume = Math.max(0, volume);
				volume = Math.min(volume, 1);

				// position the slider and handle
				positionVolumeHandle(volume);

				// set the media object (this will trigger the `volumechanged` event)
				if (volume === 0) {
					media.setMuted(true);
				} else {
					media.setMuted(false);
				}
				media.setVolume(volume);
			},
			mouseIsDown = false,
			mouseIsOver = false;

		// SLIDER
		mute
			.on('mouseenter focusin', () => {
				volumeSlider.show();
				mouseIsOver = true;
			})
			.on('mouseleave focusout', () => {
				mouseIsOver = false;

				if (!mouseIsDown && mode === 'vertical') {
					volumeSlider.hide();
				}
			});

		/**
		 * @private
		 */
		let updateVolumeSlider = () => {

			let volume = Math.floor(media.volume * 100);

			volumeSlider.attr({
				'aria-label': i18n.t('mejs.volume-slider'),
				'aria-valuemin': 0,
				'aria-valuemax': 100,
				'aria-valuenow': volume,
				'aria-valuetext': `${volume}%`,
				'role': 'slider',
				'tabindex': -1
			});

		};

		// Events
		volumeSlider
			.on('mouseover', () => {
				mouseIsOver = true;
			})
			.on('mousedown', (e) => {
				handleVolumeMove(e);
				t.globalBind('mousemove.vol', (e) => {
					handleVolumeMove(e);
				});
				t.globalBind('mouseup.vol', () => {
					mouseIsDown = false;
					t.globalUnbind('mousemove.vol mouseup.vol');

					if (!mouseIsOver && mode === 'vertical') {
						volumeSlider.hide();
					}
				});
				mouseIsDown = true;

				return false;
			})
			.on('keydown', (e) => {

				if (t.options.keyActions.length) {
					let
						keyCode = e.which || e.keyCode || 0,
						volume = media.volume
					;
					switch (keyCode) {
						case 38: // Up
							volume = Math.min(volume + 0.1, 1);
							break;
						case 40: // Down
							volume = Math.max(0, volume - 0.1);
							break;
						default:
							return true;
					}

					mouseIsDown = false;
					positionVolumeHandle(volume);
					media.setVolume(volume);
					return false;
				}
			});

		// MUTE button
		mute.find('button').click(() => {
			media.setMuted(!media.muted);
		});

		//Keyboard input
		mute.find('button').on('focus', () => {
			if (mode === 'vertical') {
				volumeSlider.show();
			}
		}).on('blur', () => {
			if (mode === 'vertical') {
				volumeSlider.hide();
			}
		});

		// listen for volume change events from other sources
		media.addEventListener('volumechange', (e) => {
			if (!mouseIsDown) {
				if (media.muted) {
					positionVolumeHandle(0);
					mute.removeClass(`${t.options.classPrefix}mute`).addClass(`${t.options.classPrefix}unmute`);
				} else {
					positionVolumeHandle(media.volume);
					mute.removeClass(`${t.options.classPrefix}unmute`).addClass(`${t.options.classPrefix}mute`);
				}
			}
			updateVolumeSlider(e);
		}, false);

		// mutes the media and sets the volume icon muted if the initial volume is set to 0
		if (player.options.startVolume === 0) {
			media.setMuted(true);
		}

		// shim gets the startvolume as a parameter, but we have to set it on the native <video> and <audio> elements
		let isNative = t.media.rendererName !== null && t.media.rendererName.match(/(native|html5)/) !== null;

		if (isNative) {
			media.setVolume(player.options.startVolume);
		}

		t.container.on('controlsresize', () => {
			if (media.muted) {
				positionVolumeHandle(0);
				mute.removeClass(`${t.options.classPrefix}mute`)
				.addClass(`${t.options.classPrefix}unmute`);
			} else {
				positionVolumeHandle(media.volume);
				mute.removeClass(`${t.options.classPrefix}unmute`)
				.addClass(`${t.options.classPrefix}mute`);
			}
		});
	}
});


