/* global wp, JSON */
/* eslint consistent-this: [ "error", "control" ] */
/* eslint complexity: ["error", 8] */

( function ( api, $ ) {
	'use strict';

	/**
	 * An image gallery control.
	 *
	 * @class
	 * @augments wp.customize.Control
	 * @augments wp.customize.Class
	 */
	api.ImageGalleryControl = api.Control.extend(
		{
			initialize: function ( id, options ) {
				var control = this,
				args;

				args = options || {};

				if ( ! args.params.file_type ) {
					args.params.file_type = 'image';
				}

				if ( ! args.params.type ) {
					args.params.type = 'shopwell-gallery';
				}
				if ( ! args.params.content ) {
					args.params.content = $( '<li></li>' );
					args.params.content.attr( 'id', 'customize-control-' + id.replace( /]/g, '' ).replace( /\[/g, '-' ) );
					args.params.content.attr( 'class', 'customize-control customize-control-' + args.params.type );
				}

				if ( ! args.params.attachments ) {
					args.params.attachments = [];
				}

				api.Control.prototype.initialize.call( control, id, args );
			},

			/**
			 * When the control's DOM structure is ready,
			 * set up internal event bindings.
			 */
			ready: function () {
				var control = this;

				// Shortcut so that we don't have to use _.bind every time we add a callback.
				_.bindAll( control, 'openFrame', 'select' );

				/**
				 * Set gallery data and render content.
				 */
				function setGalleryDataAndRenderContent() {

					var value = control.setting.get();
					control.setAttachmentsData( value ).done(
						function () {
							control.renderContent();
							control.setupSortable();
						}
					);
				}

				// Ensure attachment data is initially set.
				setGalleryDataAndRenderContent();

				// Update the attachment data and re-render the control when the setting changes.
				control.setting.bind( setGalleryDataAndRenderContent );

				// Bind events.
				control.container.on( 'click keydown', '.upload-button', control.openFrame );
			},

			/**
			 * Fetch attachment data for rendering in control content.
			 *
			 * @param {Array} value Setting value, array of attachment ids.
			 * @returns {*}
			 */
			setAttachmentsData: function ( value ) {
				var control = this,
				promises    = [];

				control.params.attachments = [];

				_.each(
					value,
					function ( id, index ) {
						var hasAttachmentData = new $.Deferred();
						wp.media.attachment( id ).fetch().done(
							function () {
								control.params.attachments[ index ] = this.attributes;
								hasAttachmentData.resolve();
							}
						);
						promises.push( hasAttachmentData );
					}
				);

				return $.when.apply( undefined, promises ).promise();
			},

			/**
			 * Open the media modal.
			 */
			openFrame: function ( event ) {
				event.preventDefault();

				if ( ! this.frame ) {
					this.initFrame();
				}

				this.frame.open();
			},

			/**
			 * Initiate the media modal select frame.
			 * Save it for using later in case needed.
			 */
			initFrame: function () {

				var control = this,
				preSelectImages;
				this.frame  = wp.media(
					{
						button: {
							text: control.params.button_labels.frame_button
						},
						states: [
						new wp.media.controller.Library(
							{
								title: control.params.button_labels.frame_title,
								library: wp.media.query( { type: control.params.file_type } ),
								multiple: true
							}
						)
					]
					}
				);

				/**
				 * Pre-select images according to saved settings.
				 */
				preSelectImages = function () {
					var selection, ids, attachment, library;

					library   = control.frame.state().get( 'library' );
					selection = control.frame.state().get( 'selection' );

					ids = control.setting.get();

					// Sort the selected images to top when opening media modal.
					library.comparator = function ( a, b ) {
						var hasA = true === this.mirroring.get( a.cid ),
						hasB     = true === this.mirroring.get( b.cid );

						if ( ! hasA && hasB ) {
							return -1;
						} else if ( hasA && ! hasB ) {
							return 1;
						} else {
							return 0;
						}
					};

					_.each(
						ids,
						function ( id ) {
							attachment = wp.media.attachment( id );
							selection.add( attachment ? [ attachment ] : [] );
							library.add( attachment ? [ attachment ] : [] );
						}
					);
				};
				control.frame.on( 'open', preSelectImages );
				control.frame.on( 'select', control.select );
			},

			/**
			 * Callback for selecting attachments.
			 */
			select: function () {

				var control = this,
				attachments, attachmentIds;

				attachments                = control.frame.state().get( 'selection' ).toJSON();
				control.params.attachments = attachments;

				attachmentIds = control.getAttachmentIds( attachments );
				control.setSettingValues( attachmentIds );
			},

			/**
			 * Get array of attachment id-s from attachment objects.
			 *
			 * @param {Array} attachments Attachments.
			 * @returns {Array}
			 */
			getAttachmentIds: function ( attachments ) {
				var ids = [],
				i;
				for ( i in attachments ) {
					ids.push( attachments[ i ].id );
				}
				return ids;
			},

			/**
			 * Set setting values.
			 *
			 * @param {Array} values Array of ids.
			 */
			setSettingValues: function ( values ) {
				var control = this;
				control.setting.set( values );
			},

			/**
			 * Setup sortable.
			 *
			 * @returns {void}
			 */
			setupSortable: function () {
				var control = this,
				list        = $( '.image-gallery-attachments' );
				list.sortable(
					{
						items: '.image-gallery-thumbnail-wrapper',
						tolerance: 'pointer',
						stop: function () {
							var selectedValues = [];
							list.find( '.image-gallery-thumbnail-wrapper' ).each(
								function () {
									var id;
									id = parseInt( $( this ).data( 'postId' ), 10 );
									selectedValues.push( id );
								}
							);
							control.setSettingValues( selectedValues );
						}
					}
				);
			}

		}
	);

	api.controlConstructor['shopwell-gallery'] = api.ImageGalleryControl;

}( wp.customize, jQuery ) );
