jQuery( function ( $ ) {
	'use strict';

	// Global variables
	var $parent = $( '#parent_id' );

	// List of selectors for each type of element.
	// Made for compatibility with classic and Gutenberg editors.
	var selectors = {
		'template': ['#page_template', '.editor-page-attributes__template .components-select-control__input'],
		'post_format': ['input[name="post_format"]', '.editor-post-format .components-select-control__input'],
		'parent': ['#parent_id'], // No selector in Gutenberg, thus not working.
	};
	var elements = {};

	var $document = $( document );

	var initialCheck = false;

	function initElements() {
		_.forEach( selectors, function( list, key ) {
			var selector = _.find( list, function( selector ) {
				return $( selector ).length;
			} );
			elements[key] = $( selector );
		} );
	}

	// Callback functions to check for each condition
	var checkCallbacks = {
		template   : function ( templates ) {
			var value = initialCheck ? elements.template.val() : MBShowHideData.template,
				result = -1 !== templates.indexOf( value );
			console.log( 'Check by template:', result );
			return result;
		},
		post_format: function ( formats ) {
			// Make sure registered formats in lowercase
			formats = formats.map( function ( format ) {
				return format.toLowerCase();
			} );

			var value = MBShowHideData.post_format;
			if ( initialCheck ) {
				value = elements.post_format.is( 'select' ) ? elements.post_format.val() : elements.post_format.filter( ':checked' ).val();
			}
			if ( !value || 0 == value ) {
				value = 'standard';
			}

			var result = -1 != formats.indexOf( value );
			console.log( 'Check by post format:', result );
			return result;
		},
		taxonomy   : function ( taxonomy, terms ) {
			var values = [],
				$inputs = $( '#' + taxonomy + 'checklist :checked' );

			$inputs.each( function () {
				var $input = $( this ),
					text = $.trim( $input.parent().text() );
				values.push( parseInt( $input.val() ) );
				values.push( text );
			} );

			for ( var i = 0, len = values.length; i < len; i++ ) {
				if ( -1 != terms.indexOf( values[i] ) ) {
					return true;
				}
			}
			return false;
		},
		input_value: function ( inputValues, relation ) {
			relation = relation || 'OR';

			for ( var i in inputValues ) {
				var $element = $( i ),
					value = $.trim( $element.val() ),
					checked = null;

				if ( $element.is( ':checkbox' ) ) {
					checked = $element.is( ':checked' ) === !!inputValues[i];
				}

				if ( $element.is( ':radio' ) ) {
					value = $.trim( $element.filter( ':checked' ).val() );
				}

				if ( 'OR' == relation ) {
					if ( ( value == inputValues[i] && checked === null ) || checked === true ) {
						return true;
					}
				} else {
					if ( ( value != inputValues[i] && checked === null ) || checked === false ) {
						return false;
					}
				}
			}
			return relation != 'OR';
		},
		is_child   : function () {
			var value = initialCheck ? elements.parent.val() : MBShowHideData.parent,
				result = !! parseInt( value );
			console.log( 'Check by is child:', result );
			return result;
		}
	};

	// Callback functions to addEventListeners for "change" event in each condition
	var addEventListenersCallbacks = {
		/**
		 * Check by page templates
		 *
		 * @param callback Callback function
		 *
		 * @return bool
		 */
		template   : function ( callback ) {
			$document.on( 'change', selectors.template.join( ',' ), callback );
		},
		post_format: function ( callback ) {
			$document.on( 'change', selectors.post_format.join( ',' ), callback );
		},
		taxonomy   : function ( taxonomy, callback ) {
			// Fire "change" event when click on popular category
			// See wp-admin/js/post.js
			$( '#' + taxonomy + 'checklist-pop' ).on( 'click', 'input', function () {
				var t = $( this ), val = t.val(), id = t.attr( 'id' );
				if ( !val ) {
					return;
				}

				var tax = id.replace( 'in-popular-', '' ).replace( '-' + val, '' );
				$( '#in-' + tax + '-' + val ).trigger( 'change' );
			} );

			$( '#' + taxonomy + 'checklist' ).on( 'change', 'input', callback );
		},
		input_value: function ( callback, selector ) {
			$( selector ).on( 'change', callback );
		},
		is_child   : function ( callback ) {
			$document.on( 'change', selectors.parent.join( ',' ), callback );
		}
	};

	/**
	 * Add JS addEventListenersers to check conditions to show/hide a meta box
	 * @param type
	 * @param conditions
	 * @param $metaBox
	 */
	function maybeShowHide( type, conditions, $metaBox ) {
		var condition = checkAllConditions( conditions );

		if ( 'show' == type ) {
			condition ? $metaBox.show() : $metaBox.hide();
		} else {
			condition ? $metaBox.hide() : $metaBox.show();
		}
	}

	/**
	 * Check all conditions
	 * @param conditions Array of all conditions
	 *
	 * @return bool
	 */
	function checkAllConditions( conditions ) {
		// Don't change "global" conditions
		var localConditions = $.extend( {}, conditions );

		var relation = localConditions.hasOwnProperty( 'relation' ) ? localConditions['relation'].toUpperCase() : 'OR',
			value;

		// For better loop of checking terms
		if ( localConditions.hasOwnProperty( 'relation' ) ) {
			delete localConditions['relation'];
		}

		initElements();

		var checkBy = ['template', 'post_format', 'input_value', 'is_child'],
			by, condition;

		for ( var i = 0, l = checkBy.length; i < l; i++ ) {
			by = checkBy[i];

			if ( !localConditions.hasOwnProperty( by ) ) {
				continue;
			}

			// Call callback function to check for each condition
			condition = checkCallbacks[by]( localConditions[by], relation );

			if ( 'OR' == relation ) {
				value = typeof value == 'undefined' ? condition : value || condition;
				if ( value ) {
					break;
				}
			} else {
				value = typeof value == 'undefined' ? condition : value && condition;
				if ( !value ) {
					break;
				}
			}

			delete localConditions[by];
		}

		// By taxonomy, including category and post format
		// Note that we unset all other parameters, so we can safely loop in the localConditions array
		if ( localConditions.length ) {
			for ( var taxonomy in localConditions ) {
				if ( !localConditions.hasOwnProperty( taxonomy ) ) {
					continue;
				}

				// Call callback function to check for each condition
				condition = checkCallbacks['taxonomy']( taxonomy, localConditions[taxonomy] );

				if ( 'OR' == relation ) {
					value = typeof value == 'undefined' ? condition : value || condition;
					if ( value ) {
						break;
					}
				} else {
					value = typeof value == 'undefined' ? condition : value && condition;
					if ( !value ) {
						break;
					}
				}
			}
		}

		initialCheck = true;

		return value;
	}

	/**
	 * Add event addEventListenersers for "change" event
	 * This will re-check all conditions to show/hide meta box
	 * @param type
	 * @param conditions
	 * @param $metaBox
	 */
	function addEventListeners( type, conditions, $metaBox ) {
		// Don't change "global" conditions
		var localConditions = $.extend( {}, conditions );

		// For better loop of checking terms
		if ( localConditions.hasOwnProperty( 'relation' ) ) {
			delete localConditions['relation'];
		}

		var checkBy = ['template', 'post_format', 'input_value', 'is_child'], by;
		for ( var i = 0, l = checkBy.length; i < l; i++ ) {
			by = checkBy[i];

			if ( ! localConditions.hasOwnProperty( by ) ) {
				continue;
			}

			if ( 'input_value' != by ) {
				// Call callback function to check for each condition
				addEventListenersCallbacks[by]( function () {
					maybeShowHide( type, conditions, $metaBox );
				} );
				delete localConditions[by];
				continue;
			}

			// Input values
			for ( var selector in localConditions[by] ) {
				// Call callback function to check for each condition
				addEventListenersCallbacks[by]( function () {
					maybeShowHide( type, conditions, $metaBox );
				}, selector );
			}
			delete localConditions[by];

		}

		// By taxonomy, including category and post format
		// Note that we unset all other parameters, so we can safely loop in the localConditions array
		if ( !localConditions.length ) {
			for ( var taxonomy in localConditions ) {
				if ( ! localConditions.hasOwnProperty( taxonomy ) ) {
					continue;
				}

				// Call callback function to check for each condition
				addEventListenersCallbacks['taxonomy']( taxonomy, function () {
					maybeShowHide( type, conditions, $metaBox );
				} );
			}
		}
	}

	function init() {
		$( '.mb-show-hide' ).each( function () {
			var $this = $( this ),
				$metaBox = $this.closest( '.postbox' ),
				conditions;

			// Check for show rules
			if ( $this.data( 'show' ) ) {
				conditions = $this.data( 'show' );
				maybeShowHide( 'show', conditions, $metaBox );
			}

			// Check for hide rules
			if ( $this.data( 'hide' ) ) {
				conditions = $this.data( 'hide' );
				maybeShowHide( 'hide', conditions, $metaBox );
			}
		} );
	}

	function initEventListeners() {
		$( '.mb-show-hide' ).each( function () {
			var $this = $( this ),
				$metaBox = $this.closest( '.postbox' ),
				conditions;

			// Check for show rules
			if ( $this.data( 'show' ) ) {
				conditions = $this.data( 'show' );
				addEventListeners( 'show', conditions, $metaBox );
			}

			// Check for hide rules
			if ( $this.data( 'hide' ) ) {
				conditions = $this.data( 'hide' );
				addEventListeners( 'hide', conditions, $metaBox );
			}
		} );
	}

	// Run the code after Gutenberg has done rendering.
	// https://stackoverflow.com/a/34999925/371240
	setTimeout( function() {
		window.requestAnimationFrame( function() {
			initElements();
			init();
			initEventListeners();
		} );
	}, 1000 );
} );