ShortcodesUltimateMakerEditor.Attributes = ( function( $ ) {

	/**
	 * Define main object.
	 */
	var self = {};

	/**
	 * Define component elements.
	 */
	self.el = {
		field: $( '#sum-attributes' ),
		value: $( '#sum-attributes-value' ),
		head: null,
		items: null,
		add: null
	};

	/**
	 * Define component templates.
	 */
	self.tpl = {
		general: $( '#sum-attributes-tpl' ).html(),
		item: $( '#sum-attributes-item-tpl' ).html(),
		option: $( '#sum-attributes-option-tpl' ).html(),
		types: ''
	};

	/**
	 * Retrieve l10n strings.
	 */
	self.l10n = ShortcodesUltimateMakerEditorData.l10n.Attributes;

	/**
	 * State variable to store number of existsting attributes.
	 */
	self.index = 0;

	/**
	 * Default attribute settings.
	 */
	self.defaults = ShortcodesUltimateMakerEditorData.attributes.defaults;

	/**
	 * Attribute field types.
	 */
	self.types = ShortcodesUltimateMakerEditorData.attributes.types;

	/**
	 * Initialize the component.
	 */
	self.init = function() {

		/**
		 * Parse general template.
		 */
		self.tpl.general = self.parseTemplate( self.tpl.general, self.l10n.general );

		/**
		 * Append general template to the attributes field.
		 */
		$( self.tpl.general ).appendTo( self.el.field );

		/**
		 * Define items container element.
		 */
		self.el.items = self.el.field.children( '.sum-attributes-items' );

		/**
		 * Add existing attributes.
		 */
		var value = self.getValue(),
			i = 0;

		for ( ; i < value.length; i++ ) {
			self.addAttribute( value[ i ] );
		}

		/**
		 * Make attributes sortable.
		 */
		self.el.items.sortable( {
			tolerance: 'pointer',
			handle: '.sum-attributes-item-head',
			containment: 'parent',
			placeholder: 'sum-attributes-item-placeholder',
			start: function() {
				self.closeAll();
			},
			stop: function() {
				self.updateValue();
			}
		} );

		/**
		 * Define add attribute button element (a.button).
		 */
		self.el.add = self.el.field.children( '.sum-attributes-add' );
		self.el.add.children( 'a' ).text( self.l10n.add );

		/**
		 * Setup event listeners.
		 */
		self.bindEvents();

	};

	/**
	 * Setup event listeners.
	 */
	self.bindEvents = function() {

		self.el.add.on( 'click', 'a', self.addNewAttribute );

		self.el.field.on(
			'click',
			'.sum-attributes-item-head-label > a, ' +
			'.sum-attributes-item-head-toggle, ' +
			'.sum-attributes-item-toggle',
			self.toggleAttribute
		);

		self.el.field.on( 'click', '.sum-attributes-item-close', self.toggleAttribute );
		self.el.field.on( 'click', '.sum-attributes-item-head-delete', self.deleteAttribute );
		self.el.field.on( 'click', '.sum-attributes-item-restore > a', self.restoreAttribute );

		self.el.field.on( 'change', '.sum-attributes-item-label', self.changeLabel );
		self.el.field.on( 'change', '.sum-attributes-item-name', self.changeName );
		self.el.field.on( 'change', '.sum-attributes-item-type', self.changeType );
		self.el.field.on( 'change', '.sum-attributes-item-default', self.changeDefault );

		self.el.field.on( 'keyup', '.sum-attributes-item-name', self.validateName );

		self.el.items.on( 'change', 'td input, td select, td textarea', self.updateValue );

	};

	/**
	 * Add new attribute.
	 */
	self.addNewAttribute = function() {

		var index = self.index + 1;

		self.addAttribute( $.extend( {}, self.defaults, {
			name: self.l10n.item.newName.replace( '%s', index ),
			slug: 'attribute_' + index,
			options: "option1|%s 1\noption2|%s 2".replace( /%s/g, self.l10n.general.option ),
			open: true
		} ) );

		self.updateValue();

	};

	/**
	 * Add attribute with specific settings.
	 */
	self.addAttribute = function( args ) {

		args = $.extend( {},
			self.defaults,
			args
		);

		var itemData = $.extend( {}, {
				index: self.index
			},
			self.l10n.item
		);

		var item = $( self.parseTemplate( self.tpl.item, itemData ) );

		item.find( '.sum-attributes-item-head-label > a' ).text( args.name || args.slug );
		item.find( '.sum-attributes-item-head-type' ).text( self.types[ args.type ] );
		item.find( '.sum-attributes-item-head-default' ).text( args.default );

		item.find( '.sum-attributes-item-label' ).val( args.name );
		item.find( '.sum-attributes-item-default' ).val( args.default );
		item.find( '.sum-attributes-item-name' ).val( args.slug );
		item.find( '.sum-attributes-item-desc' ).val( args.desc );
		item.find( '.sum-attributes-item-options' ).val( args.options );
		item.find( '.sum-attributes-item-min' ).val( args.min );
		item.find( '.sum-attributes-item-max' ).val( args.max );
		item.find( '.sum-attributes-item-step' ).val( args.step );

		item.find( '.sum-attributes-item-type' )
			.append( self.getTypes() )
			.val( args.type );

		item.attr( 'data-type', args.type );

		item.appendTo( self.el.items );

		self.index++;

		if ( typeof args.open !== undefined && args.open ) {

			self.closeAll();

			item.addClass( 'open' );

			item.find( 'input:text:first' ).focus();

		}

	};

	/**
	 * Close all attributes.
	 */
	self.closeAll = function() {
		self.el.items.children( 'div' ).removeClass( 'open' );
	};

	/**
	 * Get field value as an object.
	 */
	self.getValue = function() {
		return JSON.parse( self.el.value.val() );
	};

	/**
	 * Retrieve list of field types as <option> tags.
	 */
	self.getTypes = function() {

		if ( self.tpl.types !== '' ) {
			return self.tpl.types;
		}

		var i = 0;

		$.each( self.types, function( value, label ) {
			self.tpl.types += self.tpl.option
				.replace( '%value%', value )
				.replace( '%label%', label );
		} );

		return self.tpl.types;

	};

	/**
	 * Update hidden field value.
	 */
	self.updateValue = function() {

		var attributes = self.el.items.children( 'div' ),
			newValue = [];

		/**
		 * Loop attributes.
		 */
		attributes.each( function( attributeIndex ) {

			var item = $( this ),
				inputs = item.find( 'input, textarea, select' ),
				values = {};

			// Skip deleted attributes
			if ( item.hasClass( 'deleted' ) ) {
				return;
			}

			/**
			 * Loop inputs.
			 */
			inputs.each( function() {

				var input = $( this ),
					name = input.data( 'name' ),
					value = input.val();

				values[ name ] = value;

			} );

			newValue.push( values );

		} );

		self.el.value.val( JSON.stringify( newValue ) );

		$( document ).trigger( 'sum/attributes/updated' );

	};

	/**
	 * Helper function to parse template.
	 */
	self.parseTemplate = function( template, data ) {

		return template.replace( /%(\w+)%/g, function( _, k ) {
			return ( data[ k ] !== undefined ) ? data[ k ] : '';
		} );

	};

	/**
	 * Toggle clicked attribute.
	 */
	self.toggleAttribute = function( event ) {

		event.preventDefault();

		var item = $( this ).parents( '.sum-attributes-item' );

		if ( item.hasClass( 'open' ) ) {
			self.closeAll();
		} else {
			self.closeAll();
			item.addClass( 'open' );
		}

	};

	/**
	 * Delete the attribute.
	 */
	self.deleteAttribute = function( event ) {

		event.preventDefault();

		var item = $( this ).parents( '.sum-attributes-item' );

		item.removeClass( 'open' );
		item.addClass( 'deleted' );

		self.updateValue();

	};

	/**
	 * Restore the attribute.
	 */
	self.restoreAttribute = function( event ) {

		var item = $( this ).parents( '.sum-attributes-item' );

		item.removeClass( 'deleted' );

		self.updateValue();

	};

	/**
	 * Update attribute label.
	 */
	self.changeLabel = function() {

		var item = $( this ).parents( '.sum-attributes-item' ),
			displayValue = item.find( '.sum-attributes-item-head-label > a' ),
			label = item.find( '.sum-attributes-item-label' ).val(),
			name = item.find( '.sum-attributes-item-name' ).val();

		// Field label is not empty
		if ( label !== '' ) {
			displayValue.text( label );
		}

		// Field label is empty, use name as attribute label
		else {
			displayValue.text( name );
		}

	};

	/**
	 * Update attribute name.
	 */
	self.changeName = function() {

		var item = $( this ).parents( '.sum-attributes-item' ),
			displayValue = item.find( '.sum-attributes-item-head-label > a' ),
			label = item.find( '.sum-attributes-item-label' ).val(),
			name = item.find( '.sum-attributes-item-name' ).val();

		// Field label is empty, use name as attribute label
		if ( label === '' ) {
			displayValue.text( name );
		}

	};

	/**
	 * Update attribute type.
	 */
	self.changeType = function( event ) {

		var item = $( this ).parents( '.sum-attributes-item' ),
			value = item.find( '.sum-attributes-item-head-type' ),
			fieldValue = $( this ).val();

		value.text( self.types[ fieldValue ] );

		item.attr( 'data-type', fieldValue );

	};

	/**
	 * Update default value.
	 */
	self.changeDefault = function() {

		var item = $( this ).parents( '.sum-attributes-item' ),
			value = item.find( '.sum-attributes-item-head-default' ),
			fieldValue = $( this ).val();

		value.text( fieldValue );

	};

	/**
	 * Validate attribute name.
	 */
	self.validateName = function() {

		var field = $( this ),
			value = field.val(),
			isValid = /^([a-z0-9_]*)$/.test( value ),
			isEmpty = value === '';

		if ( isValid ) {
			field.parent( 'td' ).removeClass( 'sum-validation-failed' );
		} else {
			field.parent( 'td' ).addClass( 'sum-validation-failed' );
		}

		if ( isEmpty ) {
			field.parent( 'td' ).addClass( 'sum-validation-required' );
		} else {
			field.parent( 'td' ).removeClass( 'sum-validation-required' );
		}

	};

	/**
	 * Provide public API.
	 */
	return {
		init: self.init
	};

} )( jQuery );