<?php

namespace XF\BbCode;

class RuleSet
{
	protected $tags = [];

	protected $context = null;
	protected $subContext = null;

	public function __construct($context, $subContext = null, $addDefault = true)
	{
		$this->context = $context;
		$this->subContext = $subContext;

		if ($addDefault)
		{
			$this->addDefaultTags();
		}
	}

	public function getContext()
	{
		return $this->context;
	}

	public function getSubContext()
	{
		return $this->subContext;
	}

	public function addDefaultTags()
	{
		$this->addTag('b', ['hasOption' => false]);
		$this->addTag('i', ['hasOption' => false]);
		$this->addTag('u', ['hasOption' => false]);
		$this->addTag('s', ['hasOption' => false]);

		$this->addTag('color', [
			'hasOption' => true,
			'optionMatch' => '/^(rgb\(\s*\d+%?\s*,\s*\d+%?\s*,\s*\d+%?\s*\)|#[a-f0-9]{6}|#[a-f0-9]{3}|[a-z]+)$/i'
		]);

		$this->addTag('font', [
			'hasOption' => true,
			'optionMatch' => '/^[a-z0-9 \-]+$/i' // regex matched to HTML->BB code regex
		]);

		$this->addTag('size', [
			'hasOption' => true,
			'optionMatch' => '/^[0-9]+(px)?$/i'
		]);

		$this->addTag('url', [
			'parseValidate' => [$this, 'parsePlainNoOption'],
			'stopAutoLink' => true
		]);
		$this->addTag('email', [
			'parseValidate' => [$this, 'parsePlainNoOption'],
			'stopAutoLink' => true
		]);

		$this->addTag('left', [
			'hasOption' => false
		]);
		$this->addTag('center', [
			'hasOption' => false
		]);
		$this->addTag('right', [
			'hasOption' => false
		]);

		$this->addTag('indent', [
			'optionMatch' => '/^[0-9]+$/'
		]);

		$this->addTag('img', [
			'hasOption' => false,
			'plain' => true,
			'stopSmilies' => true,
			'stopAutoLink' => true
		]);

		$this->addTag('quote', []);

		$this->addTag('code', [
			'parseValidate' => [$this, 'parseValidateCode'],
			'stopSmilies' => true,
			'stopAutoLink' => true
		]);

		$this->addTag('icode', [
			'parseValidate' => [$this, 'parseValidateCode'],
			'stopSmilies' => true,
			'stopAutoLink' => true
		]);

		$this->addTag('php', [
			'hasOption' => false,
			'plain' => true,
			'stopSmilies' => true,
			'stopAutoLink' => true
		]);

		$this->addTag('html', [
			'hasOption' => false,
			'plain' => true,
			'stopSmilies' => true,
			'stopAutoLink' => true
		]);

		$this->addTag('list', []);

		$this->addTag('plain', [
			'hasOption' => false,
			'plain' => true,
			'stopSmilies' => true,
			'stopAutoLink' => true
		]);

		$this->addTag('media', [
			'hasOption' => true,
			'plain' => true,
			'stopSmilies' => true,
			'stopAutoLink' => true
		]);

		$this->addTag('spoiler', []);

		$this->addTag('attach', [
			'plain' => true,
			'stopSmilies' => true,
			'stopAutoLink' => true
		]);

		$this->addTag('user', [
			'hasOption' => true,
			'plain' => true,
			'stopSmilies' => true,
			'stopAutoLink' => true
		]);
	}

	public function addTag($tag, array $config)
	{
		$this->tags[$tag] = $config;
	}

	public function modifyTag($tag, array $modification)
	{
		if (isset($this->tags[$tag]))
		{
			$this->tags[$tag] = array_merge($this->tags[$tag], $modification);
		}
	}

	public function removeTag($tag)
	{
		unset($this->tags[$tag]);
	}

	public function getTag($tag)
	{
		return isset($this->tags[$tag]) ? $this->tags[$tag] : null;
	}

	public function getCustomTagConfig(array $tag)
	{
		$output = [];

		if ($tag['has_option'] == 'yes')
		{
			$output['hasOption'] = true;
		}
		else if ($tag['has_option'] == 'no')
		{
			$output['hasOption'] = false;
		}

		if (strlen($tag['option_regex']))
		{
			$output['optionMatch'] = $tag['option_regex'];
		}

		if ($tag['disable_smilies'])
		{
			$output['stopSmilies'] = true;
		}

		if ($tag['disable_autolink'] || $tag['plain_children'])
		{
			$output['stopAutoLink'] = true;
		}

		if ($tag['plain_children'])
		{
			$output['plain'] = true;
		}

		return $output;
	}

	public function validateTag($tag, $option = null, &$parsingModifiers = [])
	{
		$parsingModifiers = [];

		$definition = $this->getTag($tag);
		if (!is_array($definition))
		{
			return false;
		}

		if (isset($definition['hasOption']))
		{
			if (
				(!$definition['hasOption'] && $option !== null)
				|| ($definition['hasOption'] && $option === null)
			)
			{
				return false;
			}
		}

		if ($option !== null && !empty($definition['optionMatch']))
		{
			if (!preg_match($definition['optionMatch'], $option))
			{
				return false;
			}
		}

		$parsingModifiers = $definition;

		if (isset($definition['parseValidate']))
		{
			$validated = call_user_func($definition['parseValidate'], $tag, $option);
			if ($validated === false)
			{
				return false;
			}

			if (is_array($validated))
			{
				$parsingModifiers = array_merge($parsingModifiers, $validated);
			}
		}

		return true;
	}

	public function parsePlainNoOption($tag, $option)
	{
		if ($option === null)
		{
			return ['plain' => true];
		}

		return true;
	}

	public function parseValidateCode($tag, $option)
	{
		if ($option && strtolower($option) == 'rich')
		{
			return true;
		}

		return ['plain' => true];
	}
}