<?php

namespace PhpOffice\PhpSpreadsheet\Reader\Ods;

use DOMDocument;
use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use stdClass;

class PageSettings
{
    private string $officeNs = '';

    private string $stylesNs = '';

    private string $stylesFo = '';

    private string $tableNs = '';

    /**
     * @var string[]
     */
    private array $tableStylesCrossReference = [];

    /** @var mixed[] */
    private array $pageLayoutStyles = [];

    /**
     * @var string[]
     */
    private array $masterStylesCrossReference = [];

    /**
     * @var string[]
     */
    private array $masterPrintStylesCrossReference = [];

    public function __construct(DOMDocument $styleDom)
    {
        $this->setDomNameSpaces($styleDom);
        $this->readPageSettingStyles($styleDom);
        $this->readStyleMasterLookup($styleDom);
    }

    private function setDomNameSpaces(DOMDocument $styleDom): void
    {
        $this->officeNs = (string) $styleDom->lookupNamespaceUri('office');
        $this->stylesNs = (string) $styleDom->lookupNamespaceUri('style');
        $this->stylesFo = (string) $styleDom->lookupNamespaceUri('fo');
        $this->tableNs = (string) $styleDom->lookupNamespaceUri('table');
    }

    private function readPageSettingStyles(DOMDocument $styleDom): void
    {
        $item0 = $styleDom->getElementsByTagNameNS($this->officeNs, 'automatic-styles')->item(0);
        $styles = ($item0 === null) ? [] : $item0->getElementsByTagNameNS($this->stylesNs, 'page-layout');

        foreach ($styles as $styleSet) {
            $styleName = $styleSet->getAttributeNS($this->stylesNs, 'name');
            $pageLayoutProperties = $styleSet->getElementsByTagNameNS($this->stylesNs, 'page-layout-properties')->item(0);
            $styleOrientation = $pageLayoutProperties?->getAttributeNS($this->stylesNs, 'print-orientation');
            $styleScale = $pageLayoutProperties?->getAttributeNS($this->stylesNs, 'scale-to');
            $stylePrintOrder = $pageLayoutProperties?->getAttributeNS($this->stylesNs, 'print-page-order');
            $centered = $pageLayoutProperties?->getAttributeNS($this->stylesNs, 'table-centering');

            $marginLeft = $pageLayoutProperties?->getAttributeNS($this->stylesFo, 'margin-left');
            $marginRight = $pageLayoutProperties?->getAttributeNS($this->stylesFo, 'margin-right');
            $marginTop = $pageLayoutProperties?->getAttributeNS($this->stylesFo, 'margin-top');
            $marginBottom = $pageLayoutProperties?->getAttributeNS($this->stylesFo, 'margin-bottom');
            $header = $styleSet->getElementsByTagNameNS($this->stylesNs, 'header-style')->item(0);
            $headerProperties = $header?->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties')?->item(0);
            $marginHeader = $headerProperties?->getAttributeNS($this->stylesFo, 'min-height');
            $footer = $styleSet->getElementsByTagNameNS($this->stylesNs, 'footer-style')->item(0);
            $footerProperties = $footer?->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties')?->item(0);
            $marginFooter = $footerProperties?->getAttributeNS($this->stylesFo, 'min-height');

            $this->pageLayoutStyles[$styleName] = (object) [
                'orientation' => $styleOrientation ?: PageSetup::ORIENTATION_DEFAULT,
                'scale' => $styleScale ?: 100,
                'printOrder' => $stylePrintOrder,
                'horizontalCentered' => $centered === 'horizontal' || $centered === 'both',
                'verticalCentered' => $centered === 'vertical' || $centered === 'both',
                // margin size is already stored in inches, so no UOM conversion is required
                'marginLeft' => (float) ($marginLeft ?? 0.7),
                'marginRight' => (float) ($marginRight ?? 0.7),
                'marginTop' => (float) ($marginTop ?? 0.3),
                'marginBottom' => (float) ($marginBottom ?? 0.3),
                'marginHeader' => (float) ($marginHeader ?? 0.45),
                'marginFooter' => (float) ($marginFooter ?? 0.45),
            ];
        }
    }

    private function readStyleMasterLookup(DOMDocument $styleDom): void
    {
        $item0 = $styleDom->getElementsByTagNameNS($this->officeNs, 'master-styles')->item(0);
        $styleMasterLookup = ($item0 === null) ? [] : $item0->getElementsByTagNameNS($this->stylesNs, 'master-page');

        foreach ($styleMasterLookup as $styleMasterSet) {
            $styleMasterName = $styleMasterSet->getAttributeNS($this->stylesNs, 'name');
            $pageLayoutName = $styleMasterSet->getAttributeNS($this->stylesNs, 'page-layout-name');
            $this->masterPrintStylesCrossReference[$styleMasterName] = $pageLayoutName;
        }
    }

    public function readStyleCrossReferences(DOMDocument $contentDom): void
    {
        $item0 = $contentDom->getElementsByTagNameNS($this->officeNs, 'automatic-styles')->item(0);
        $styleXReferences = ($item0 === null) ? [] : $item0->getElementsByTagNameNS($this->stylesNs, 'style');

        foreach ($styleXReferences as $styleXreferenceSet) {
            $styleXRefName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'name');
            $stylePageLayoutName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'master-page-name');
            $styleFamilyName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'family');
            if (!empty($styleFamilyName) && $styleFamilyName === 'table') {
                $styleVisibility = 'true';
                foreach ($styleXreferenceSet->getElementsByTagNameNS($this->stylesNs, 'table-properties') as $tableProperties) {
                    $styleVisibility = $tableProperties->getAttributeNS($this->tableNs, 'display');
                }
                $this->tableStylesCrossReference[$styleXRefName] = $styleVisibility;
            }
            if (!empty($stylePageLayoutName)) {
                $this->masterStylesCrossReference[$styleXRefName] = $stylePageLayoutName;
            }
        }
    }

    public function setVisibilityForWorksheet(Worksheet $worksheet, string $styleName): void
    {
        if (!array_key_exists($styleName, $this->tableStylesCrossReference)) {
            return;
        }

        $worksheet->setSheetState(
            $this->tableStylesCrossReference[$styleName] === 'false'
                ? Worksheet::SHEETSTATE_HIDDEN
                : Worksheet::SHEETSTATE_VISIBLE
        );
    }

    public function setPrintSettingsForWorksheet(Worksheet $worksheet, string $styleName): void
    {
        if (!array_key_exists($styleName, $this->masterStylesCrossReference)) {
            return;
        }
        $masterStyleName = $this->masterStylesCrossReference[$styleName];

        if (!array_key_exists($masterStyleName, $this->masterPrintStylesCrossReference)) {
            return;
        }
        $printSettingsIndex = $this->masterPrintStylesCrossReference[$masterStyleName];

        if (!array_key_exists($printSettingsIndex, $this->pageLayoutStyles)) {
            return;
        }
        /** @var (object{orientation: string, scale: int|string, printOrder: string|null,
         * horizontalCentered: bool, verticalCentered: bool, marginLeft: float, marginRight: float, marginTop: float,
         * marginBottom: float, marginHeader: float, marginFooter: float}&stdClass) */
        $printSettings = $this->pageLayoutStyles[$printSettingsIndex];

        $worksheet->getPageSetup()
            ->setOrientation($printSettings->orientation ?? PageSetup::ORIENTATION_DEFAULT)
            ->setPageOrder($printSettings->printOrder === 'ltr' ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER)
            ->setScale((int) trim((string) $printSettings->scale, '%'))
            ->setHorizontalCentered($printSettings->horizontalCentered)
            ->setVerticalCentered($printSettings->verticalCentered);

        $worksheet->getPageMargins()
            ->setLeft($printSettings->marginLeft)
            ->setRight($printSettings->marginRight)
            ->setTop($printSettings->marginTop)
            ->setBottom($printSettings->marginBottom)
            ->setHeader($printSettings->marginHeader)
            ->setFooter($printSettings->marginFooter);
    }
}
