<?php
/**
 * @package    Essentials for VirtueMart
 * @author     Abhishek Das <info@virtueplanet.com>
 * @copyright  Copyright (C) 2012-2024 VirtuePlanet Services LLP. All rights reserved.
 * @license    GNU General Public License version 2 or later; see LICENSE.txt
 * @link       https://www.virtueplanet.com
 */

defined('_JEXEC') or die;

class VMEssentialsParser extends JObject
{
	protected $doc;
	protected $app;
	protected $params;
	protected $buffer;
	protected $tab;
	protected $lineBreak;
	
	protected $head          = null;
	protected $body          = null;
	protected $processedHead = null;
	protected $processedBody = null;

	public function __construct()
	{
		$this->doc       = JFactory::getDocument();
		$this->app       = JFactory::getApplication();
		$this->buffer    = $this->app->getBody();
		$this->params    = $this->app->getParams('com_vmessentials');
		$this->tab       = $this->doc->_getTab();
		$this->lineBreak = $this->doc->_getLineEnd();
	}
	
	public function getRegex($type)
	{
		$type = strtolower($type);
		$regex = array();
		
		if (!in_array($type, array('addtocart', 'price', 'noscript', 'if', 'head', 'body')))
		{
			throw new Exception(__METHOD__ . '() failed. Invalid type ' . $type);
			return false;
		}
		
		switch ($type)
		{
			case 'addtocart':
				$cssClass = 'addtocart-area';
				$cssClass = preg_quote($cssClass, '/');
				
				$regex[0] = "<div\s+[^>]*?\s*class=[\"']?" . $cssClass . "[^\w]+\w*+[^\s>]?\s*+>(.*?)<form\b[^>]*+>(.*?)";
				$regex[1] = "<input\s+[^>]*?\s*name=[\"']virtuemart_product_id\[\][\"']\s+[^>]*?\s*value=\"(.*?)\"[^>]+[\s|\/]*>";
				$regex[2] = "(.*?)</form\s*>(.*?)</div\s*>";
				break;
			case 'price':
				$idPrefix = 'productPrice';
				$idPrefix = preg_quote($idPrefix, '/');
				
				$regex[0] = "<div\b [^>]*?\bid\s*+=\s*+ ([\'\"]?+) \b" . $idPrefix . "(.*?)\b(?(1)\\1)[^>]*+>";
				$regex[1] = "((?:[^<]++ | < (?!\/?div\b| !-- ) | <!--.*?--> | <div\b[^>]*+>  (?3) ";
				$regex[2] = "</div\s*>)*+ ) </div\s*>";
				break;
			case 'noscript':
				$regex[0] = '<noscript\b[^>]*+>(?><?[^<]*+)*?</noscript>';
				break;
			case 'if':
				$regex[0] = '<!--(?>-?[^-]*+)*?-->';
				break;
			case 'head':
				$regex[0] = '#<head\b[^>]*+>(?><?[^<]*+)*?</head>#i';
				break;
			case 'body':
				$regex[0] = '#<body\b[^>]*+>.*</body>#si';
				break;
		}
		
		return implode('', $regex);
	}
	
	public function getHead($noError = false)
	{
		if ($this->head === null)
		{
			$regex = $this->getRegex('head');
			
			if (preg_match($regex, $this->buffer, $matches) === false || empty($matches))
			{
				if ($noError)
				{
					$this->head = false;
				}
				else
				{
					throw new Exception('An error occured while trying to find the <head> tags in the HTML document. Make sure your template has <head> and </head>');
				}
			}
			else
			{
				$this->head = $matches[0];
			}
		}
		
		return $this->head;
	}
	
	public function getBody()
	{
		if ($this->body === null)
		{
			$regex = $this->getRegex('body');
			
			if (preg_match($regex, $this->buffer, $matches) === false || empty($matches))
			{
				throw new Exception('An error occurred while trying to find the <body> tags in the HTML document. Check your template for open and closing body tags.');
			}
			
			$this->body = $matches[0];
		}
		
		return $this->body;
	}
	
	public function getOriginalBuffer()
	{
		return $this->buffer;
	}
	
	public function getBuffer()
	{
		$buffer    = $this->getOriginalBuffer();
		$bodyRegex = $this->getRegex('body');
		
		if ($this->processedBody)
		{
			$buffer = preg_replace($bodyRegex, self::cleanReplacement($this->processedBody), $buffer, 1);
			
			if (!$buffer)
			{
				throw new Exception(__METHOD__ . '() failed. An error occured while trying to replace body buffer.');
			}
		}
		
		return $buffer;
	}
	
	public function addButtons()
	{
		$autoPos  = $this->params->get('auto_buttons_position', 2);
		$type     = ($autoPos == 1 || $autoPos == 2) ? 'addtocart' : 'price';
		$position = ($autoPos == 1 || $autoPos == 3) ? 'before' : 'after';
		
		//vpdump($this->getRegex('addtocart'));exit;
		$regex = "#" . $this->getRegex($type) . "#six";

		$options = array(
			'type' => $type,
			'position' => $position
		);

		// Parse body
		$this->parseAndReplace($regex, $options);
		
		$buffer = $this->getBuffer();
		
		if (!empty($buffer))
		{
			$this->app->setBody($buffer);
		}
	}
	
	protected function parseAndReplace($regex, $options = array())
	{
		$html = $this->getBody();
		
		if (empty($html))
		{
			throw new Exception(__METHOD__ . '() failed. No html found.');
			return false;
		}
		
		$that = $this;
		
		$processedHtml = preg_replace_callback($regex, function($matches) use($that, $options) {
			return $that->replace($matches, $options);
		}, $html);
		
		if (!$processedHtml)
		{
			throw new Exception(__METHOD__ . '() failed. Parsing error.');
			return false;
		}
		
		$this->processedBody = $processedHtml;
	}
	
	protected function replace(&$matches, $options = array())
	{
		$type       = !empty($options['type']) ? $options['type'] : 'addtocart';
		$position   = !empty($options['position']) ? $options['position'] : 'after';
		$product_id = 0;
		
		if ($type == 'addtocart')
		{
			$element = $matches[0];
			
			if (!empty($matches[3]))
			{
				$product_id = (int) $matches[3];
			}
			
			// Recheck the element
			if (strpos($element, 'com_virtuemart') === false || strpos($element, 'cart') === false)
			{
				return $element;
			}
		}
		else
		{
			$element = $matches[0];
			
			if (!empty($matches[2]))
			{
				$product_id = (int) $matches[2];
			}
		}

		if (empty($product_id))
		{
			return $element;
		}
		
		$buttons = JHtml::_('vmessentials.allbuttons', $product_id);
		
		if (!empty($buttons))
		{
			if ($position == 'before')
			{
				$element = $buttons . $element;
			}
			else
			{
				$element .= $buttons;
			}
		}
		
		return $element;
	}
	
	protected static function cleanReplacement($string)
	{
		return strtr($string, array('\\' => '\\\\', '$' => '\$'));
	}
}