(
	function( $ ) {
		'use strict';

		$.fn.BilleyGridLayout = function() {
			var $el, $grid, resizeTimer;

			/**
			 * Calculate size for grid items
			 */
			function calculateMasonrySize( isotopeOptions ) {
				var tabletBreakPoint = 1025,
				    mobileBreakPoint = 768,
				    windowWidth      = window.innerWidth,
				    gridWidth        = $grid[ 0 ].getBoundingClientRect().width,
				    gridColumns      = 1,
				    gridGutter       = 0,
				    zigzagHeight     = 0,
				    settings         = $el.data( 'grid' ),
				    lgGutter         = settings.gutter ? settings.gutter : 0,
				    mdGutter         = settings.gutterTablet ? settings.gutterTablet : lgGutter,
				    smGutter         = settings.gutterMobile ? settings.gutterMobile : mdGutter,
				    lgColumns        = settings.columns ? settings.columns : 1,
				    mdColumns        = settings.columnsTablet ? settings.columnsTablet : lgColumns,
				    smColumns        = settings.columnsMobile ? settings.columnsMobile : mdColumns,
				    lgZigzagHeight   = settings.zigzagHeight ? settings.zigzagHeight : 0,
				    mdZigzagHeight   = settings.zigzagHeightTablet ? settings.zigzagHeightTablet : lgZigzagHeight,
				    smZigzagHeight   = settings.zigzagHeightMobile ? settings.zigzagHeightMobile : mdZigzagHeight,
				    zigzagReversed   = settings.zigzagReversed && settings.zigzagReversed === 1 ? true : false;

				if ( typeof elementorFrontendConfig !== 'undefined' ) {
					tabletBreakPoint = elementorFrontendConfig.breakpoints.lg;
					mobileBreakPoint = elementorFrontendConfig.breakpoints.md;
				}

				if ( windowWidth >= tabletBreakPoint ) {
					gridColumns = lgColumns;
					gridGutter = lgGutter;
					zigzagHeight = lgZigzagHeight;
				} else if ( windowWidth >= mobileBreakPoint ) {
					gridColumns = mdColumns;
					gridGutter = mdGutter;
					zigzagHeight = mdZigzagHeight;
				} else {
					gridColumns = smColumns;
					gridGutter = smGutter;
					zigzagHeight = smZigzagHeight;
				}

				var totalGutterPerRow = (
					                        gridColumns - 1
				                        ) * gridGutter;

				var columnWidth = (
					                  gridWidth - totalGutterPerRow
				                  ) / gridColumns;

				columnWidth = Math.floor( columnWidth );

				var columnWidth2 = columnWidth;
				if ( gridColumns > 1 ) {
					columnWidth2 = columnWidth * 2 + gridGutter;
				}

				$grid.children( '.grid-sizer' ).css( {
					'width': columnWidth + 'px'
				} );

				var columnHeight   = 0,
				    columnHeight2  = 0, // 200%.
				    columnHeight7  = 0, // 70%.
				    columnHeight13 = 0, // 130%.
				    isMetro        = false,
				    ratioW         = 1,
				    ratioH         = 1;

				if ( settings.ratio ) {
					ratioH = settings.ratio;
					isMetro = true;
				}

				// Calculate item height for only metro type.
				if ( isMetro ) {
					columnHeight = columnWidth * ratioH / ratioW;
					columnHeight = Math.floor( columnHeight );

					if ( gridColumns > 1 ) {
						columnHeight2 = columnHeight * 2 + gridGutter;
						columnHeight13 = parseInt( columnHeight * 1.3 );
						columnHeight7 = columnHeight2 - gridGutter - columnHeight13;
					} else {
						columnHeight2 = columnHeight7 = columnHeight13 = columnHeight;
					}
				}

				$grid.children( '.grid-item' ).each( function( index ) {
					var gridItem = $( this );

					// Zigzag.
					if (
						zigzagHeight > 0 // Has zigzag.
						&& gridColumns > 1 // More than 1 column.
						&& index + 1 <= gridColumns // On top items.
					) {
						if ( zigzagReversed === false ) { // Is odd item.
							if ( index % 2 === 0 ) {
								gridItem.css( {
									'marginTop': zigzagHeight + 'px'
								} );
							} else {
								gridItem.css( {
									'marginTop': '0px'
								} );
							}
						} else {
							if ( index % 2 !== 0 ) {
								gridItem.css( {
									'marginTop': zigzagHeight + 'px'
								} );
							} else {
								gridItem.css( {
									'marginTop': '0px'
								} );
							}
						}

					} else {
						gridItem.css( {
							'marginTop': '0px'
						} );
					}

					if ( gridItem.data( 'width' ) === 2 ) {
						gridItem.css( {
							'width': columnWidth2 + 'px'
						} );
					} else {
						gridItem.css( {
							'width': columnWidth + 'px'
						} );
					}

					if ( 'grid' === settings.type ) {
						gridItem.css( {
							'marginBottom': gridGutter + 'px'
						} );
					}

					if ( isMetro ) {
						var $itemHeight;

						if ( gridItem.hasClass( 'grid-item-height' ) ) {
							$itemHeight = gridItem;
						} else {
							$itemHeight = gridItem.find( '.grid-item-height' );
						}

						if ( gridItem.data( 'height' ) === 2 ) {
							$itemHeight.css( {
								'height': columnHeight2 + 'px'
							} );
						} else if ( gridItem.data( 'height' ) === 1.3 ) {
							$itemHeight.css( {
								'height': columnHeight13 + 'px'
							} );
						} else if ( gridItem.data( 'height' ) === 0.7 ) {
							$itemHeight.css( {
								'height': columnHeight7 + 'px'
							} );
						} else {
							$itemHeight.css( {
								'height': columnHeight + 'px'
							} );
						}
					}
				} );

				if ( isotopeOptions ) {
					isotopeOptions.packery.gutter = gridGutter;
					isotopeOptions.fitRows.gutter = gridGutter;
					$grid.isotope( isotopeOptions );
				}

				$grid.isotope( 'layout' );
			}

			function handlerEntranceAnimation() {
				var $items = $grid.children( '.grid-item' );

				$items.elementorWaypoint( function() {
					// Fix for different ver of waypoints plugin.
					var _self = this.element ? this.element : this;
					var $self = $( _self );
					$self.addClass( 'animate' );
				}, {
					offset: '90%',
					triggerOnce: true
				} );
			}

			return this.each( function() {
				$el = $( this );
				$grid = $el.find( '.billey-grid' );

				var settings = $el.data( 'grid' );
				var gridData;

				if ( $grid.length > 0 && settings && typeof settings.type !== 'undefined' ) {
					var isotopeOptions = {
						itemSelector: '.grid-item',
						percentPosition: true,
						transitionDuration: 0,
						packery: {
							columnWidth: '.grid-sizer',
						},
						fitRows: {
							gutter: 10
						}
					};

					if ( $billey.isRTL ) {
						isotopeOptions.originLeft = false;
					}

					if ( 'masonry' === settings.type || 'metro' === settings.type ) {
						isotopeOptions.layoutMode = 'packery';
					} else {
						isotopeOptions.layoutMode = 'fitRows';
					}

					gridData = $grid.imagesLoaded( function() {
						calculateMasonrySize( isotopeOptions );
						$grid.addClass( 'loaded' );
					} );

					gridData.one( 'arrangeComplete', function() {
						handlerEntranceAnimation();
					} );

					$( window ).on( 'resize', function() {
						calculateMasonrySize( isotopeOptions );

						// Sometimes layout can be overlap. then re-cal layout one time.
						clearTimeout( resizeTimer );
						resizeTimer = setTimeout( function() {
							// Run code here, resizing has "stopped"
							calculateMasonrySize( isotopeOptions );
						}, 500 ); // DO NOT decrease the time. Sometime, It'll make layout overlay on resize.
					} );
				} else {
					handlerEntranceAnimation();
				}

				$el.on( 'BilleyQueryEnd', function( event, el, $items ) {
					if ( $grid.length > 0 && settings && typeof settings.type !== 'undefined' ) {
						$grid.isotope()
						     .append( $items )
						     .isotope( 'reloadItems', $items )
						     .imagesLoaded()
						     .always( function() {
							     $items.addClass( 'animate' );
							     calculateMasonrySize( isotopeOptions );
						     } );
					} else {
						$grid.append( $items ).imagesLoaded().always( function() {
							$items.addClass( 'animate' );
						} );
					}
				} );
			} );
		};
	}( jQuery )
);
