import { useEffect, createElement, useLayoutEffect, useRef } from '@wordpress/element';
import { useBlockProps } from '@wordpress/block-editor';
import { SaveBlocks } from './save-blocks';
import { PreviewType } from './getPreviewType';
import * as BlockFunctions from  './widgets';
import { addDynamicStyles } from  './add-dynamic-styles';
import { pagelayerParseHtmlToReact } from './html-to-react';
import { PagelayerLibrary } from "./library";

const { pagelayer_shortcodes, pagelayer_styles} = pagelayer_config;
const pagelayer_tabs = ['settings', 'options'];

var pl_blocks = pagelayer_shortcodes;
var pl_blocks_styles = pagelayer_styles;
var pagelayer_pro = 1;

for(const block in pl_blocks){
	
	let block_name = pagelayerBlockNameByTag(block),
	pl_props = pl_blocks[block];
	
	let iconClass = 'pagelayer-shortcode ';
	if ('icon' in pl_blocks[block]) {
		iconClass += pl_blocks[block]['icon']
	}
	else {
		iconClass += 'pagelayer-' + block
	}

	let icons = createElement('i', { class: iconClass });
	
	// Cache the block name 
	pagelayerCacheBlockTags[block_name] = block;
	
	var blockProps  = {};
	
	// To hide block from list need to set parent
	if('parent' in pl_props){
		blockProps['parent'] = [];
		
		for(let i in pl_props['parent']){
			blockProps['parent'][i] = pagelayerBlockNameByTag(pl_props['parent'][i]);
		}
	}else{
		// Skip category for child blocks to hide form the left panel
		blockProps['category'] = 'pagelayer';
	}
	
	// Create attribute Object
	var attributes = {};
	
	for(var i in pagelayer_tabs){
		var tab = pagelayer_tabs[i];
		var section_close = false;// First section always open
		
		//section_loop2:
		for(var section in pl_props[tab]){
			//console.log(tab+' '+section);
			
			var props = section in pl_props ? pl_props[section] : pl_blocks_styles[section];
			//console.log(props);	
	
			// Reset / Create the cache
			for(var x in props){
				attributes[x] = new Object();
				
				attributes[x] = {
					type: props[x]['type'],
				}
				
				// Are we to set this value ?
				if('default' in props[x] && !pagelayer_empty(props[x]['default'])){
				
					var tmp_val =  props[x]['default'];
					
					// If there is a unit and there is no unit suffix in atts value
					if('units' in props[x]){
						if(jQuery.isNumeric(tmp_val)){
							tmp_val = tmp_val+props[x]['units'][0];
						}else{
							var sep = 'sep' in props[x] ? props[x]['sep'] : ',';
							var tmp2 = tmp_val.split(sep);
							for(var k in tmp2){
								if(jQuery.isNumeric(tmp2[k])){
									tmp2[k] = tmp2[k]+props[x]['units'][0];
								}
							}
							tmp_val = tmp2.join(sep);
						}
					}
						
					attributes[x]['default'] = tmp_val;
				}
				
				var modes = ['tablet', 'mobile'];
				
				// Do we have screen ?
				if('screen' in props[x] || props[x]['type'] == 'typography'){
					for(var m in modes){
						let prop_name = x +'_'+modes[m];
						
						attributes[prop_name] = new Object();
				
						attributes[prop_name] = {
							type: props[x]['type'],
						}
						
						// TODO: 
						//if('default' in props[prop_name]){
							//attributes[prop_name]['default'] =  props[x]['default'];
						//}
					}
				}
			}
		}
	}
	
	if(block == 'pl_contact'){
		attributes['con_post_id'] = {
			type: 'string'
		};
	}
	
	if(pl_props.innerHTML && pl_props.innerHTML in attributes ){
		attributes[pl_props.innerHTML]['source'] = 'html';
	}
  
	// Attr for id
	attributes['pagelayer-id'] = {
		type: 'string'
	};
	
	// TO save Tmp Atts
	attributes['tmpAtts'] = {
		type: 'object',
		default: {},
	};
	
	blockProps['supports'] = {
		'html' : false
	};
	
	var blockPropsWithAdditional = {
		title: pl_props['name'],
		apiVersion: 3,
		icon: icons,
		attributes: attributes,
		edit: function(props){
			return pagelayer_render_blocks(block, props, pl_props);
		},
		save: function(props){
			return createElement(SaveBlocks, {
				...props,
				pl_props: pl_props,
			});
		}
	};
	
	// Merge blockProps and blockPropsWithAdditional objects
	var finalBlockProps = { ...blockProps, ...blockPropsWithAdditional };
	
	wp.blocks.registerBlockType(block_name, finalBlockProps);
}

var pagelayerDomReadyInterval = {};

// Render Modal
wp.domReady( function(){
	var libraryRender = document.createElement("div")
	wp.element.render(<PagelayerLibrary/>, libraryRender);
	document.querySelector('body').appendChild(libraryRender);
	
	pagelayerDomReadyInterval = setInterval(function(){
		var contentEle = document.querySelector('.interface-interface-skeleton__content');
		if(!contentEle){
			return;
		}
		clearInterval(pagelayerDomReadyInterval);
		new WOW({boxClass:'pagelayer-wow', scrollContainer: '.interface-interface-skeleton__content'}).init();
		
		// Trigger window scroll on skeleton scroll to make compatibility with widgets scroll code 
		// This only for desktop mode
		jQuery(contentEle).on('scroll', function(){
			// Dispatch the event on the window
			window.dispatchEvent(new Event('scroll'));
		});
		
		// Trigger window scroll on skeleton scroll to make compatibility with widgets scroll code 
		// This only for desktop mode
		jQuery(contentEle).on('resize', function(){
			// Dispatch the event on the window
			window.dispatchEvent(new Event('resize'));
		});
			
	}, 100);
	
	// Prevent clicks 
	jQuery(document).on('click', '[pagelayer-id]', function(e){
		e.preventDefault(); // We also did this in the pagelayer editor
	});
	
	// Block mover handler for slider child
	jQuery(document).on('mousedown', '.block-editor-block-mover-button', function(e){
		
		const currentId = wp.data.select('core/block-editor').getSelectedBlockClientId();
		
		var selBlock = pagelayer_empty(currentId) ? pagelayer_query('.wp-block.is-multi-selected') : pagelayer_query('#block-'+currentId);
		
		var pEle = selBlock.parent('.pagelayer-owl-item').closest('.pagelayer-owl-carousel');
			
		if (pEle.length < 1) {
			return;
		}
		
		// Click triggered because mover was not clicked due to mover being hidden on mousedown 
		jQuery(this)[0].dispatchEvent(new Event('click',  { bubbles: true }));
		
		// Refresh slider
		pEle.trigger('refresh.pagelayer.owl');
		
		// For immediate destroy slider
		pagelayer_owl_destroy(pEle.closest('[pagelayer-id][pagelayer-setup]'), '.pagelayer-owl-carousel');
	});
});

// Render block on editor
function pagelayer_render_blocks(tag, elProps, pl_props){
	// TODO replace vars with html.
	
	// Make Tag name withot pl prefix
	var tagName = tag.replace('pl_', '');
	
	const { InspectorControls } = wp.blockEditor;
	const { TextControl } = wp.components;
	const { setAttributes, className, attributes, isSelected, clientId } = elProps;
	const deviceType = PreviewType().toLowerCase();
	const jEleRef = useRef(null);
	
	// Handle the CSS part
	// Get the id, tag, atts, data, etc
	const el = pagelayerData(elProps, true);
	
	useLayoutEffect(() => {
		// Check the pagelayer id is duplicat or set for first time !
		if( !('pagelayer-id' in el.atts) || el.atts['pagelayer-id'] !== el.id){
			
			// Prevent to recall this condition
			el.atts['pagelayer-id'] = el.id;
			
			// Save the ID
			setAttributes( { ['pagelayer-id']: el.id } );			
		}
	});
	
	el.props = elProps;
  
	var elCSS = {
		classes: [],
		remove_classes: [],
		attr: [],
		remove_attr: [],
		css: [],
		edit: [],
		cssSel: '.p-'+el.id,
		sel: '[pagelayer-id="'+el.id+'"]',
		wrap: '[pagelayer-wrap-id="'+el.id+'"]'
	};
	
	// Add Classes
	el.className = className+' p-'+el.id+' pagelayer-'+tagName;
	
	// Create a reference
	el.CSS = elCSS;
	
	// For storing the setting and options components
	var settings = [];
	var options = [];
	
	for(var i in pagelayer_tabs){
		var tab = pagelayer_tabs[i];
		
		// Skip settings for post_props block
		if(tab == 'settings' && tagName == 'post_props'){
			continue;
		}
		
		for(var section in pl_props[tab]){
			
			var props = section in pl_props ? pl_props[section] : pl_blocks_styles[section];
			//console.log(_props);
			
			// Store array of all components
			var controls = [];
			var groupControls = {};
			var ShowGroupControls = {};
			
			// Loop the props
			for(var x in props){
				
				var prop = props[x];
				
				// pagelayer_data will return attributes even if they are BLANK e.g. attr=""
				// Render doesnt consider BLANK values as values, and we are unsetting them now
				// If in any situation you need to consider blank values, please handle in the JS / PHP function of the Shortcode
				if(x in el.atts && pagelayerLength(el.atts[x]) < 1){
					delete el.atts[x];
				}
				
				// Skip creating props components
				if(prop['type'] == 'group'){
					continue;
				}
				
				// Load permalink values
				if(prop['type'] == 'link'){

					if('selector' in prop && typeof el.atts[x] == 'object'){
						var tmp = {};
						
						// Link is required for check IF and IF-EXT in html
						if(pagelayerLength(el.atts[x]['link'])  < 1){
							delete el.atts[x];
						}else{
							
							// TODO: remove attrs if target and rel not exists
							if( 'target' in el.atts[x] && !pagelayer_empty(el.atts[x]['target']) ){
								tmp = {'sel': prop['selector'], 'val': 'target="_blank"'};
								elCSS['attr'].push(tmp);
							}
							
							if( 'rel' in el.atts[x] && !pagelayer_empty(el.atts[x]['rel']) ){
								tmp = {'sel': prop['selector'], 'val': 'rel="nofollow"'};
								elCSS['attr'].push(tmp);
							}

							if( 'attrs' in el.atts[x] && !pagelayer_empty(el.atts[x]['attrs']) ){

								var attrsVal = pagelayerTrim(el.atts[x]['attrs'].split(';'));
				
								attrsVal.forEach(function(item, index){
					  
									var splitValue = item.split(/=(.*)/);
									var attKey = pagelayerTrim(splitValue[0]);
									var setAtt = '';
										
									// Validate the attrs name 
									if(attKey.length < 1 || pagelayer_empty(attKey.match(/^[a-z_]+[\w:.-]*$/i))){
										return;
									}
										
									if(splitValue.length < 2){
										setAtt = attKey+'=""';
									}else{
										setAtt = attKey+'="'+splitValue[1]+'"';
									}
										
									tmp = {'sel': prop['selector'], 'val': setAtt};
									elCSS['attr'].push(tmp);
								});
							}
							
							// TODO: do with temporary
							el.atts[x] = pagelayer_empty(el.atts[x]['url']) ? el.atts[x]['link'] : el.atts[x]['url'];
						}
					}
				}
				
				// Do we have a addClass ?
				// We are checking before the element has a value so that we can add or remove the class
				if('addClass' in prop){
					
					var addClasses;
					
					// Convert the string to an array
					if(typeof prop['addClass'] === 'string'){
						addClasses = [prop['addClass']];
					}else{
						addClasses = prop['addClass'];
					}
					
					for(var c in addClasses){
							
						// The selector
						var tSel = jQuery.isNumeric(c) ? '' : c;
						
						// If there is a VAL
						// NOTE : Only val is allowed when there is a list
						if(addClasses[c].match(/\{\{val\}\}/) && 'list' in prop){
							var pushCSS = (cl) => {
								var tmp = {'sel': tSel, 'val': addClasses[c].replace('{{val}}', cl)};
								
								if(el.atts[x] == cl){
									elCSS['classes'].push(tmp);
								}else{
									elCSS['remove_classes'].push(tmp);
								}
							}
							
							for(var l in prop['list']){
								
								if(typeof prop['list'][l] != 'object'){
									pushCSS(l);
									continue;
								}
								
								for(var deepList in prop['list'][l]){
									pushCSS(deepList);
								}
								
							}
							
						}else{
							
							var tmp = {'sel': tSel, 'val': addClasses[c].replace('{{val}}', el.atts[x])};
							
							// If the value is there
							if(x in el.atts){
								elCSS['classes'].push(tmp);
							}else{
								elCSS['remove_classes'].push(tmp);
							}
						
						}
					}
				}
				
				// Do we have a addAttr ? 
				// We are checking before the element has a value so that we can add or remove the attr
				// Also skip for id in editor
				if('addAttr' in prop && 'ele_id' != x){
					
					var addAttr;
					
					// Convert the string to an array
					if(typeof prop['addAttr'] === 'string'){
						addAttr = [prop['addAttr']];
					}else{
						addAttr = prop['addAttr'];
					}
					
					for(var c in addAttr){
							
						// The selector
						var tSel = jQuery.isNumeric(c) ? '' : c;
						var tmp = {'sel': tSel, 'val': addAttr[c]};
						
						// If the value is there
						if(x in el.atts){
							elCSS['attr'].push(tmp);
						}else{
							elCSS['remove_attr'].push(tmp);
						}
					}
				}
				
				// Do we have a CSS ? 
				if('css' in prop){
					
					var css;
	
					// Convert the string to an array
					if(typeof prop['css'] === 'string'){
						css = [prop['css']];
					}else{
						css = prop['css'];
					}
					
					// Screen modes
					var modes = {desktop: '', tablet: '_tablet', mobile: '_mobile'};
					var desk_global = (prop['type'] == 'typography') ? pagelayerIsGlobalTypo(el.atts[x]) : '';
					
					for(var m in modes){
						
						var xm = x+modes[m];
						
						// If the value is there
						if(!(xm in el.atts)){ //  && pagelayer_empty(desk_global)
							continue;
						}
						
						var xm_val = el.atts[xm];
						
						// If is global color
						if(prop['type'] == 'color'){
							xm_val = pagelayerParseColor(el.atts[xm]);
						}
						
						// If is global font
						if(prop['type'] == 'typography'){
							xm_val = pagelayerParseTypo(xm_val, false, desk_global, m);
						}
						
						// If there is global gradient color
						if(prop['type'] == 'gradient'){
							
							if(pagelayer_is_string(xm_val)){
								xm_val = xm_val.split(',');
							}
							
							for(let key in xm_val){								
								xm_val[key] = pagelayerParseColor(xm_val[key]);
							}
							
						}
						
						for(var c in css){
								
							// The selector
							var tSel = jQuery.isNumeric(c) ? '{{element}}' : c;
							var tmp = {
								sel: tSel, 
								val: pagelayerCssRender(css[c], xm_val, (prop.sep || ',')),
							};
							
							// Is this a tablet
							if(m == 'tablet'){
								tmp.sel = '@media (max-width: '+ pagelayer_settings['tablet_breakpoint'] +'px) and (min-width: '+ (pagelayer_settings['mobile_breakpoint'] +1) +'px){'+tmp.sel;
								tmp.val = tmp.val+'}';
							}
							
							// Is this a mobile mode ?
							if(m == 'mobile'){
								tmp.sel = '@media (max-width: '+ pagelayer_settings['mobile_breakpoint'] +'px){'+tmp.sel;
								tmp.val = tmp.val+'}';
							}
							
							// Push to store
							elCSS.css.push(tmp);
						}
					
					}
					
				}
				
				// Any editor ?
				if('edit' in prop){
					var editEle = {prop: x, sel: prop['edit']};
					
					// Keep the props and if condition
					if(!pagelayer_empty(prop['keep_prop'])){
						editEle['keep_prop'] = true;
					}
						
					elCSS.edit.push(editEle);
					
					if(pagelayer_empty(prop['keep_prop'])){
						continue;
					}
				}
				
				// Do we need to hide prop ?
				var toShow = true;
				
				// Now lets show or hide the element
				if('req' in prop || 'show' in prop){
					
					// List of considerations
					var show = {};
					
					// We have both req and show, so lets just combine the values and then show
					// NOTE : We need to make an array and not just merge the 2 as they are references
					if('req' in prop && 'show' in prop){
						
						// Add the req values
						show = JSON.parse(JSON.stringify(prop['req']));
						
						// Now the show values need to be looped
						for(var t in prop['show']){
							show[t] = prop['show'][t];
						}
						
					}else{
						show = 'req' in prop ? prop['req'] : prop['show'];
					}
					
					for(var showParam in show){
						var reqval = show[showParam];
						var except = showParam.substr(0, 1) == '!' ? true : false;
						showParam = except ? showParam.substr(1) : showParam;
						var val = el.atts[showParam] || '';
						
						//console.log('Show '+x+' '+showParam+' '+reqval+' '+val);
						
						// Is the value not the same, then we can show
						if(except){
							
							if(typeof reqval == 'string' && reqval == val){
								toShow = false;
								break;
							}
							
							// Its an array and a value is found, then dont show
							if(typeof reqval != 'string' && reqval.indexOf(val) > -1){
								toShow = false;
								break;
							}
							
						// The value must be equal
						}else{
							
							 if(typeof reqval == 'string' && reqval != val){
								toShow = false;
								break;
							 }
							
							// Its an array and no value is found, then dont show
							if(typeof reqval != 'string' && reqval.indexOf(val) === -1){
								toShow = false;
								break;
							}
						}
					}
				}

				// Show / Req Props
				if(!toShow || !isSelected){
					continue;
				}
        
				var modeAtts = x;
				
				// Response atts Handle
				if( 'screen' in prop){
					var modesType = {desktop: '', tablet: '_tablet', mobile: '_mobile'};
					modeAtts = x+modesType[deviceType];
				}
				
				// Start creating block components
				prop['c'] = {};
				prop['c']['name'] = modeAtts;
				prop['c']['val'] = el.atts[modeAtts];
				
				// TODo form pagelayerData function
				// Handle the amount
				if(pagelayerLength(prop.c['val']) < 1){
					prop.c['val'] = '';
				}
			
				// TODO: if props is selected then create components
				var controlEle = TextControl;
				var elAtts = { // TODO: merge with props
					prop: prop,
					label: prop['label'],
					value: prop['c']['val'],
					deviceType: deviceType,
					...elProps
				};
				
				// Render Props
				if(prop['type'] in wp.PagelayerComponents){
					// Pagelayer components
					controlEle = wp.PagelayerComponents[prop['type']];
				}else{
					elAtts['type'] = prop['type'];
					
					const _x = x;
					elAtts['onChange'] = function(value){
						setAttributes({ [_x]: value });
					}
				}
				
				if('show_group' in prop){
					
					// Push a place holder to reserve the Group position
					var cPosition = controls.push( 'Show Group' );
					var show_group = prop.show_group;
					
					ShowGroupControls[cPosition - 1] = [controlEle, elAtts, show_group];
					continue;
				}
				
				var controlEle = createElement(controlEle, elAtts);
				
				if('group' in prop){
					
					if( !(prop['group'] in groupControls) ){
						groupControls[prop['group']] = [];
					}
					
					groupControls[prop['group']].push(controlEle);
					continue;
				}
				
				controls.push( controlEle );
				
			}
			
			// Create Group element like access and modal props
			if(!pagelayer_empty(ShowGroupControls)){
				
				// Create Group components
				for(var pos in ShowGroupControls){
					
					var showGroup = ShowGroupControls[pos];
					
					var show_group = showGroup[2];
					controls[pos] = createElement(showGroup[0], showGroup[1], groupControls[show_group]);
				}
				
			}
			
			// There are no controls to show
			if(pagelayer_empty(controls)){
				continue;
			}
			
			var panel = createElement(wp.components.PanelBody, {
					title: pl_props[tab][section],
					className: 'pagelayer-panel__body',
					initialOpen: false
				},
				controls
			);
			
			if(tab == 'settings'){
				settings.push(panel);
				continue;
			}
			
			options.push(panel);
		}
	}
	
	// Create a Inspector Controls 
	var inspector = createElement(InspectorControls, {key: 'inspector1'},
		createElement(wp.PagelayerComponents['setting_tabs'], {} ),
		createElement('div', {'className': 'pagelayer-components-tabs pl-settings'},
			settings
		),
		createElement('div', {'className': 'pagelayer-components-tabs pl-options'},
			options
		)
	);
	
	// Is there a function to render ?
	var fn = BlockFunctions['pagelayer_render_'+tag];
	
	if(typeof fn == 'function'){
		fn(el);
	}
	
	// Generate HTML
	var htmlToRender = '';
	var plAtts = { 'pagelayer-id': el.id};
	var foundGroup = false;
	
	if(tagName in wp.PagelayerBlocks){
		var blockComponent = wp.PagelayerBlocks[tagName];
		
		// The Groups manage in react components
		foundGroup = true;
		
		htmlToRender = createElement(blockComponent, {
			_props: elProps,
			pl_props: pl_props,
			tag: tagName,
			deviceType: deviceType,
			jEleRef: jEleRef,
			data: el
		});
		
	}else{
		
		if('html' in pl_props){
			
			var html = pl_props['html'];
			
			// Replace image tags with placeholders
			var modifiedHTML = html.replace(/src=/g, 'plmodifysrc=');
			
			// Parse html
			var iHTML = jQuery('<div>'+modifiedHTML+'</div>');
			
			// If [if] condition is true we will still allow to show
			if(elCSS.edit.length > 0){
				for(var c in elCSS.edit){
					var prop = elCSS.edit[c]['prop'];
					
					// Keep the props and if condition
					if(elCSS.edit[c]['keep_prop']){
						continue;
					}
					
					// Remove [if] attribute for editable content
					iHTML.find('[if="{{'+prop+'}}"]').removeAttr('if');
				}
			}
			
			// Lets process the 'if-ext'
			iHTML.find('[if-ext]').each(function (){
				var $j = jQuery(this);
				var reqvar = pagelayerVar($j.attr('if-ext'));
				$j.removeAttr('if-ext');
				
				// Is the element there ?
				if(!(reqvar in el.atts && !pagelayer_empty(el.atts[reqvar]))){
					//console.log('HERE');
					$j[0].outerHTML = $j.html();
				}
				
			});
			
			// Lets process the 'if'
			iHTML.find('[if]').each(function (){
				var $j = jQuery(this);
				var reqvar = pagelayerVar($j.attr('if'));
				$j.removeAttr('if');
				
				// Is the element there ?
				if(!(reqvar in el.atts && !pagelayer_empty(el.atts[reqvar]))){
					//console.log('HERE');
					$j.remove();
				}
				
			});
			
			// Do we have to create children ?
			if('has_group' in pl_props && 'holder' in pl_props){
				iHTML.find(pl_props['holder']).attr('pagelayer-group-holder', tag);
				foundGroup = true;
			}
			
			// Add pagelayer-editable attribute to make element editable while generating react components
			if(elCSS.edit.length > 0){
				
				for(var c in elCSS.edit){
					var prop = elCSS.edit[c]['prop'];
					var tSel = elCSS.edit[c]['sel'];
					var node = tSel.length < 1 ? iHTML : iHTML.find(tSel);
					node.attr({'pagelayer-editable': prop});
				}
				
			}
			
			var innerHTML = iHTML[0].innerHTML.replace(/plmodifysrc=/g, 'src=');
				
			htmlToRender = pagelayerParseVars(innerHTML, el);
		};
		
		htmlToRender = pagelayerParseHtmlToReact(htmlToRender, elProps);
	}
	
	var style = '';
  
	// If we have any RULES CSS, then handle it
	if(elCSS.css.length > 0){
		
		// Make the rules
		var rules = [];
		
		// Loop
		for(var c in elCSS.css){
			var tSel = pagelayerParseElVars(elCSS.css[c]['sel'], el);
			var rule = elCSS.css[c]['val'];
			if(tSel.length > 0){
				rules.push(tSel+'{'+rule+'}');
			}else{
				rules.push(pagelayerParseElVars(rule, el));
			}
		}
		
		// CSS Selector overide
		if(!pagelayer_empty(pl_props['overide_css_selector'])){
			for(var r in rules){
				var overide_css_selector = pagelayerParseElVars(pl_props['overide_css_selector'], el);
				rules[r] = rules[r].split(el.CSS.cssSel).join(overide_css_selector);
				rules[r] = rules[r].split(el.CSS.wrap).join(overide_css_selector);
			}
		}
		
		// Set the style
		style = pagelayerParseVars(rules.join("\n"), el);
		//console.log(style);
	}
	
	if(!pagelayer_empty(style)){
		style = `<style>${style}</style>`;
	}
	
	const eleBlockProps = {
		className: el.className,
		ref: jEleRef,
		...plAtts
	};
	
	const simpleBlockProps = useBlockProps({
		'pagelayer-wrap-id': el.id,
	});
	
	var gEleProps = {
		plTag: tag,
		_props: elProps,
		ref: jEleRef,
		className: el.className,
		...plAtts
	};
	
	const RenderGroupBlock = wp.PagelayerBlocks['addGroupBlock'];
	
	var components = (
		<> 
			{ isSelected && 
				inspector
			}
			
			{ pl_props?.has_group && !foundGroup ? (
				<div {...simpleBlockProps} >
					{ pagelayerParseHtmlToReact(style, elProps) }
					<RenderGroupBlock {...gEleProps} />
				</div>
			) : (
				<div {...simpleBlockProps} >
					<div {...eleBlockProps} >
						{ pagelayerParseHtmlToReact(style, elProps) }
						{ htmlToRender }
					</div>
				</div>
			) }
			
		</>
	);
	
	useEffect(() => {

		const jEle = pagelayer_query(`.p-${el.id}`);
		
		////////////////////////////
		// Are there any addClass ?
		////////////////////////////

		// If we have any classes to add
		if (elCSS.classes.length > 0) {
			for (const c of elCSS.classes) {
				const tSel = c.sel.replace('{{element}}', '');
				const node = tSel.length < 1 ? jEle : jEle.find(tSel);
				if (!node.hasClass(c.val)) {
					node.addClass(c.val);
				}
			}
		}

		// If we have any classes to remove
		if (elCSS.remove_classes.length > 0) {
			for (const c of elCSS.remove_classes) {
				const tSel = c.sel.replace('{{element}}', '');
				const node = tSel.length < 1 ? jEle : jEle.find(tSel);
				if (node.hasClass(c.val)) {
					node.removeClass(c.val);
				}
			}
		}

		////////////////////////////
		// Are there any addAttr ?
		////////////////////////////

		// If we have any attributes to add
		if (elCSS.attr.length > 0) {
			for (const c of elCSS.attr) {
				const tSel = c.sel.replace('{{element}}', '');
				const node = tSel.length < 1 ? jEle : jEle.find(tSel);
				const att = c.val.split(/=(.*)/);
				att[1] = pagelayerParseVars(att[1], el);
				att[1] = pagelayerTrim(att[1], '"');

				// Is it the same value?
				if (!node.attr(att[0]) !== att[1]) {
					node.attr(att[0], att[1]);
				}
			}
		}

		// If we have any attributes to add
		if (elCSS.remove_attr.length > 0) {
			for(const c of elCSS.remove_attr){
				const tSel = c.sel.replace('{{element}}', '');
				const node = tSel.length < 1 ? jEle : jEle.find(tSel);
				const att = c.val.split('=');

				if (node.is('[' + att[0] + ']')) {
					node.removeAttr(att[0]);
				}
			}
		}
		
		el.$ = pagelayer_query(`.p-${el.id}`);
		
		// Is there a function to render at the end ?
		var end = BlockFunctions['pagelayer_render_end_'+tag];
		
		if(typeof end == 'function'){
			end(el);
		}
		
		// Is there any adding_attribute attribute then initialize in editor
		if(el.atts['ele_attributes']){
			pagelayer_add_attributes(el);
		}
		
		if('pagelayer_scrolling_effects' in window){
			
			var skeleton__content = jQuery('.interface-interface-skeleton__content');
			
			// Is there any scrolling effects then initialize in editor
			if(el.atts['ele_scrolling_effects']){
				
				var iframeWindow = jQuery('iframe[name="editor-canvas"]');
				var parentEle = skeleton__content;
				
				if (iframeWindow.length > 0) {
					var iframeDocument = iframeWindow[ 0 ]?.contentWindow?.document || iframeWindow[ 0 ]?.contentDocument;
					parentEle = jQuery(iframeDocument);
				}
				
				pagelayer_scrolling_effects(el.$, parentEle);
			}	
			
			// Is there any mouse effects then initialize in editor
			if(el.atts['ele_mouse_effects']){
				pagelayer_mouse_effects(el.$);
			}
			
			//console.log(el);
			if(pagelayer_empty(el.atts['ele_sticky_pos'])){return;}
			
			// Do sticky the elemen
			pagelayer_ele_sticky_handler(el.$, skeleton__content);
		}
    
	}, [elCSS, el.atts]); // Run this effect whenever elCSS changes
	
	useEffect( () => {
		addDynamicStyles();
	}, [] );
	
	// On remove slider items
	useLayoutEffect(() => {
		return (() => {
			const parEle = pagelayer_query(`#block-${clientId}`).parent('.pagelayer-owl-item').closest('.pagelayer-owl-carousel');
			
			if (parEle.length < 1) {
				return;
			}
			
			pagelayer_owl_destroy(parEle.closest('[pagelayer-id][pagelayer-setup]'), '.pagelayer-owl-carousel');
		});
	}, []);

	return components;
}

// Loads the Data
function pagelayerData(props, clean){
	
	var ret = new Object();
	
	const { attributes, name } = props
	
	// Get the data
	ret.id = pagelayerAssignId(attributes);
	ret.tag = pagelayerCacheBlockTags[name];
	
	// Parse the attributes
	ret.atts = {...attributes};
	ret.tmp = {} ; // todo create all tmp values here
  
	if(ret.atts.tmpAtts){
		ret.tmp = ret.atts.tmpAtts;
	}
	
	//console.log(ret.atts);
	//console.log(ret.tmp);
	
	clean = clean || false;
	
	// Remove values which have 'req'. NOTE : 'show' ones will be allowed
	if(clean){
		
		var tag	= ret.tag;
		
		// Anything to set ?
		//ret.set = {};
		
		// Function to clear any att data
		var pagelayer_delete_atts = function(x){
			delete ret.atts[x];
			delete ret.atts[x+'_tablet'];// Any tablet and mobile values as well
			delete ret.atts[x+'_mobile'];
			//delete ret.set[x];		
		}
		
		// All props
		var all_props = pl_blocks[tag];

		// Loop through all props
		for(var i in pagelayer_tabs){
			
			var tab = pagelayer_tabs[i];
			
			// section_loop1:
			for(var section in all_props[tab]){
				
				/* // Any section to skip by post type ?
				if(!pagelayer_empty(all_props['post_type_cats'])){					
					for(var post_type in all_props['post_type_cats']){
						if(pagelayer_post.post_type != post_type && jQuery.inArray(section, all_props['post_type_cats'][post_type]) > -1){
							continue section_loop1;
						}
					}
				} */
        
				var props = section in all_props ? all_props[section] : pl_blocks_styles[section];
				
				// In case of widgets its possible !
				if(pagelayer_empty(props)){
					continue;
				}
				
				for(var x in props){
					
					var prop = props[x];
				
					// Any prop to skip ?
					if(!pagelayer_empty(all_props['skip_props']) && jQuery.inArray(x, all_props['skip_props']) > -1){
						pagelayer_delete_atts(x);
						continue;
					}
					
					// TODO: create a tmp values for props
					/* if(prop['type'] == 'image' && x in ret.atts && !isNaN(ret.atts[x]) ){
						var attachmentId = ret.atts[x];
						
						// Load attachment details
						wp.media.query({ post__in: attachmentId }).more().then(() => {
							// Retrieve information about the attachment by its ID.
							const attachment = wp.media.attachment(attachmentId).attributes;
							ret.tmp[x+'-url'] = attachment.url;
							
							// Keep a list of all sizes
							for(var size in attachment.sizes){
								ret.tmp[x+'-'+size+'url']  =  attachment.sizes[size].url;
							}
						});
					}
					
					// Are we to set this value ?
					if(!(x in ret.atts) && 'default' in prop && !pagelayer_empty(prop['default'])){
				
						// We need to make sure its not a PRO value
						if(!('pro' in prop && pagelayer_empty(pagelayer_pro))){
							
							var tmp_val = prop['default'];
							
							// If there is a unit and there is no unit suffix in atts value
							if('units' in prop){
								if(jQuery.isNumeric(tmp_val)){
									tmp_val = tmp_val+prop['units'][0];
								}else{
									var sep = 'sep' in prop ? prop['sep'] : ',';
									var tmp2 = tmp_val.split(sep);
									for(var k in tmp2){
										if(jQuery.isNumeric(tmp2[k])){
											tmp2[k] = tmp2[k]+prop['units'][0];
										}
									}
									tmp_val = tmp2.join(sep);
								}
							}
							
							//console.log(x+' - '+tmp_val);
							ret.set[x] = tmp_val;
							
						}
					} */
					
					if(!('req' in prop)){
						continue;
					}
					
					//console.log('[pagelayer_data] Cleaning :'+x);
					
					// List of considerations
					var show = prop['req'];
					
					// We will hide by default
					var toShow = true;
					
					for(var showParam in show){
						var reqval = show[showParam];
						var except = showParam.substr(0, 1) == '!' ? true : false;
						showParam = except ? showParam.substr(1) : showParam;
						var val = ret.atts[showParam] || '';
						
						//console.log('Show '+x+' '+showParam+' '+reqval+' '+val);
						
						// Is the value not the same, then we can show
						if(except){
							
							if(typeof reqval == 'string' && reqval == val){
								toShow = false;
								break;
							}
							
							// Its an array and a value is found, then dont show
							if(typeof reqval != 'string' && reqval.indexOf(val) > -1){
								toShow = false;
								break;
							}
							
						// The value must be equal
						}else{
							
							 if(typeof reqval == 'string' && reqval != val){
								toShow = false;
								break;
							 }
							
							// Its an array and no value is found, then dont show
							if(typeof reqval != 'string' && reqval.indexOf(val) === -1){
								toShow = false;
								break;
							}
						}
						
					}
					
					// Are we to show ?
					if(!toShow){
						//console.log('Delete : '+x);
						pagelayer_delete_atts(x);
					}
				}
			}
		}
	}
	
	return ret;
};

// Adding Custom Attributes
var pagelayer_custom_attr = {};	
function pagelayer_add_attributes(el){

	// Remove all attributes first	  
	if(el.id in pagelayer_custom_attr){
		pagelayer_custom_attr[el.id].forEach(function(item, index){
			el.$.removeAttr(item);
		});  
	}
  
	pagelayer_custom_attr[el.id] = [];
  	
	// Then create attributes
	var val = pagelayerTrim(el.atts['ele_attributes'].split(';'));
			
	val.forEach(function(item, index){
		
		// Split from first equal only
		var splitValue = item.split(/=(.*)/);
		var attKey = pagelayerTrim(splitValue[0]);
		
		pagelayer_custom_attr[el.id].push(attKey);
		
		if(attKey.length < 1 || pagelayer_empty(attKey.match(/^[a-z_]+[\w:.-]*$/i))){
			return;
		}
		
		if(splitValue.length < 2){
			el.$.attr(attKey, '');
			return;
		}
		
		el.$.attr(attKey, splitValue[1]);
		
	});
}

// Removes {{}} from the variable name
function pagelayerVar(val){
	return val.substring(2, (val.length - 2));
}

// Replace the variables
function pagelayerParseElVars(str, el){
	
	str = str.split('{{element}}').join(el.CSS.cssSel);
	str = str.split('{{wrap}}').join(el.CSS.wrap);
	str = str.split('{{ele_id}}').join(el.id);
	
	return str;
}

// Replace the variables
function pagelayerParseVars(str, el){
	
	for(var x in el.tmp){
		str = str.split('{{{'+x+'}}}').join(el.tmp[x]);
	}
	
	for(var x in el.atts){
		str = str.split('{{'+x+'}}').join(el.atts[x]);
	}
	
	return str;
};

// Take care of the CSS
function pagelayerCssRender(css, val, seperator){
	//console.log('CSS '+css+' | '+val);
	
	// Seperator
	seperator = seperator || ',';
	
	var replaceCss = function(rule, value, toreplace){
		
		value = pagelayerHex8ToRGBA(value);
		
		// If value has css var then we remove units
		if(value.match(/var\(/)){
			var toreplace = toreplace.replace(/[-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
			toreplace  = new RegExp( toreplace+'?[^\\s|;]+', 'ig');
		}
		
		return rule.split(toreplace).join(value);
	}
	
	// Replace the val
	css = replaceCss(css, val, '{{val}}');
	
	// If there is an array
	if(css.match(/val\[\d/)){
		
		if(typeof val != 'object' || val === null){
			val = String(val).split(seperator);
		}
		
		for(var i in val){
			css = replaceCss(css, val[i], '{{val['+i+']}}');
		}
	}
	
	//console.log('Final CSS '+css);
	
	return css;
	
};

// Handle hexa to rgba and also remove alpha which is ff
function pagelayerHex8ToRGBA(val){
	
	val = String(val);
	
	// If opacity is ff then discard ff
	if(val.match(/^#([a-f0-9]{6})ff$/)){
		return val.substr(0,7);
	}
	
	// Lets handle the RGB+opacity
	if(val.match(/^#([a-f0-9]{8})$/)){
		var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(val);
		val = 'rgba('+parseInt(result[1], 16)+', '+parseInt(result[2], 16)+', '+parseInt(result[3], 16)+', '+(parseInt(result[4], 16)/255).toFixed(2)+')';
	}
	
	return val;
};

// Assign the jQuery object an ID
function pagelayerAssignId(attributes, newId){
	
	newId = newId || false;
	
	// Do you have the pagelayer id
	var id = attributes['pagelayer-id'];
	if(newId || !id || id.length < 1){
		id = pagelayerRandstr(3)+pagelayerRandInt(9999).toString();
		id = id.toLowerCase();
	}
	
	let ele = jQuery(`.p-${id}`, pagelayerGetDocumentElement());
	
	if(ele.length > 1){
		id = pagelayerAssignId(attributes, true);
	}
	
	return id;
	
}

function pagelayerRandInt(max){
	return Math.floor(Math.random() * Math.floor(max));
}