/**
 * Text pattern plugin for TinyMCE
 *
 * @since 4.3.0
 *
 * This plugin can automatically format text patterns as you type. It includes two patterns:
 *  - Unordered list (`* ` and `- `).
 *  - Ordered list (`1. ` and `1) `).
 *
 * If the transformation in unwanted, the user can undo the change by pressing backspace,
 * using the undo shortcut, or the undo button in the toolbar.
 */
( function( tinymce, setTimeout ) {
	tinymce.PluginManager.add( 'wptextpattern', function( editor ) {
		var $$ = editor.$,
			VK = tinymce.util.VK,
			patterns = [],
			canUndo = false;

		/**
		 * Add a pattern to format with a callback.
		 *
		 * @since 4.3.0
		 *
		 * @param {RegExp}   regExp   RegEx pattern.
		 * @param {Function} callback Callback.
		 */
		function add( regExp, callback ) {
			patterns.push( {
				regExp: regExp,
				callback: callback
			} );
		}

		add( /^[*-]\s/, function() {
			this.execCommand( 'InsertUnorderedList' );
		} );

		add( /^1[.)]\s/, function() {
			this.execCommand( 'InsertOrderedList' );
		} );

		add( /^>\s/, function() {
			this.formatter.toggle( 'blockquote' );
		} );

		add( /^(#{2,6})\s/, function() {
			this.formatter.toggle( 'h' + arguments[1].length );
		} );

		editor.on( 'selectionchange', function() {
			canUndo = false;
		} );

		editor.on( 'keydown', function( event ) {
			if ( canUndo && ( event.keyCode === VK.BACKSPACE || event.keyCode === 27 /* ESCAPE */ ) ) {
				editor.undoManager.undo();
				event.preventDefault();
			}
		} );

		editor.on( 'keyup', function( event ) {
			var rng, node, text, parent, child;

			if ( event.keyCode !== VK.SPACEBAR ) {
				return;
			}

			rng = editor.selection.getRng();
			node = rng.startContainer;

			if ( ! node || node.nodeType !== 3 ) {
				return;
			}

			text = node.nodeValue;
			parent = editor.dom.getParent( node, 'p' );

			if ( ! parent ) {
				return;
			}

			while ( child = parent.firstChild ) {
				if ( child.nodeType !== 3 ) {
					parent = child;
				} else {
					break;
				}
			}

			if ( ! child ) {
				return;
			}

			if ( ! child.nodeValue ) {
				child = child.nextSibling;
			}

			if ( child !== node ) {
				return;
			}

			tinymce.each( patterns, function( pattern ) {
				var args,
					replace = text.replace( pattern.regExp, function() {
						args = arguments;
						return '';
					} );

				if ( text === replace ) {
					return;
				}

				if ( rng.startOffset !== text.length - replace.length ) {
					return;
				}

				editor.undoManager.add();

				editor.undoManager.transact( function() {
					var $$parent;

					if ( replace ) {
						$$( node ).replaceWith( document.createTextNode( replace ) );
					} else  {
						$$parent = $$( node.parentNode );

						$$( node ).remove();

						if ( ! $$parent.html() ) {
							$$parent.append( '<br>' );
						}
					}

					editor.selection.setCursorLocation( parent );

					pattern.callback.apply( editor, args );
				} );

				// We need to wait for native events to be triggered.
				setTimeout( function() {
					canUndo = true;
				} );

				return false;
			} );
		} );
	} );
} )( window.tinymce, window.setTimeout );
