/*
 * This file is part of the MediaWiki extension MultimediaViewer.
 *
 * MultimediaViewer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * MultimediaViewer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MultimediaViewer.  If not, see <http://www.gnu.org/licenses/>.
 */

( function ( mw, $, oo ) {
	// Shortcut for prototype later
	var DP;

	/**
	 * UI component that provides functionality to download the media asset displayed.
	 *
	 * @class mw.mmv.ui.download.Pane
	 * @extends mw.mmv.ui.Element
	 * @constructor
	 * @param {jQuery} $container
	 */
	function Pane( $container ) {
		mw.mmv.ui.Element.call( this, $container );

		/** @property {mw.mmv.ui.Utils} utils - */
		this.utils = new mw.mmv.ui.Utils();

		this.$pane = $( '<div>' )
			.addClass( 'mw-mmv-download-pane' )
			.appendTo( this.$container );

		this.$downloadArea = $( '<div>' )
			.addClass( 'mw-mmv-download-area' )
			.appendTo( this.$pane );

		this.createDownloadButton( this.$downloadArea );
		this.createSizePulldownMenu( this.$downloadArea );
		this.createPreviewLink( this.$downloadArea );

		this.formatter = new mw.mmv.EmbedFileFormatter();
		this.currentAttrView = 'plain';
		this.createAttributionButton( this.$pane );

		/**
		 * Default item for the size menu.
		 * @property {OO.ui.MenuOptionWidget}
		 */
		this.defaultItem = this.downloadSizeMenu.getMenu().findSelectedItem();

		/** @property {mw.mmv.model.Image|null} Image the download button currently points to. */
		this.image = null;
	}
	oo.inheritClass( Pane, mw.mmv.ui.Element );
	DP = Pane.prototype;

	/**
	 * @event mmv-download-cta-open
	 * Fired when the attribution call to action panel is clicked.
	 */
	/**
	 * @event mmv-download-cta-close
	 * Fired when the attribution area is closed.
	 */

	/**
	 * Creates download split button. It is a link with the "download" property set plus an
	 * arrow that allows the user to select the image size desired. The "download" property
	 * triggers native browser downloading in browsers that support it. The fallback is the
	 * 'download' parameter which instructs the server to send the right headers so the browser
	 * downloads the file instead of just displaying it. If all this fails, the image will appear
	 * in another window/tab.
	 *
	 * @param {jQuery} $container
	 */
	DP.createDownloadButton = function ( $container ) {
		// TODO: Use OOUI progressive button widget instead
		this.$downloadButton = $( '<a>' )
			.attr( 'target', '_blank' )
			.attr( 'download', '' )
			.addClass( 'mw-ui-button mw-ui-progressive mw-mmv-download-go-button' )
			.click( function () {
				mw.mmv.actionLogger.log( 'download' );
			} );

		this.$selectionArrow = $( '<span>' )
			.addClass( 'mw-ui-button mw-ui-progressive mw-mmv-download-select-menu' )
			.append(
				$( '<span>' )
					.addClass( 'mw-mmv-download-image-size-name' )
					.html( '&nbsp;' )
			)
			.append(
				$( '<span>' )
					.addClass( 'mw-mmv-download-image-size' )
					.html( '&nbsp;' )
			);

		$container
			.append( this.$downloadButton )
			.append( this.$selectionArrow );
	};

	/**
	 * Creates pulldown menu to select image sizes.
	 *
	 * @param {jQuery} $container
	 */
	DP.createSizePulldownMenu = function ( $container ) {
		this.downloadSizeMenu = this.utils.createPulldownMenu(
			[ 'original', 'small', 'medium', 'large' ],
			[ 'mw-mmv-download-size' ],
			'original'
		);

		this.downloadSizeMenu.getMenu().on( 'select', function ( item ) {
			mw.mmv.actionLogger.log( 'download-select-menu-' + item.data.name );
		} );

		$container.append( this.downloadSizeMenu.$element );
	};

	/**
	 * Creates preview link.
	 *
	 * @param {jQuery} $container
	 */
	DP.createPreviewLink = function ( $container ) {
		this.$previewLink = $( '<a>' )
			.attr( 'target', '_blank' )
			.addClass( 'mw-mmv-download-preview-link' )
			.text( mw.message( 'multimediaviewer-download-preview-link-title' ).text() )
			.appendTo( $container )
			.click( function () {
				mw.mmv.actionLogger.log( 'download-view-in-browser' );
			} );
	};

	DP.createAttributionButton = function ( $container ) {
		var dl = this,
			attributionInput = new oo.ui.TextInputWidget( {
				classes: [ 'mw-mmv-download-attr-input' ],
				readOnly: true
			} ),
			attributionSwitch = new oo.ui.ButtonSelectWidget( {
				classes: [ 'mw-mmv-download-attr-select' ]
			} ),
			plainOption = new oo.ui.ButtonOptionWidget( {
				data: 'plain',
				label: mw.message( 'multimediaviewer-attr-plain' ).text()
			} ),
			htmlOption = new oo.ui.ButtonOptionWidget( {
				data: 'html',
				label: mw.message( 'multimediaviewer-attr-html' ).text()
			} );

		attributionSwitch.addItems( [
			plainOption,
			htmlOption
		] );

		attributionSwitch.selectItem( plainOption );

		attributionSwitch.on( 'select', function ( selection ) {
			dl.selectAttribution( selection.getData() );

			dl.attributionInput.$element.find( 'input' ).focus();
		} );

		this.$attributionSection = $( '<div>' )
			.addClass( 'mw-mmv-download-attribution mw-mmv-download-attribution-collapsed' )
			.appendTo( $container )
			.click( function () {
				if ( dl.$attributionSection.hasClass( 'mw-mmv-download-attribution-collapsed' ) ) {
					dl.$container.trigger( 'mmv-download-cta-open' );
					dl.$attributionSection.removeClass( 'mw-mmv-download-attribution-collapsed' );
					dl.attributionInput.$element.find( 'input' ).focus();
				}
			} );

		this.$attributionCtaHeader = $( '<p>' )
			.addClass( 'mw-mmv-download-attribution-cta-header' )
			.text( mw.message( 'multimediaviewer-download-attribution-cta-header' ).text() );
		this.$attributionCta = $( '<div>' )
			.addClass( 'mw-mmv-download-attribution-cta' )
			.append(
				this.$attributionCtaHeader,
				$( '<p>' )
					.addClass( 'mw-mmv-download-attribution-cta-invite' )
					.text( mw.message( 'multimediaviewer-download-attribution-cta' ).text() )
			)
			.appendTo( this.$attributionSection );
		this.attributionInput = attributionInput;
		this.$attributionCopy = this.$copyButton = $( '<button>' )
			.addClass( 'mw-ui-button mw-mmv-button mw-mmv-dialog-copy' )
			.click( function () {
				// Select the text, and then try to copy the text.
				// If the copy fails or is not supported, continue as if nothing had happened.
				dl.attributionInput.select();
				try {
					if ( document.queryCommandSupported &&
						document.queryCommandSupported( 'copy' ) ) {
						document.execCommand( 'copy' );
					}
				} catch ( e ) {
					// queryCommandSupported in Firefox pre-41 can throw errors when used with
					// clipboard commands. We catch and ignore these and other copy-command-related
					// errors here.
				}
			} )
			.prop( 'title', mw.msg( 'multimediaviewer-download-attribution-copy' ) )
			.text( mw.msg( 'multimediaviewer-download-attribution-copy' ) )
			.tipsy( {
				delayIn: mw.config.get( 'wgMultimediaViewer' ).tooltipDelay,
				gravity: this.correctEW( 'se' )
			} );

		this.$attributionHowHeader = $( '<p>' )
			.addClass( 'mw-mmv-download-attribution-how-header' )
			.text( mw.message( 'multimediaviewer-download-attribution-cta-header' ).text() );
		this.$attributionHow = $( '<div>' )
			.addClass( 'mw-mmv-download-attribution-how' )
			.append(
				this.$attributionHowHeader,
				this.attributionInput.$element,
				this.$attributionCopy,
				attributionSwitch.$element,
				$( '<p>' )
					.addClass( 'mw-mmv-download-attribution-close-button' )
					.click( function ( e ) {
						dl.$container.trigger( 'mmv-download-cta-close' );
						dl.$attributionSection.addClass( 'mw-mmv-download-attribution-collapsed' );
						e.stopPropagation();
					} )
					.text( ' ' )
			)
			.appendTo( this.$attributionSection );
	};

	/**
	 * Selects the specified attribution type.
	 *
	 * @param {string} [name='plain'] The attribution type to use ('plain' or 'html')
	 */
	DP.selectAttribution = function ( name ) {
		this.currentAttrView = name;

		if ( this.currentAttrView === 'html' ) {
			this.attributionInput.setValue( this.htmlCredit );
		} else {
			this.attributionInput.setValue( this.textCredit );
		}
	};

	/**
	 * Registers listeners.
	 */
	DP.attach = function () {
		var download = this;

		// Register handlers for switching between file sizes
		this.downloadSizeMenu.getMenu().on( 'choose', function ( item ) {
			download.handleSizeSwitch( item );
		} );
		this.$selectionArrow.on( 'click', function () {
			download.downloadSizeMenu.getMenu().toggle();
		} );

		this.attributionInput.$element.find( 'input' )
			.on( 'focus', this.selectAllOnEvent )
			.on( 'mousedown click', this.onlyFocus );
	};

	/**
	 * Clears listeners.
	 */
	DP.unattach = function () {
		mw.mmv.ui.Element.prototype.unattach.call( this );

		this.downloadSizeMenu.getMenu().off( 'choose' );
		this.$selectionArrow.off( 'click' );

		this.attributionInput.$element.find( 'input' )
			.off( 'focus mousedown click' );
	};

	/**
	 * Handles size menu change events.
	 *
	 * @param {OO.ui.MenuOptionWidget} item
	 */
	DP.handleSizeSwitch = function ( item ) {
		var download = this,
			value = item.getData();

		if ( value.name === 'original' && this.image !== null ) {
			this.setDownloadUrl( this.image.url );
			this.setButtonText( value.name, this.getExtensionFromUrl( this.image.url ),
				value.width, value.height );
		} else {
			// Disable download while we get the image
			this.$downloadButton.addClass( 'disabledLink' );
			// Set a temporary message. It will be updated once we have the file type.
			this.setButtonText( value.name, this.imageExtension, value.width, value.height );

			this.utils.getThumbnailUrlPromise( value.width ).done( function ( thumbnail ) {
				download.setDownloadUrl( thumbnail.url );
				download.setButtonText( value.name, download.getExtensionFromUrl( thumbnail.url ),
					value.width, value.height );
			} );
		}
	};

	/**
	 * Sets the URL on the download button.
	 *
	 * @param {string} url
	 */
	DP.setDownloadUrl = function ( url ) {
		this.$downloadButton.attr( 'href', url + '?download' );
		this.$previewLink.attr( 'href', url );

		// Re-enable download
		this.$downloadButton.removeClass( 'disabledLink' );
	};

	/**
	 * Sets the text of the download button.
	 *
	 * @param {string} sizeClass A size class such as 'small'
	 * @param {string} extension file extension
	 * @param {number} width
	 * @param {number} height
	 */
	DP.setButtonText = function ( sizeClass, extension, width, height ) {
		var sizeClasMessage, sizeMessage, dimensionMessage;

		sizeClasMessage = mw.message( 'multimediaviewer-download-' + sizeClass + '-button-name' ).text();
		dimensionMessage = mw.message( 'multimediaviewer-embed-dimensions', width, height ).text();
		sizeMessage = mw.message( 'multimediaviewer-embed-dimensions-with-file-format',
			dimensionMessage, extension ).text();

		// Update button label and size strings to reflect new selected size
		this.$downloadButton.html(
			'<span class="mw-mmv-download-image-size-name">' + sizeClasMessage + '</span>' +
			'<span class="mw-mmv-download-image-size">' + sizeMessage + '</span>'
		);
	};

	/**
	 * Sets the text in the attribution input element.
	 *
	 * @param {mw.mmv.model.EmbedFileInfo} embed
	 */
	DP.setAttributionText = function ( embed ) {
		this.htmlCredit = this.formatter.getCreditHtml( embed );
		this.textCredit = this.formatter.getCreditText( embed );
		this.selectAttribution( this.currentAttrView );
	};

	/**
	 * Chops off the extension part of an URL.
	 *
	 * @param {string} url URL
	 * @return {string} Extension
	 */
	DP.getExtensionFromUrl = function ( url ) {
		var urlParts = url.split( '.' );
		return urlParts[ urlParts.length - 1 ];
	};

	/**
	 * Sets the data on the element.
	 *
	 * @param {mw.mmv.model.Image} image
	 * @param {mw.mmv.model.Repo} repo
	 */
	DP.set = function ( image, repo ) {
		var attributionCtaMessage,
			license = image && image.license,
			sizeOptions = this.downloadSizeMenu.getMenu().getItems(),
			sizes = this.utils.getPossibleImageSizesForHtml( image.width, image.height );

		this.image = image;

		this.utils.updateMenuOptions( sizes, sizeOptions );

		this.downloadSizeMenu.$element.addClass( 'active' );

		// Note: This extension will not be the real one for file types other than: png/gif/jpg/jpeg
		this.imageExtension = image.title.getExtension().toLowerCase();

		// Reset size menu to default item and update download button label now that we have the info
		this.downloadSizeMenu.getMenu().chooseItem( this.defaultItem );

		if ( image && repo ) {
			this.setAttributionText( new mw.mmv.model.EmbedFileInfo( image, repo ) );
		}

		attributionCtaMessage = ( license && license.needsAttribution() ) ?
			'multimediaviewer-download-attribution-cta-header' :
			'multimediaviewer-download-optional-attribution-cta-header';
		this.$attributionCtaHeader.text( mw.message( attributionCtaMessage ).text() );
		this.$attributionHowHeader.text( mw.message( attributionCtaMessage ).text() );
	};

	/**
	 * @inheritdoc
	 */
	DP.empty = function () {
		this.downloadSizeMenu.getMenu().toggle( false );
		this.downloadSizeMenu.$element.removeClass( 'active' );

		this.$downloadButton.attr( 'href', '' );
		this.$previewLink.attr( 'href', '' );
		this.imageExtension = undefined;

		this.image = null;
	};

	mw.mmv.ui.download.Pane = Pane;
}( mediaWiki, jQuery, OO ) );
