var CookieconsentManager = {
	descriptor: null,
	intervalID: 0,

	/* Mapping */
	itemMap: {},
	setMap: [
		{ isOn: false, items: [] },
		{ isOn: false, items: [] },
		{ isOn: false, items: [] },
		{ isOn: false, items: [] },
		{ isOn: false, items: [] }
	],

	/* Cookies */
	blockedList: [],
	removedList: [],
	whitelist: [ 'bypass' ],

	/* Other items */
	unconsentedIFrames: null,
	unconsentedScripts: null,
	
	clearCookies: function() {
		var currentCookieLength = document.cookie.length;
		if ( !currentCookieLength ) { return; }

		var _ = this;
		var whitelistCount = this.whitelist.length;
		var removeCookieByPart = function( cookie, part ) {
			document.cookie = cookie.replace( /=.*/, '=' + part + ';expires=' + new Date().toUTCString() );
			
			/* Return on failure */
			if ( document.cookie.length >= currentCookieLength ) { return false; }

			/* Update length */
			currentCookieLength = document.cookie.length;

			/* Add record */
			_.removedList.push( cookie + part );

			return true;
		};
		
		document.cookie.split( ';' ).forEach( function( cookie ) {
			for ( var i = 0; i != whitelistCount; ++i ) {
				var cookieName = _.whitelist[ i ] + '=';
				if ( cookie.indexOf( cookieName ) != -1 ) { return; }
			}

			var cookie = cookie.replace( /^ +/, '' );
			if ( removeCookieByPart( cookie, ';path=/' )) { return; }
			if ( removeCookieByPart( cookie, ';domain=' + location.host + ';path=/' )) { return; }
            if ( removeCookieByPart( cookie, ';domain=' + location.host.split( '.' ).slice( -2 ).join( '.' ) + ';path=/' )) { return; }
		});
	},
	consent: function( sets ) {
		clearInterval( this.intervalID );

		let selectedSets = [];
		let reload = false;

		switch ( sets ) {
			case 'all':
				selectedSets = [ 0, 1, 2, 3, 4 ];
				break;
			
			case 'required':
				selectedSets = [ 1 ];
				reload = true;
				break;
			
			default:
				selectedSets = sets.map( Number );
				reload = true;
				break;
		}

		/* Remove empty or inactive sets, if any */
		const nonemptySets = this.getNonemptySets();
		if ( selectedSets.length && nonemptySets.length ) {
			selectedSets = selectedSets.filter( index => nonemptySets.includes( index ));
		}

		/* Add `required` set, if missing */
		if ( selectedSets.indexOf( 1 ) == -1 ) {
			selectedSets.push( 1 );
		}

		localStorage.setItem( 'c11t', selectedSets.join( ':' ) );

		/* Set sets' states */
		this.setStates( selectedSets );

		/* Set temporarily blocked cookies */
		this.flushBlocked();

		if ( reload ) {
			location.reload();
		}

		this.reobserve();
	},
	flushBlocked: function() {
		if ( this.removedList.length ) {
			this.removedList.forEach( function( cookie ) {
				document.cookie = cookie;
			});

			this.removedList = [];
		}
		
		if ( this.blockedList.length ) {
			this.blockedList.forEach( function( cookie ) {
				document.cookie = cookie;
			});

			this.blockedList = [];
		}
	},
	flushIFrames: function() {
		if ( !this.unconsentedIFrames || !this.unconsentedIFrames.size ) { return; }

		for ( const [ index, items ] of this.unconsentedIFrames ) {
			if ( !this.isConsented( index )) {
				continue;
			}

			/* Reload items */
			for ( const iframe of items ) {
				const src = iframe.dataset.consentSrc;
				iframe.removeAttribute( 'data-consent-category' );
				iframe.removeAttribute( 'data-consent-src' );

				if ( src ) {
					iframe.src = src;
				}

				items.delete( iframe );
			}
			/* Remove item from map */
			this.unconsentedIFrames.delete( index );
		}

		/* Nullify map, if empty */
		if ( !this.unconsentedIFrames.size ) {
			this.unconsentedIFrames = null;
		}
	},
	flushScripts: function() {
		if ( !this.unconsentedScripts || !this.unconsentedScripts.size ) { return; }

		for ( const [ index, items ] of this.unconsentedScripts ) {
			if ( !this.isConsented( index )) { continue; }

			/* Execute items */
			for ( const script of items ) {
				items.delete( script );

				script.removeAttribute( 'data-consent-category' );
				script.removeAttribute( 'type' );

				if ( script.hasAttribute( 'data-consent-src' )) {
					script.src = script.dataset.consentSrc;
					script.removeAttribute( 'data-consent-src' );
				}
				else if ( script.hasAttribute( 'src' )) {
					script.src = script.src;
				}
				else {
					const parent = script.parentElement;
					const referenceElement = script.nextSiblingElement ? script.nextSiblingElement : null;
					parent.insertBefore( script, referenceElement );
				}
			}

			/* Remove item from map */
			this.unconsentedScripts.delete( index );
		}

		/* Nullify map, if empty */
		if ( !this.unconsentedScripts.size ) {
			this.unconsentedScripts = null;
		}
	},
	getConsented: function() {
		var record = localStorage.getItem( 'c11t' );
		if ( record ) {
			if ( record.match( /^(\d\:?){1,5}$/ )) {
				var sets = record.split( ':' );
				if ( sets.indexOf( '1' ) == -1 ) {
					sets.push( 1 );
				}
				return sets.map( Number );
			}
			else {
				/* Return `required` set */
				return [ 1 ];
			}
		}
		return [];
	},
	getCookieName: function( cookie ) {
		var parts = cookie.split( ';' );
		if ( !parts.length ) { return; }

		var attrs = [ 'expires', 'domain', 'path', 'samesite', 'secure', 'max-age' ];

		for ( var i = 0; i != parts.length; ++i ) {
			var part = parts[ i ];
			var pair = part.split( '=' );
			if ( !pair.length ) { continue; }

			var key = pair[ 0 ].trim();
			if ( attrs.indexOf( key ) != -1 ) { continue; }

			return key;
		}
	},
	getNonemptySets: function() {
		return CookieconsentConfig && CookieconsentConfig.hasOwnProperty( 'nonemptySets' ) && Array.isArray( CookieconsentConfig.nonemptySets ) ? CookieconsentConfig.nonemptySets : [];
	},
	hasConsented: function() {
		return !!this.getConsented().length;
	},
	isConsented: function( index ) {
		const a = this.getConsented();
		return a.length && a.includes( index );
	},
	isGDPRZone: function() {
		/* Almost only EU countries in 0, +1 and +2 have DST */
		var currentYear = new Date().getFullYear();
		var janOffset = new Date( currentYear, 0, 1 ).getTimezoneOffset();
		var julOffset = new Date( currentYear, 6, 1 ).getTimezoneOffset();
		return ( janOffset >= -120 && janOffset <= 0 && julOffset < janOffset );
	},
	ondomcontentloaded: function() {
		this.reobserve();
	},
	reobserve: function() {
		this.rescanIFrames();
		this.rescanScripts();

		this.flushIFrames();
		this.flushScripts();
	},
	rescan: function( nodelist ) {
		if ( !nodelist.length ) { return null; }
		
		const map = new Map();
		
		for ( const x of nodelist ) {
			let index = x.dataset.consentCategory;
			if ( index == undefined ) { continue; }

			index = parseInt( index );

			const items = map.has( index ) ? map.get( index ) : new Set();
			items.add( x );

			map.set( index, items );
		}

		return map.size ? map : null;
	},
	rescanIFrames: function() {
		this.unconsentedIFrames = this.rescan( document.body.querySelectorAll( 'iframe[data-consent-src]' ));
	},
	rescanScripts: function() {
		this.unconsentedScripts = this.rescan( document.querySelectorAll( 'script[type="consent"]' ));
	},
	setStates: function( states ) {
		for ( var i = 0; i != 5; ++i ) {
			this.setMap[ i ].isOn = states.indexOf( i ) != -1;
		}
	},
	start: function() {
		if ( !this.isGDPRZone() ) { return; }

		this.itemMap = CookieconsentConfig.itemMap;
		for ( var i = 0; i != 5; ++i ) {
			this.setMap[ i ].items = CookieconsentConfig.setMap[ i ];
		}

		var consentedSets = this.getConsented();
		if ( !consentedSets.length ) {
			this.clearCookies();
		}
		else {
			/* Restore sets' states */
			this.setStates( consentedSets );
		}

		var _ = this;
		this.descriptor = Object.getOwnPropertyDescriptor( Document.prototype, 'cookie' ) || Object.getOwnPropertyDescriptor( HTMLDocument.prototype, 'cookie' );
		if ( this.descriptor && this.descriptor.configurable ) {
			Object.defineProperty( document, 'cookie', {
				get: function() {
					return _.descriptor.get.call( document );
				},
				set: function( cookie ) {
					/* Block nameless cookie */
					var name = _.getCookieName( cookie );
					if ( !name ) { return; }

					/* Block unknown cookie */
					if ( !(( _.whitelist.indexOf( name ) != -1 ) || _.itemMap.hasOwnProperty( name ))) { return; }
					
					var setID = _.itemMap[ name ];
					var set = _.setMap[ setID ];

					/* Block cookies of not yet accepted sets */
					if ( !set.isOn ) {
						console.log( 'COOKIEBLOCKER: Set `' + setID + '` was not consented. Cookie `' + name + '` was blocked.' );
						_.blockedList.push( cookie );
						return;
					}

					/* Set cookie */
					_.descriptor.set.call( document, cookie );
				}
			});
		}
		else {
			this.intervalID = setInterval( function() {
				_.clearCookies();
			}, 1000 );
		}
	}
};

CookieconsentManager.start();

function cookieconsent_ondomcontentloaded( e ) {
	e.stopImmediatePropagation();
	document.removeEventListener( 'DOMContentLoaded', cookieconsent_ondomcontentloaded );
}
document.addEventListener( 'DOMContentLoaded', cookieconsent_ondomcontentloaded );

function cookieconsent_onwindowloaded( e ) {
	e.stopImmediatePropagation();

	window.removeEventListener( 'load', cookieconsent_onwindowloaded );

	CookieconsentManager.ondomcontentloaded();
	document.dispatchEvent( new Event( 'DOMContentLoaded' ));
	
	window.dispatchEvent( new Event( 'load' ));
}
window.addEventListener( 'load', cookieconsent_onwindowloaded );
