/*!
 * VisualEditor ContentEditable MWImageNode class.
 *
 * @copyright 2011-2020 VisualEditor Team and others; see AUTHORS.txt
 * @license The MIT License (MIT); see LICENSE.txt
 */

/**
 * ContentEditable MediaWiki image node.
 *
 * @class
 * @abstract
 * @extends ve.ce.GeneratedContentNode
 * @mixins ve.ce.FocusableNode
 * @mixins ve.ce.MWResizableNode
 *
 * @constructor
 * @param {jQuery} $focusable Focusable part of the node
 * @param {jQuery} $image Image part of the node
 * @param {Object} [config] Configuration options
 */
ve.ce.MWImageNode = function VeCeMWImageNode( $focusable, $image, config ) {
	config = ve.extendObject( {
		enforceMax: false,
		minDimensions: { width: 1, height: 1 },
		$bounding: this.$element
	}, config );

	// Properties
	this.$image = $image;
	// Parent constructor triggers render so this must precede it
	this.renderedDimensions = null;

	// Parent constructor
	ve.ce.GeneratedContentNode.call( this );

	// Mixin constructors
	ve.ce.FocusableNode.call( this, $focusable, config );
	ve.ce.MWResizableNode.call( this, this.$image, config );

	// Events
	this.model.connect( this, { attributeChange: 'onAttributeChange' } );

	// Initialization
	this.updateMediaType();
};

/* Inheritance */

OO.inheritClass( ve.ce.MWImageNode, ve.ce.GeneratedContentNode );

OO.mixinClass( ve.ce.MWImageNode, ve.ce.FocusableNode );

// Need to mixin base class as well (T92540)
OO.mixinClass( ve.ce.MWImageNode, ve.ce.ResizableNode );

OO.mixinClass( ve.ce.MWImageNode, ve.ce.MWResizableNode );

/* Static Properties */

ve.ce.MWImageNode.static.primaryCommandName = 'media';

/* Static Methods */

/**
 * @inheritdoc ve.ce.Node
 */
ve.ce.MWImageNode.static.getDescription = function ( model ) {
	var title = new mw.Title( model.getFilename() );
	return title.getMainText();
};

/* Methods */

/**
 * Update the rendering of the 'align', src', 'width' and 'height' attributes
 * when they change in the model.
 *
 * @param {string} key Attribute key
 * @param {string} from Old value
 * @param {string} to New value
 */
ve.ce.MWImageNode.prototype.onAttributeChange = function () {
	this.update();
};

/**
 * @inheritdoc ve.ce.GeneratedContentNode
 */
ve.ce.MWImageNode.prototype.onGeneratedContentNodeUpdate = function () {
	// Do nothing to avoid re-rendering every time the caption is changed.
	// Call update inside onAttributeChange instead.
};

/**
 * @inheritdoc ve.ce.GeneratedContentNode
 */
ve.ce.MWImageNode.prototype.generateContents = function () {
	var model = this.getModel(),
		width = model.getAttribute( 'width' ),
		height = model.getAttribute( 'height' ),
		mwData = model.getAttribute( 'mw' ) || {},
		deferred = ve.createDeferred();

	// If the current rendering is larger don't fetch a new image, just let the browser resize
	if ( this.renderedDimensions && this.renderedDimensions.width > width ) {
		return deferred.reject().promise();
	}

	var params;
	if ( mwData.thumbtime !== undefined ) {
		params = 'seek=' + mwData.thumbtime;
	} else if ( mwData.page !== undefined ) {
		params = 'page' + mwData.page + '-' + width + 'px';
		// Don't send width twice
		width = undefined;
	}

	var xhr = ve.init.target.getContentApi( this.getModel().getDocument() ).get( {
		action: 'query',
		prop: 'imageinfo',
		iiprop: 'url',
		iiurlwidth: width,
		iiurlheight: height,
		iiurlparam: params,
		titles: this.getModel().getFilename()
	} )
		.done( this.onParseSuccess.bind( this, deferred ) )
		.fail( this.onParseError.bind( this, deferred ) );

	return deferred.promise( { abort: xhr.abort } );
};

/**
 * Handle a successful response from the parser for the image src.
 *
 * @param {jQuery.Deferred} deferred The Deferred object created by generateContents
 * @param {Object} response Response data
 */
ve.ce.MWImageNode.prototype.onParseSuccess = function ( deferred, response ) {
	var thumburl = ve.getProp( response.query.pages[ 0 ], 'imageinfo', 0, 'thumburl' );
	if ( thumburl ) {
		deferred.resolve( thumburl );
	} else {
		deferred.reject();
	}
};

/**
 * @inheritdoc ve.ce.GeneratedContentNode
 */
ve.ce.MWImageNode.prototype.render = function ( generatedContents ) {
	this.$image.attr( 'src', generatedContents );
	// As we only re-render when the image is larger than last rendered size
	// this will always be the largest ever rendering
	this.renderedDimensions = ve.copy( this.model.getScalable().getCurrentDimensions() );
	if ( this.live ) {
		this.afterRender();
	}
};

/**
 * Handle an unsuccessful response from the parser for the image src.
 *
 * @param {jQuery.Deferred} deferred The promise object created by generateContents
 * @param {Object} response Response data
 */
ve.ce.MWImageNode.prototype.onParseError = function ( deferred ) {
	deferred.reject();
};

/**
 * Update rendering when media type changes
 */
ve.ce.MWImageNode.prototype.updateMediaType = function () {
	this.$image.toggleClass( 've-ce-mwImageNode-audioPlayer', this.model.getMediaType() === 'AUDIO' );
};
