( function ( $, undefined ) {
	var Field = acf.Field.extend( {
		type: 'taxonomy',

		data: {
			ftype: 'select',
		},

		select2: false,

		wait: 'load',

		events: {
			'click a[data-name="add"]': 'onClickAdd',
			'click input[type="radio"]': 'onClickRadio',
			'keydown label': 'onKeyDownLabel',
			removeField: 'onRemove',
		},

		$control: function () {
			return this.$( '.acf-taxonomy-field' );
		},

		$input: function () {
			return this.getRelatedPrototype().$input.apply( this, arguments );
		},

		getRelatedType: function () {
			// vars
			var fieldType = this.get( 'ftype' );

			// normalize
			if ( fieldType == 'multi_select' ) {
				fieldType = 'select';
			}

			// return
			return fieldType;
		},

		getRelatedPrototype: function () {
			return acf.getFieldType( this.getRelatedType() ).prototype;
		},

		getValue: function () {
			return this.getRelatedPrototype().getValue.apply( this, arguments );
		},

		setValue: function () {
			return this.getRelatedPrototype().setValue.apply( this, arguments );
		},

		initialize: function () {
			this.getRelatedPrototype().initialize.apply( this, arguments );
		},

		onRemove: function () {
			var proto = this.getRelatedPrototype();
			if ( proto.onRemove ) {
				proto.onRemove.apply( this, arguments );
			}
		},

		onClickAdd: function ( e, $el ) {
			// vars
			var field = this;
			var popup = false;
			var $form = false;
			var $name = false;
			var $parent = false;
			var $button = false;
			var $message = false;
			var notice = false;

			// step 1.
			var step1 = function () {
				// popup
				popup = acf.newPopup( {
					title: $el.attr( 'title' ),
					loading: true,
					width: '300px',
				} );

				// ajax
				var ajaxData = {
					action: 'acf/fields/taxonomy/add_term',
					field_key: field.get( 'key' ),
					nonce: field.get( 'nonce' ),
				};

				// get HTML
				$.ajax( {
					url: acf.get( 'ajaxurl' ),
					data: acf.prepareForAjax( ajaxData ),
					type: 'post',
					dataType: 'html',
					success: step2,
				} );
			};

			// step 2.
			var step2 = function ( html ) {
				// update popup
				popup.loading( false );
				popup.content( html );

				// vars
				$form = popup.$( 'form' );
				$name = popup.$( 'input[name="term_name"]' );
				$parent = popup.$( 'select[name="term_parent"]' );
				$button = popup.$( '.acf-submit-button' );

				// focus
				$name.trigger( 'focus' );

				// submit form
				popup.on( 'submit', 'form', step3 );
			};

			// step 3.
			var step3 = function ( e, $el ) {
				// prevent
				e.preventDefault();
				e.stopImmediatePropagation();

				// basic validation
				if ( $name.val() === '' ) {
					$name.trigger( 'focus' );
					return false;
				}

				// disable
				acf.startButtonLoading( $button );

				// ajax
				var ajaxData = {
					action: 'acf/fields/taxonomy/add_term',
					field_key: field.get( 'key' ),
					nonce: field.get( 'nonce' ),
					term_name: $name.val(),
					term_parent: $parent.length ? $parent.val() : 0,
				};

				$.ajax( {
					url: acf.get( 'ajaxurl' ),
					data: acf.prepareForAjax( ajaxData ),
					type: 'post',
					dataType: 'json',
					success: step4,
				} );
			};

			// step 4.
			var step4 = function ( json ) {
				// enable
				acf.stopButtonLoading( $button );

				// remove prev notice
				if ( notice ) {
					notice.remove();
				}

				// success
				if ( acf.isAjaxSuccess( json ) ) {
					// clear name
					$name.val( '' );

					// update term lists
					step5( json.data );

					// notice
					notice = acf.newNotice( {
						type: 'success',
						text: acf.getAjaxMessage( json ),
						target: $form,
						timeout: 2000,
						dismiss: false,
					} );
				} else {
					// notice
					notice = acf.newNotice( {
						type: 'error',
						text: acf.getAjaxError( json ),
						target: $form,
						timeout: 2000,
						dismiss: false,
					} );
				}

				// focus
				$name.trigger( 'focus' );
			};

			// step 5.
			var step5 = function ( term ) {
				// update parent dropdown
				var $option = $(
					'<option value="' +
						term.term_id +
						'">' +
						term.term_label +
						'</option>'
				);
				if ( term.term_parent ) {
					$parent
						.children( 'option[value="' + term.term_parent + '"]' )
						.after( $option );
				} else {
					$parent.append( $option );
				}

				// add this new term to all taxonomy field
				var fields = acf.getFields( {
					type: 'taxonomy',
				} );

				fields.map( function ( otherField ) {
					if (
						otherField.get( 'taxonomy' ) == field.get( 'taxonomy' )
					) {
						otherField.appendTerm( term );
					}
				} );

				// select
				field.selectTerm( term.term_id );
			};

			// run
			step1();
		},

		appendTerm: function ( term ) {
			if ( this.getRelatedType() == 'select' ) {
				this.appendTermSelect( term );
			} else {
				this.appendTermCheckbox( term );
			}
		},

		appendTermSelect: function ( term ) {
			this.select2.addOption( {
				id: term.term_id,
				text: term.term_label,
			} );
		},

		appendTermCheckbox: function ( term ) {
			// vars
			var name = this.$( '[name]:first' ).attr( 'name' );
			var $ul = this.$( 'ul:first' );

			// allow multiple selection
			if ( this.getRelatedType() == 'checkbox' ) {
				name += '[]';
			}

			// create new li
			var $li = $(
				[
					'<li data-id="' + term.term_id + '">',
					'<label>',
					'<input type="' +
						this.get( 'ftype' ) +
						'" value="' +
						term.term_id +
						'" name="' +
						name +
						'" /> ',
					'<span>' + term.term_name + '</span>',
					'</label>',
					'</li>',
				].join( '' )
			);

			// find parent
			if ( term.term_parent ) {
				// vars
				var $parent = $ul.find(
					'li[data-id="' + term.term_parent + '"]'
				);

				// update vars
				$ul = $parent.children( 'ul' );

				// create ul
				if ( ! $ul.exists() ) {
					$ul = $( '<ul class="children acf-bl"></ul>' );
					$parent.append( $ul );
				}
			}

			// append
			$ul.append( $li );
		},

		selectTerm: function ( id ) {
			if ( this.getRelatedType() == 'select' ) {
				this.select2.selectOption( id );
			} else {
				var $input = this.$( 'input[value="' + id + '"]' );
				$input.prop( 'checked', true ).trigger( 'change' );
			}
		},

		onClickRadio: function ( e, $el ) {
			// vars
			var $label = $el.parent( 'label' );
			var selected = $label.hasClass( 'selected' );

			// remove previous selected
			this.$( '.selected' ).removeClass( 'selected' );

			// add active class
			$label.addClass( 'selected' );

			// allow null
			if ( this.get( 'allow_null' ) && selected ) {
				$label.removeClass( 'selected' );
				$el.prop( 'checked', false ).trigger( 'change' );
			}
		},
		onKeyDownLabel: function ( e, $el ) {
			// bail early if not space or enter
			if ( e.which !== 13 ) {
				return;
			}
			e.preventDefault();
			const firstInput = $el.find( 'input' ).first();
			if ( firstInput.length ) {
				firstInput.trigger( 'click' ).trigger( 'focus' );
			}
		},
	} );

	acf.registerFieldType( Field );
} )( jQuery );
