/*!
 * VisualEditor UserInterface UrlStringTransferHandler class.
 *
 * @copyright See AUTHORS.txt
 */

/**
 * Handle pastes and drag-and-drop of URLs and links.
 * Attempts to preserve link titles when possible.
 *
 * @class
 * @extends ve.ui.PlainTextStringTransferHandler
 *
 * @constructor
 * @param {ve.ui.Surface} surface
 * @param {ve.ui.DataTransferItem} item
 */
ve.ui.UrlStringTransferHandler = function VeUiUrlStringTransferHandler() {
	// Parent constructor
	ve.ui.UrlStringTransferHandler.super.apply( this, arguments );
};

/* Inheritance */

OO.inheritClass( ve.ui.UrlStringTransferHandler, ve.ui.PlainTextStringTransferHandler );

/* Static properties */

ve.ui.UrlStringTransferHandler.static.name = 'urlString';

ve.ui.UrlStringTransferHandler.static.types = [
	// DnD standard, no title information
	'text/uri-list',
	// Support: Firefox
	// Firefox type, preserves title
	'text/x-moz-url',
	// Support: Edge
	// Format used by Microsoft Edge when copying from the address bar (T341281)
	'text/link-preview',
	// Used in GNOME drag-and-drop
	'text/x-uri',
	// Identify links in pasted plain text as well
	...ve.ui.UrlStringTransferHandler.super.static.types
];

ve.ui.UrlStringTransferHandler.static.handlesPaste = true;

/**
 * RegExp matching an external url.
 *
 * @property {RegExp}
 * @private
 */
ve.ui.UrlStringTransferHandler.static.urlRegExp = null; // Initialized below

ve.init.Platform.static.initializedPromise.then( () => {
	ve.ui.UrlStringTransferHandler.static.urlRegExp =

		new RegExp(
			ve.init.platform.getExternalLinkUrlProtocolsRegExp().source +
				'\\S+$',
			'i' // Protocols are case-insensitive
		);
} );

/* Methods */

ve.ui.UrlStringTransferHandler.static.matchFunction = function ( item ) {
	// Match all specific mime types
	if ( ve.ui.UrlStringTransferHandler.static.types.includes( item.type ) &&
		item.type !== 'text/plain' ) {
		return true;
	}

	// If the type if unspecified or text/plain, then let's check whether it
	// is a valid URL.
	return ve.ui.UrlStringTransferHandler.static.urlRegExp.test(
		item.getAsString()
	);
};

/**
 * @inheritdoc
 */
ve.ui.UrlStringTransferHandler.prototype.process = function () {
	const surface = this.surface,
		store = surface.getModel().getDocument().getStore(),
		linkAction = ve.ui.actionFactory.create( 'link', surface ),
		data = this.item.getAsString();

	let links;
	switch ( this.item.type ) {
		case 'text/uri-list':
			// text/uri-list has embedded comments; remove them before
			// autolinking.  In theory the embedded comments can be
			// used for link titles, but I've never seen this done by
			// real apps.  You could add code here to annotate the
			// links with the comment information if you can find a
			// spec for how it should be done.
			links = data.replace( /^#.*(\r\n?|\n|$)/mg, '' ).trim()
				.split( /[\r\n]+/g ).map( ( line ) => ( { href: line } ) );
			// Support: Chrome
			// When Chrome uses this mime type the link titles can
			// be extracted from the 'text/html' version of the item.
			// Let's try that.
			if ( this.item.data.htmlStringData ) {
				const html = this.item.data.htmlStringData;
				const doc = ve.createDocumentFromHtml( html );
				links = $.makeArray( doc.querySelectorAll( 'a[href]' ) )
					.map( ( a ) => ( { href: a.href, title: a.textContent } ) );
			}
			break;

		case 'text/x-moz-url':
			// text/x-moz-url includes titles with the links (alternating lines)
			links = data.match( /^(.*)(\r\n?|\n)(.*)$/mg ).map( ( item ) => {
				item = item.split( /[\r\n]+/ );
				return { href: item[ 0 ], title: item[ 1 ] };
			} );
			break;

		case 'text/link-preview': {
			// data is a JSON string
			try {
				const parsed = JSON.parse( data );
				if ( parsed.url && parsed.title ) {
					links = [ { href: parsed.url, title: parsed.title } ];
				}
			} catch ( err ) {
				links = [];
			}
			break;
		}

		default:
			// A single URL
			links = [ { href: data.trim() } ];
			break;
	}

	// Create linked text.
	const result = [];
	links.forEach( ( link ) => {
		const annotation = linkAction.getLinkAnnotation( link.href ),
			annotationSet = new ve.dm.AnnotationSet( store, store.hashAll( [
				annotation
			] ) ),
			title = link.title || annotation.getDisplayTitle(),
			content = title.split( '' );

		// Put a space between multiple links
		if ( result.length ) {
			result.push( ' ' );
		}

		ve.dm.Document.static.addAnnotationsToData( content, annotationSet );
		ve.batchPush( result, content );
	} );
	this.resolve( result );
};

/* Registration */

ve.ui.dataTransferHandlerFactory.register( ve.ui.UrlStringTransferHandler );
