<?php

namespace DynamicOOOS\Sabberworm\CSS;

/**
 * Class OutputFormat
 *
 * @method OutputFormat setSemicolonAfterLastRule(bool $bSemicolonAfterLastRule) Set whether semicolons are added after
 *     last rule.
 */
class OutputFormat
{
    /**
     * Value format: `"` means double-quote, `'` means single-quote
     *
     * @var string
     */
    public $sStringQuotingType = '"';
    /**
     * Output RGB colors in hash notation if possible
     *
     * @var string
     */
    public $bRGBHashNotation = \true;
    /**
     * Declaration format
     *
     * Semicolon after the last rule of a declaration block can be omitted. To do that, set this false.
     *
     * @var bool
     */
    public $bSemicolonAfterLastRule = \true;
    /**
     * Spacing
     * Note that these strings are not sanity-checked: the value should only consist of whitespace
     * Any newline character will be indented according to the current level.
     * The triples (After, Before, Between) can be set using a wildcard (e.g. `$oFormat->set('Space*Rules', "\n");`)
     */
    public $sSpaceAfterRuleName = ' ';
    /**
     * @var string
     */
    public $sSpaceBeforeRules = '';
    /**
     * @var string
     */
    public $sSpaceAfterRules = '';
    /**
     * @var string
     */
    public $sSpaceBetweenRules = '';
    /**
     * @var string
     */
    public $sSpaceBeforeBlocks = '';
    /**
     * @var string
     */
    public $sSpaceAfterBlocks = '';
    /**
     * @var string
     */
    public $sSpaceBetweenBlocks = "\n";
    /**
     * Content injected in and around at-rule blocks.
     *
     * @var string
     */
    public $sBeforeAtRuleBlock = '';
    /**
     * @var string
     */
    public $sAfterAtRuleBlock = '';
    /**
     * This is what’s printed before and after the comma if a declaration block contains multiple selectors.
     *
     * @var string
     */
    public $sSpaceBeforeSelectorSeparator = '';
    /**
     * @var string
     */
    public $sSpaceAfterSelectorSeparator = ' ';
    /**
     * This is what’s printed after the comma of value lists
     *
     * @var string
     */
    public $sSpaceBeforeListArgumentSeparator = '';
    /**
     * @var string
     */
    public $sSpaceAfterListArgumentSeparator = '';
    /**
     * @var string
     */
    public $sSpaceBeforeOpeningBrace = ' ';
    /**
     * Content injected in and around declaration blocks.
     *
     * @var string
     */
    public $sBeforeDeclarationBlock = '';
    /**
     * @var string
     */
    public $sAfterDeclarationBlockSelectors = '';
    /**
     * @var string
     */
    public $sAfterDeclarationBlock = '';
    /**
     * Indentation character(s) per level. Only applicable if newlines are used in any of the spacing settings.
     *
     * @var string
     */
    public $sIndentation = "\t";
    /**
     * Output exceptions.
     *
     * @var bool
     */
    public $bIgnoreExceptions = \false;
    /**
     * Render comments for lists and RuleSets
     *
     * @var bool
     */
    public $bRenderComments = \false;
    /**
     * @var OutputFormatter|null
     */
    private $oFormatter = null;
    /**
     * @var OutputFormat|null
     */
    private $oNextLevelFormat = null;
    /**
     * @var int
     */
    private $iIndentationLevel = 0;
    public function __construct()
    {
    }
    /**
     * @param string $sName
     *
     * @return string|null
     */
    public function get($sName)
    {
        $aVarPrefixes = ['a', 's', 'm', 'b', 'f', 'o', 'c', 'i'];
        foreach ($aVarPrefixes as $sPrefix) {
            $sFieldName = $sPrefix . \ucfirst($sName);
            if (isset($this->{$sFieldName})) {
                return $this->{$sFieldName};
            }
        }
        return null;
    }
    /**
     * @param array<array-key, string>|string $aNames
     * @param mixed $mValue
     *
     * @return self|false
     */
    public function set($aNames, $mValue)
    {
        $aVarPrefixes = ['a', 's', 'm', 'b', 'f', 'o', 'c', 'i'];
        if (\is_string($aNames) && \strpos($aNames, '*') !== \false) {
            $aNames = [\str_replace('*', 'Before', $aNames), \str_replace('*', 'Between', $aNames), \str_replace('*', 'After', $aNames)];
        } elseif (!\is_array($aNames)) {
            $aNames = [$aNames];
        }
        foreach ($aVarPrefixes as $sPrefix) {
            $bDidReplace = \false;
            foreach ($aNames as $sName) {
                $sFieldName = $sPrefix . \ucfirst($sName);
                if (isset($this->{$sFieldName})) {
                    $this->{$sFieldName} = $mValue;
                    $bDidReplace = \true;
                }
            }
            if ($bDidReplace) {
                return $this;
            }
        }
        // Break the chain so the user knows this option is invalid
        return \false;
    }
    /**
     * @param string $sMethodName
     * @param array<array-key, mixed> $aArguments
     *
     * @return mixed
     *
     * @throws \Exception
     */
    public function __call($sMethodName, array $aArguments)
    {
        if (\strpos($sMethodName, 'set') === 0) {
            return $this->set(\substr($sMethodName, 3), $aArguments[0]);
        } elseif (\strpos($sMethodName, 'get') === 0) {
            return $this->get(\substr($sMethodName, 3));
        } elseif (\method_exists(OutputFormatter::class, $sMethodName)) {
            return \call_user_func_array([$this->getFormatter(), $sMethodName], $aArguments);
        } else {
            throw new \Exception('Unknown OutputFormat method called: ' . $sMethodName);
        }
    }
    /**
     * @param int $iNumber
     *
     * @return self
     */
    public function indentWithTabs($iNumber = 1)
    {
        return $this->setIndentation(\str_repeat("\t", $iNumber));
    }
    /**
     * @param int $iNumber
     *
     * @return self
     */
    public function indentWithSpaces($iNumber = 2)
    {
        return $this->setIndentation(\str_repeat(" ", $iNumber));
    }
    /**
     * @return OutputFormat
     */
    public function nextLevel()
    {
        if ($this->oNextLevelFormat === null) {
            $this->oNextLevelFormat = clone $this;
            $this->oNextLevelFormat->iIndentationLevel++;
            $this->oNextLevelFormat->oFormatter = null;
        }
        return $this->oNextLevelFormat;
    }
    /**
     * @return void
     */
    public function beLenient()
    {
        $this->bIgnoreExceptions = \true;
    }
    /**
     * @return OutputFormatter
     */
    public function getFormatter()
    {
        if ($this->oFormatter === null) {
            $this->oFormatter = new OutputFormatter($this);
        }
        return $this->oFormatter;
    }
    /**
     * @return int
     */
    public function level()
    {
        return $this->iIndentationLevel;
    }
    /**
     * Creates an instance of this class without any particular formatting settings.
     *
     * @return self
     */
    public static function create()
    {
        return new OutputFormat();
    }
    /**
     * Creates an instance of this class with a preset for compact formatting.
     *
     * @return self
     */
    public static function createCompact()
    {
        $format = self::create();
        $format->set('Space*Rules', "")->set('Space*Blocks', "")->setSpaceAfterRuleName('')->setSpaceBeforeOpeningBrace('')->setSpaceAfterSelectorSeparator('')->setRenderComments(\false);
        return $format;
    }
    /**
     * Creates an instance of this class with a preset for pretty formatting.
     *
     * @return self
     */
    public static function createPretty()
    {
        $format = self::create();
        $format->set('Space*Rules', "\n")->set('Space*Blocks', "\n")->setSpaceBetweenBlocks("\n\n")->set('SpaceAfterListArgumentSeparator', ['default' => '', ',' => ' '])->setRenderComments(\true);
        return $format;
    }
}
