/**
 * MARQUEE 3000 MARQUEE 3000 MARQUEE 3000 MARQUEE 3000 MARQUEE 3000
 * http://github.com/ezekielaquino/marquee3000
 * Marquees for the new millennium v1.0
 * MIT License
 */

;(function (root, factory)
{
	if (typeof define === 'function' && define.amd)
	{
		define([], factory);
	}
	else if (typeof exports === 'object')
	{
		module.exports = factory();
	}
	else
	{
		root.Marquee3k = factory();
	}
}(this, function ()
{
	'use strict';

	let animationId = 0;
	let onResize = null;

	class Marquee3k {
		constructor(element, options)
		{

			if (element.children.length === 0)
			{
				throw new Error("Encountered a marquee element without children, please supply a wrapper for your content");
			}

			this.element = element;
			this.selector = options.selector;
			this.speed = element.dataset.speed || 0.25;
			this.pausable = element.dataset.pausable === 'true' ? true : false;
			this.reverse = element.dataset.reverse === 'true' ? true : false;
			this.paused = false;
			this.parent = element.parentElement;
			this.parentProps = this.parent.getBoundingClientRect();
			this.content = element.children[0];
			this.innerContent = this.content.innerHTML;
			this.wrapStyles = '';
			this.offset = 0;
			this.stopped = false;

			this._setupWrapper();
			this._setupContent();
			this._setupEvents();

			this.wrapper.appendChild(this.content);
			this.element.appendChild(this.wrapper);
		}

		_setupWrapper()
		{
			this.wrapper = document.createElement('div');
			this.wrapper.classList.add('marquee3k__wrapper');
			this.wrapper.style.whiteSpace = 'nowrap';
		}

		_setupContent()
		{
			this.content.classList.add(`${this.selector}__copy`);
			this.content.style.display = 'inline-block';
			this.contentWidth = this.content.offsetWidth;

			this.requiredReps = this.contentWidth > this.parentProps.width ? 2 : Math.ceil((this.parentProps.width - this.contentWidth) / this.contentWidth) + 1;

			for (let i = 0; i < this.requiredReps; i++)
			{
				this._createClone();
			}

			if (this.reverse)
			{
				this.offset = this.contentWidth * -1;
			}

			this.element.classList.add('is-init');
		}

		_setupEvents()
		{
			this.element.addEventListener('mouseenter', () =>
			{
				if (this.pausable) this.paused = true;
			});

			this.element.addEventListener('mouseleave', () =>
			{
				if (this.pausable) this.paused = false;
			});
		}

		_createClone()
		{
			const clone = this.content.cloneNode(true);
			clone.style.display = 'inline-block';
			clone.classList.add(`${this.selector}__copy`);
			this.wrapper.appendChild(clone);
		}

		animate()
		{
			if (!this.paused && !this.stopped)
			{
				const isScrolled = this.reverse ? this.offset < 0 : this.offset > this.contentWidth * -1;
				const direction = this.reverse ? -1 : 1;
				const reset = this.reverse ? this.contentWidth * -1 : 0;

				if (isScrolled) this.offset -= this.speed * direction;
				else this.offset = reset;

				this.wrapper.style.whiteSpace = 'nowrap';
				this.wrapper.style.transform = `translate(${this.offset}px, 0) translateZ(0)`;
			}
		}

		_refresh()
		{
			this.contentWidth = this.content.offsetWidth;
		}

		repopulate(difference, isLarger)
		{
			this.contentWidth = this.content.offsetWidth;

			if (isLarger)
			{
				const amount = Math.ceil(difference / this.contentWidth) + 1;

				for (let i = 0; i < amount; i++)
				{
					this._createClone();
				}
			}
		}

		static refresh(index)
		{
			MARQUEES[index]._refresh();
		}

		static pause(index)
		{
			MARQUEES[index].paused = true;
		}

		static play(index)
		{
			MARQUEES[index].paused = false;
		}

		static toggle(index)
		{
			MARQUEES[index].paused = !MARQUEES[index].paused;
		}

		static stop(index)
		{
			MARQUEES[index].stopped = true;
		}

		static refreshAll()
		{
			for (let i = 0; i < MARQUEES.length; i++)
			{
				MARQUEES[i]._refresh();
			}
		}

		static pauseAll()
		{
			for (let i = 0; i < MARQUEES.length; i++)
			{
				MARQUEES[i].paused = true;
			}
		}

		static playAll()
		{
			for (let i = 0; i < MARQUEES.length; i++)
			{
				MARQUEES[i].paused = false;
			}
		}

		static toggleAll()
		{
			for (let i = 0; i < MARQUEES.length; i++)
			{
				MARQUEES[i].paused = !MARQUEES[i].paused;
			}
		}

		static stopAll()
		{
			for (let i = 0; i < MARQUEES.length; i++)
			{
				MARQUEES[i].stopped = true;
			}
		}

		static init(options = {selector: 'marquee3k'})
		{
			if (animationId) window.cancelAnimationFrame(animationId);

			window.MARQUEES = [];
			const marquees = Array.from(document.querySelectorAll(`.${options.selector}`));
			let previousWidth = window.innerWidth;
			let timer;

			for (let i = 0; i < marquees.length; i++)
			{
				const marquee = marquees[i];
				const instance = new Marquee3k(marquee, options);
				MARQUEES.push(instance);
			}

			animate();

			function animate()
			{
				let allStopped = true;
				for (let i = 0; i < MARQUEES.length; i++)
				{
					MARQUEES[i].animate();
					if (!MARQUEES[i].stopped) allStopped = false;
				}

				if (allStopped)
				{
					if (onResize) window.removeEventListener('resize', onResize);
				}
				else
				{
					animationId = window.requestAnimationFrame(animate);
				}
			}

			onResize = () =>
			{
				clearTimeout(timer);

				timer = setTimeout(() =>
				{
					const isLarger = previousWidth < window.innerWidth;
					const difference = window.innerWidth - previousWidth;

					for (let i = 0; i < MARQUEES.length; i++)
					{
						MARQUEES[i].repopulate(difference, isLarger);
					}

					previousWidth = this.innerWidth;
				}, 250);
			};

			window.addEventListener('resize', onResize);
		}
	}

	return Marquee3k;

}));
