<?php
/**
 * Greyd Animation render callback.
 * 
 * @since 1.6.0
 */
namespace greyd\animations;

use \greyd\blocks\helper as Helper;
use \greyd\blocks\render as Render;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( function_exists( 'add_filter' ) ) {
	add_filter( 'render_block', 'greyd\animations\render_block_with_greydAnim', 80, 2 );
	add_filter( 'render_block', 'greyd\animations\render_block_with_greydBackgroundAnim', 80, 2 );
}

/**
 * Render blocks with attribute 'greydAnim'
 * @see https://developer.wordpress.org/reference/hooks/render_block/
 * 
 * @param string $block_content     The block content about to be appended.
 * @param array  $block             The full block, including name and attributes.
 * 
 * @return string $block_content    altered Block Content
 */
function render_block_with_greydAnim( $block_content, $block ) {

	// early escape
	if (
		! $block
		|| ! isset( $block["attrs"] )
		|| ! is_array( $block["attrs"] )
		|| ! isset( $block["attrs"]["greydAnim"] )
		|| empty( $block["attrs"]["greydAnim"] )
	) {
		return $block_content;
	}
	
	$anim = get_animation( $block["attrs"] );
	// debug( $anim );

	if ( !empty($anim['data']) ) {
		$html_attributes = Helper::implode_html_attributes( array( 'data' => $anim['data'] ) );
		$block_content = preg_replace(
			'/(<[^<]+?)( [\w-]+="[^"]+")?( ?\/?>)/',
			'$1$2 '.$html_attributes.'$3',
			$block_content,
			1 // only the first occurence
		);
	}

	if ( !empty($anim['styles']) ) {
		foreach( $anim['styles'] as $selector => $styles ) {
			// we enqueue it using the render class, to not have
			// styles duplicated on the page.
			Render::enqueue_custom_style( $selector, $styles );
		}
	}

	return $block_content;
}

/**
 * Render blocks with attribute 'greydBackgroundAnim'
 * @see https://developer.wordpress.org/reference/hooks/render_block/
 * 
 * @param string $block_content     The block content about to be appended.
 * @param array  $block             The full block, including name and attributes.
 * 
 * @return string $block_content    altered Block Content
 */
function render_block_with_greydBackgroundAnim( $block_content, $block ) {

	// early escape
	if (
		! $block
		|| ! isset( $block["attrs"] )
		|| ! is_array( $block["attrs"] )
		|| ! isset( $block["attrs"]["greydBackgroundAnim"] )
		|| empty( $block["attrs"]["greydBackgroundAnim"] )
	) {
		return $block_content;
	}
	
	$anim = get_animation( $block["attrs"], 'greydBackgroundAnim' );
	// debug( $anim );

	if ( !empty($anim['data']) ) {
		$html_attributes = Helper::implode_html_attributes( array( 'data' => $anim['data'] ) );

		if ( strpos( $block_content, 'greyd-background' ) !== false ) {
			$block_content = preg_replace(
				'/(<div [^>]+?)(class=[\"\']greyd-background)/',
				'$1 '.$html_attributes.' $2',
				$block_content,
				1 // only the first occurence
			);
		}
		else if ( $block['blockName'] === 'core/cover' ) {
			
			$block_content = preg_replace(
				'/(<(?:img|video)[^>]+?)(class="[^"]*?wp-block-cover__(?:image|video)-background)/',
				'$1 '.$html_attributes.' $2',
				$block_content,
				1 // only the first occurence
			);
			
		}
	}

	if ( !empty($anim['styles']) ) {
		foreach( $anim['styles'] as $selector => $styles ) {
			// we enqueue it using the render class, to not have
			// styles duplicated on the page.
			Render::enqueue_custom_style( $selector, $styles );
		}
	}

	return $block_content;
}

/**
 * Get the animation attributes.
 * @param  array $block_atts   The block attributes.
 * @param  string $attribute   The attribute name to look for.
 * 
 * @return array @example:
 *      array(
 *          'data' => array( 'anim-action' => 'hide' ),
 *          'css' => '.gs_1234 { opacity: 1; }'
 *      )
 */
function get_animation( $block_atts, $attribute='greydAnim' ) {

	$anim   = $block_atts[ $attribute ];
	$data   = array(
		'anim-action'    => $anim['action'],
		'anim-event'     => $anim['event'],
		'anim-triggered' => 'false'
	);

	// action
	switch ( $anim['action'] ) {

		case 'hide':
		case 'show':
			if ( ! empty( $anim['preset'] ) ) {
				$data['anim-action'] = $anim['preset'];
			}
			$data = array_merge(
				$data,
				get_animation_preset_data( $data['anim-action'] )
			);
			break;

		case 'changeColor':

			$data['anim-from'] = '';
			$data['anim-to']   = '';
			$color      = isset( $anim['color'] ) ? $anim['color'] : '';
			$background = isset( $anim['background'] ) ? $anim['background'] : '';
			if ( !empty( $color ) ) {
				$data['anim-to']   .= 'color: ' . $color . ' !important;';
			}
			if ( !empty( $background ) ) {
				$data['anim-to']   .= 'background-color: ' . $background . ' !important;';
			}

			break;

		case 'translateX':
		case 'translateY':
		case 'scale':
		case 'rotate':
			$unit = $anim['action'] == 'rotate' ? 'deg' : '';
			$data['anim-from'] = 'transform: ' . $anim['action'] . '(' . $anim['from'] . $unit . ');';
			$data['anim-to']   = 'transform: ' . $anim['action'] . '(' . $anim['to'] . $unit . ');';
			break;

		case 'filter':
			$unit = $anim['preset'] == 'blur' ? 'px' : '%';
			$data['anim-from'] = 'filter: ' . $anim['preset'] . '(' . $anim['from'] . $unit . ');';
			$data['anim-to']   = 'filter: ' . $anim['preset'] . '(' . $anim['to'] . $unit . ');';
			break;

		case 'custom':
			$data['anim-from'] = $anim['from'];
			$data['anim-to']   = $anim['to'];
			break;
	}
	
	// selector
	$selector = '[data-anim-action]';


	// for background animations, we need to add a unique selector,
	// because the background element is not the block itself.
	if ( $attribute == 'greydBackgroundAnim' ) {
		$data[ 'anim-id' ] = wp_unique_id( 'anim-' );
		$selector .= '[data-anim-id="' . $data[ 'anim-id' ] . '"]';
	}
	else {
		// if the block has a custom class, we use that as selector
		if ( isset( $block_atts["greydClass"] ) && !empty( $block_atts["greydClass"] ) ) {
			$selector .= '.' . $block_atts["greydClass"];
		}
		// if the block has no custom class, we use a unique selector
		else {
			$data[ 'anim-id' ] = wp_unique_id( 'anim-' );
			$selector .= '[data-anim-id="' . $data[ 'anim-id' ] . '"]';
		}
	}
	$selector_active = $selector . '[data-anim-triggered="true"]';

	// event
	switch ( $anim['event'] ) {

		case 'hover':
			// for background animations, we actually trigger the animation when
			// the direct parent is hovered.
			if ( $attribute == 'greydBackgroundAnim' ) {
				$selector_active = ':is(:hover, :focus-visible) > ' . $selector;
			}
			else {
				$selector_active = $selector . ':is(:hover, :focus-visible)';
			}
			break;

		case 'click':
			if ( $attribute == 'greydBackgroundAnim' ) {
				// for background animations, we actually trigger the animation when
				// the direct parent is clicked. So we need to make sure the parent
				// has a selector, that the JS can use.
				if ( ! isset( $block_atts["greydClass"] ) || empty( $block_atts["greydClass"] ) ) {
					$block_atts["greydClass"] = Helper::generate_greydClass();
				}
				$data['anim-event']  = 'parentClick';
				$data['anim-parent'] = '.' . $block_atts["greydClass"];
			}
			// everything else about click animations is handled by JS
			break;

		case 'parentHover':
			$data['anim-parent'] = $anim['parent'];
			$selector_active = $anim['parent'] . ':is(:hover, :focus-visible) ' . $selector;
			break;
			
		case 'parentClick':
			$data['anim-parent'] = $anim['parent'];
			// everything else about click animations is handled by JS
			break;

		case 'onScroll':
			$data['anim-start'] = isset($anim['start']) ? esc_attr( $anim['start'] ) : 50;
			$data['anim-reverse'] = isset($anim['reverse']) && $anim['reverse'] ? "true" : "false";
			break;

		case 'whileScroll':
			$data['anim-start'] = isset($anim['start']) ? intval($anim['start']) : 100;
			$data['anim-end']   = isset($anim['end']) ? intval($anim['end']) : 0;
			break;

		case 'idle':
			/** @todo idle */
			break;

		case 'isSticky':
			// silence is golden
			break;

		case 'parentSticky':
			$selector_active = '[data-anim-event="isSticky"][data-anim-triggered="true"] ' . $selector;
			break;
	}

	// styles
	$css        = $data['anim-from'];
	$css_active = $data['anim-to'];

	// whilescroll
	if ( $anim['event'] == 'whileScroll' ) {
		$css_active = '';
	}
	// all other
	else {

		// presets do not need extra css
		if ( $anim['action'] == 'hide' && !empty($anim['preset']) && $anim['event'] != 'parentHover' ) {
			$css = $css_active = '';
		} else if ( $anim['action'] == 'show' && !empty($anim['preset']) && $anim['event'] != 'parentHover' ) {
			$css = $css_active = '';
		}

		// transition properties
		if ( $anim['event'] == 'onScroll' && $data['anim-reverse'] === "false" ) {
			// $css .= 'transition-duration: 0s; transition-delay: 0s;';
			if ( $anim['duration'] != 200 ) {
				$css_active .= 'transition-duration: ' . $anim['duration'] . 'ms;';
			}
			if ( $anim['delay'] != 0 ) {
				$css_active .= 'transition-delay: ' . $anim['delay'] . 'ms;';
			}
			if ( $anim['timing'] != 'ease' ) {
				$css_active .= 'transition-timing-function: ' . $anim['timing'] . ';';
			}
		}
		else if ( $anim['event'] != 'whileScroll' ) {
			if ( $anim['duration'] != 200 ) {
				$css .= 'transition-duration: ' . $anim['duration'] . 'ms;';
			}
			if ( $anim['delay'] != 0 ) {
				$css .= 'transition-delay: ' . $anim['delay'] . 'ms;';
			}
			if ( $anim['timing'] != 'ease' ) {
				$css .= 'transition-timing-function: ' . $anim['timing'] . ';';
			}
		}
	}
	
	// build css
	$styles = array();
	if ( !empty( $css ) ) {
		$styles[ $selector ] = $css;
	}
	if ( !empty( $css_active ) ) {
		$styles[ $selector_active ] = $css_active;
	}

	return array(
		'data' => $data,
		'styles' => $styles
	);
}


/**
 * Get animation preset data.
 * @param string $preset_name
 * @return array @example array( 'anim-from' => 'opacity: 0;', 'anim-to' => 'opacity: 1;' )
 */
function get_animation_preset_data( $preset_name ) {
	$presets = array(
		'show' => array(
			'anim-from' => 'opacity: 0;',
			'anim-to' => 'opacity: 1;',
		),
		'fadeIn' => array(
			'anim-from' => 'opacity: 0;',
			'anim-to' => 'opacity: 1;',
		),
		'fadeInUp' => array(
			'anim-from' => 'opacity: 0; transform: translateY(100px);',
			'anim-to' => 'opacity: 1; transform: translateY(0px);',
		),
		'fadeInDown' => array(
			'anim-from' => 'opacity: 0; transform: translateY(-100px);',
			'anim-to' => 'opacity: 1; transform: translateY(0px);',
		),
		'fadeInRight' => array(
			'anim-from' => 'opacity: 0; transform: translateX(100px);',
			'anim-to' => 'opacity: 1; transform: translateX(0px);',
		),
		'fadeInLeft' => array(
			'anim-from' => 'opacity: 0; transform: translateX(-100px);',
			'anim-to' => 'opacity: 1; transform: translateX(0px);',
		),
		'hide' => array(
			'anim-from' => 'opacity: 1;',
			'anim-to' => 'opacity: 0;',
		),
		'fadeOut' => array(
			'anim-from' => 'opacity: 1;',
			'anim-to' => 'opacity: 0;',
		),
		'fadeOutUp' => array(
			'anim-from' => 'opacity: 1; transform: translateY(0px);',
			'anim-to' => 'opacity: 0; transform: translateY(-100px);',
		),
		'fadeOutDown' => array(
			'anim-from' => 'opacity: 1; transform: translateY(0px);',
			'anim-to' => 'opacity: 0; transform: translateY(100px);',
		),
		'fadeOutRight' => array(
			'anim-from' => 'opacity: 1; transform: translateX(0px);',
			'anim-to' => 'opacity: 0; transform: translateX(100px);',
		),
		'fadeOutLeft' => array(
			'anim-from' => 'opacity: 1; transform: translateX(0px);',
			'anim-to' => 'opacity: 0; transform: translateX(-100px);',
		),
	);
	return isset( $presets[ $preset_name ] ) ? $presets[ $preset_name ] : null;
}
