/*!
 * VisualEditor DataModel Surface class.
 *
 * @copyright 2011-2020 VisualEditor Team and others; see http://ve.mit-license.org
 */

/**
 * DataModel surface.
 *
 * @class
 * @extends ve.ce.Surface
 *
 * @constructor
 * @param {ve.dm.Surface} model
 * @param {ve.ui.Surface} ui
 * @param {Object} [config]
 */
ve.ce.MWWikitextSurface = function VeCeMwWikitextSurface() {
	// Parent constructors
	ve.ce.MWWikitextSurface.super.apply( this, arguments );

	this.pasteTargetInput = new OO.ui.MultilineTextInputWidget();
};

/* Inheritance */

OO.inheritClass( ve.ce.MWWikitextSurface, ve.ce.Surface );

/**
 * @inheritdoc
 */
ve.ce.MWWikitextSurface.prototype.onCopy = function ( e ) {
	var originalSelection, scrollTop, slice, clipboardKey,
		view = this,
		clipboardData = e.originalEvent.clipboardData,
		text = this.getModel().getFragment().getText( true ).replace( /\n\n/g, '\n' );

	if ( !text ) {
		return;
	}

	if ( clipboardData ) {
		// Disable the default event so we can override the data
		e.preventDefault();
		clipboardData.setData( 'text/plain', text );
		// We're not going to set HTML, but for browsers that support custom data, set a clipboard key
		if ( ve.isClipboardDataFormatsSupported( e, true ) ) {
			slice = this.model.documentModel.shallowCloneFromSelection( this.getModel().getSelection() );
			this.clipboardIndex++;
			clipboardKey = this.clipboardId + '-' + this.clipboardIndex;
			this.clipboard = { slice: slice, hash: null };
			// Clone the elements in the slice
			slice.data.cloneElements( true );
			clipboardData.setData( 'text/xcustom', clipboardKey );

			// Explicitly store wikitext as text/x-wiki, so that wikitext-aware paste
			// contexts can accept it without having to do any content-
			// sniffing.
			clipboardData.setData( 'text/x-wiki', text );
		}
	} else {
		originalSelection = new ve.SelectionState( this.nativeSelection );

		// Save scroll position before changing focus to "offscreen" paste target
		scrollTop = this.$window.scrollTop();

		// Prevent surface observation due to native range changing
		this.surfaceObserver.disable();
		this.$pasteTarget.empty().append( this.pasteTargetInput.$element );
		this.pasteTargetInput.setValue( text ).select();

		// Restore scroll position after changing focus
		this.$window.scrollTop( scrollTop );

		// setTimeout: postpone until after the default copy action
		setTimeout( function () {
			// Change focus back
			view.$attachedRootNode[ 0 ].focus();
			view.showSelectionState( originalSelection );
			// Restore scroll position
			view.$window.scrollTop( scrollTop );
			view.surfaceObserver.clear();
			view.surfaceObserver.enable();
			// Detach input
			view.pasteTargetInput.$element.detach();
		} );
	}
};

/**
 * @inheritdoc
 */
ve.ce.MWWikitextSurface.prototype.afterPasteInsertExternalData = function ( targetFragment, pastedDocumentModel, contextRange ) {
	var promise, plainPastedDocumentModel, plainContextRange,
		wasSpecial = this.pasteSpecial,
		// TODO: This check returns true if the paste contains meaningful structure (tables, lists etc.)
		// but no annotations (bold, links etc.).
		wasPlain = wasSpecial || pastedDocumentModel.data.isPlainText( contextRange, true, undefined, true ),
		view = this;

	plainPastedDocumentModel = pastedDocumentModel.shallowCloneFromRange( contextRange );
	plainPastedDocumentModel.data.sanitize( { plainText: true, keepEmptyContentBranches: true } );
	// We just turned this into plaintext, which probably
	// affected the content-length. Luckily, because of
	// the earlier clone, we know we just want the whole
	// document, and because of the major change to
	// plaintext, the difference between originalRange and
	// balancedRange don't really apply. As such, clear
	// out newDocRange. (Can't just make it undefined;
	// need to exclude the internal list, and since we're
	// from a paste we also have to exclude the
	// opening/closing paragraph.)
	plainContextRange = new ve.Range( plainPastedDocumentModel.getDocumentRange().from + 1, plainPastedDocumentModel.getDocumentRange().to - 1 );
	view.pasteSpecial = true;

	// isPlainText is true but we still need sanitize (e.g. remove lists)
	promise = ve.ce.MWWikitextSurface.super.prototype.afterPasteInsertExternalData.call( this, targetFragment, plainPastedDocumentModel, plainContextRange );
	if ( ve.init.target.constructor.static.convertToWikitextOnPaste && !wasPlain ) {
		promise.then( function () {
			// We need to wait for the selection change after paste as that triggers
			// a contextChange event. Really we should wait for the afterPaste promise to resolve.
			setTimeout( function () {
				var surface = view.getSurface(),
					context = surface.getContext();
				// HACK: Directly set the 'relatedSources' result in the context to trick it
				// into showing a context at the end of the paste. This context will disappear
				// as soon as the selection change as a contextChange will fire.
				// TODO: Come up witha method to store this context on the surface model then
				// have the LinearContext read it from there.
				context.relatedSources = [ {
					embeddable: false,
					// HACK²: Pass the rich text document and original fragment (which should now cover
					// the pasted text) to the context via the otherwise-unused 'model' property.
					model: {
						doc: pastedDocumentModel,
						contextRange: contextRange,
						fragment: targetFragment
					},
					name: 'wikitextPaste',
					type: 'item'
				} ];
				context.afterContextChange();
				surface.getModel().once( 'select', function () {
					context.relatedSources = [];
					context.afterContextChange();
				} );
			} );
		} );
	}
	return promise;
};
