<?php

namespace ShortPixel\Model;

use ShortPixel\Model\Image\ImageModel as ImageModel;


if (! defined('ABSPATH')) {
	exit; // Exit if accessed directly.
}

use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;


class FrontImage
{
	protected $raw;
	protected $image_loaded = false;
	protected $is_parsable = false;
	protected $imageBase; // directory path of this image.

	protected $id; // HTML ID of image
	protected $alt;
	protected $src;  // original src of image
	protected $srcset; // orginal srcset of image
	protected $class;
	protected $width;
	protected $height;
	protected $style;
	protected $sizes;

	// Array of all other attributes.
	protected $attributes;

	// Parsed items of src /srcset / sizes
	protected $dataTags = array();

	public function __construct($raw_html)
	{
		$this->raw = $raw_html;
		$this->loadImageDom();
	}

	public function __get($attr)
	{
		if (property_exists($this, $attr) && ! is_null($attr)) {
			return $this->$attr;
		}
		return null;
	}

	public function __set($name, $value)
	{
		if (property_exists($this, $name) ) {

			$this->$name = $value;
		}

	}

	protected function loadImageDom()
	{
		if (function_exists("mb_convert_encoding")) {
			$this->raw = mb_encode_numericentity($this->raw, [0x80, 0x10FFFF, 0, ~0], 'UTF-8');
		}

		$dom = new \DOMDocument();
		libxml_use_internal_errors(true); // disable error emit from libxml

		$result = $dom->loadHTML($this->raw, LIBXML_NOWARNING);

		// HTML failed loading
		if (false === $result) {
			return false;
		}

		// Elements we support.  @todo for futuro 
		$parseable_elements = ['img', 'source'];

		$image = $dom->getElementsByTagName('img')->item(0);
		// $attributes = array();

		/* This can happen with mismatches, or extremely malformed HTML.
        In customer case, a javascript that did  for (i<imgDefer) --- </script> */
		if (! is_object($image)) {
			$source = $dom->getElementsByTagName('source')->item(0);
			if (null == $source) {
				$this->is_parsable = false;
				return false; // done. 			 
			}

			$image = $source;
		}

		foreach ($image->attributes as $attr) {
			// Skip is no value
			if (strlen($attr->nodeValue) == 0)
				continue;

			if (property_exists($this, $attr->nodeName)) {
				
				$this->{$attr->nodeName} = $attr->nodeValue;
			}

			$this->attributes[$attr->nodeName] = $attr->nodeValue;
		}

		// Seen in wild, skipping over data-srcset because 
		if (is_null($this->srcset) && isset($this->attributes['data-srcset']))
		{
			 $this->srcset = $this->attributes['data-srcset'];
		}

		//$src = $this->src; 
		//$src = preg_replace('/[^a-zA-Z],\//', '', $src)	;
		//$this->src = filter_var(stripslashes($this->src), FILTER_SANITIZE_URL);
		

		// Parse the directory path and other sources
		$result = $this->setupSource();


		if (true === $result)
			$this->image_loaded = true;
	}

	public function hasBackground()
	{
		if (! is_null($this->style) && strpos($this->style, 'background') !== false) {
			return true;
		}
		return false;
	}

	public function hasPreventClasses()
	{
		// no class, no prevent.
		if (is_null($this->class)) {
			return false;
		}

		$preventArray = apply_filters('shortpixel/front/preventclasses', array('sp-no-webp', 'rev-sildebg'));

		foreach ($preventArray as $classname) {
			if (false !== strpos($this->class, $classname)) {
				return true;
			}
		}

		return false;
	}

	public function hasSource()
	{
		if (is_null($this->src) && is_null($this->srcset)) {
			return false;
		}
		return true;
	}

	public function isParseable()
	{
		if (
			false === $this->hasPreventClasses() &&
			false === $this->hasBackground()  &&
			true === $this->hasSource() &&
			true === $this->image_loaded
		) {
			return true;
		}

		return false;
	}

	public function getImageData()
	{
		if (! is_null($this->srcset)) {
			$data = $this->getLazyData('srcset');
			$data = explode(',', $data); // srcset is multiple images, split.
				
		} else {
			$data = $this->getLazyData('src');
			$data = array($data);  // single item, wrap in array
		}

		$this->getLazyData('sizes'); // sets the sizes.

		return $data;
	}


	public function getImageBase()
	{
		if (! is_null($this->imageBase))
			return $this->imageBase->getPath();

		return null;
	}

	public function parseReplacement($args)
	{
		if (is_null($this->class)) {
			$this->class = '';
		}

		$this->class .= ' sp-no-webp';

		$output = "<picture>";

		if (isset($args['avif']) && count($args['avif']) > 0) {
			$output .= $this->buildSource($args['avif'], 'avif');
		}

		if (isset($args['webp']) && count($args['webp']) > 0) {
			$output .= $this->buildSource($args['webp'], 'webp');
		}

		$output .= $this->buildImage();

		$output .= "</picture>";

		return $output;
	}


	// Check if this image has a source to work from.
	protected function setupSource()
	{
		$src = null;

		if (! is_null($this->src)) {
			$src = $this->src;
		} elseif (! is_null($this->srcset)) {
			$parts = preg_split('/\s+/', trim($this->srcset));

			$image_url = $parts[0];
			$src = $image_url;
		}

		if (is_null($src)) {
			return false;
		}

		// Filter out extension that are not for us.
		if (false === $this->checkExtensionConvertable($src)) {
			return false;
		}

		$fs = \wpSPIO()->filesystem();
		$fileObj = $fs->getFile($src);
		$fileDir = $fileObj->getFileDir();
		$this->imageBase = $fileObj->getFileDir();

		return true;
		// If (! is_hnull $srcset)
		// Get first item from srcset ( remove the size ? , then feed it to FS, get directory from it.
	}

	/*** Check if the extension is something we want to check
	 * @param String The URL source of the image.
	 **/
	private function checkExtensionConvertable($source)
	{
		$extension = substr($source, strrpos($source, '.') + 1);
		if (in_array($extension, ImageModel::PROCESSABLE_EXTENSIONS)) {
			return true;
		}
		return false;
	}

	protected function buildSource($sources, $fileFormat)
	{

		$prefix = (isset($this->dataTags['srcset'])) ? $this->dataTags['srcset'] : $this->dataTags['src'];
		$srcset = implode(',', $sources);

		$sizeOutput = '';
		if (! is_null($this->sizes)) {
			$sizeOutput = $this->dataTags['sizes'] . 'sizes="' . $this->sizes . '"';
		}

		$output = '<source ' . $prefix . 'srcset="' . $srcset . '" ' . $sizeOutput . ' type="image/' . $fileFormat . '">';

		return $output;
	}

	public function buildImage()
	{
		$src = $this->src;
		$output = '<img src="' . $src . '" ';

		// Get this from set attributes on class.
		$attrs = array('id', 'height', 'width', 'srcset', 'sizes', 'class');
		foreach ($attrs as $attr) {
			if (! is_null($this->{$attr})) {
				$output .= $attr . '="' . \esc_attr($this->{$attr}) . '" ';
			}
		}

		// Always output alt tag, because it's important to screen readers and otherwise.
		$output .= 'alt="' . \esc_attr($this->alt) . '" ';

		// Left over attributes that should be harmless, ie extra image data or other custom tags.
		$leftAttrs = $this->getImageAttributes();
		foreach ($leftAttrs as $name => $value) {
			$output .= $name . '="' . \esc_attr($value) . '" ';
		}

		$output .= ' > '; // ending image.

		return $output;
	}

	protected function getImageAttributes()
	{

		$dontuse = array(
			'src',
			'data-src',
			'data-lazy-src',
			'srcset',
			'sizes'

		);
		$dontuse = array_merge($dontuse, array('id', 'alt', 'height', 'width', 'srcset', 'sizes', 'class'));

		$attributes = $this->attributes;

		$leftAttrs = array();
		foreach ($attributes as $name => $value) {
			if (! in_array($name, $dontuse)) {
				$leftAttrs[$name] = $value;
			}
		}

		return $leftAttrs;
	}

	protected function getLazyData($type)
	{
		$attributes = $this->attributes;
		$value = $prefix = false;

		if (isset($attributes['data-lazy-' . $type]) && strlen($attributes['data-lazy-' . $type]) > 0) {
			$value = $attributes['data-lazy-' . $type];
			$prefix = 'data-lazy-';
		} elseif (isset($attributes['data-' . $type]) && strlen($attributes['data-' . $type]) > 0) {
			$value = $attributes['data-' . $type];
			$prefix = 'data-';
		} elseif (isset($attributes[$type]) && strlen($attributes[$type]) > 0) {
			$value = $attributes[$type];
			$prefix = '';
		}

		$this->dataTags[$type] = $prefix;

		return $value;
	}
} // class FrontImage
