<?php

namespace app\ic;

use app\MarkupUtil;

class TZOPart
{
    protected ?string $html = null;

    public function __construct(
        public readonly ?int $part = null,
        public readonly bool $appendix = false
    ) {
        if (!$this->appendix) {
            if (!$this->part)
                throw new \ValueError("must be either appendix or valid part");
            if (!isset(TZO::getStrings()['part_'.$this->part]))
                throw new \ValueError("invalid part");
        }
    }

    public function getNumberWithDots(): string {
        return $this->appendix ? 'null.error' : self::getPartNumberWithDots($this->part);
    }

    protected static function getPartNumberWithDots(int $part): string {
        return implode('.', str_split((string)$part));
    }

    public function getLabel(): string {
        return TZO::getStrings()[$this->getStringsKey()]['label'];
    }

    protected function getStringsKey(): string {
        return self::getPartStringsKey($this->part, $this->appendix);
    }

    protected static function getPartStringsKey(?int $part, bool $appendix = false): string {
        return !$appendix ? 'part_'.$part : 'appendix';
    }

    protected static function getPartLabel(int $part): string {
        return TZO::getStrings()[self::getPartStringsKey($part)]['label'];
    }

    /**
     * @return string[]
     */
    public function getTableOfContents(): array {
        return TZO::getStrings()[$this->getStringsKey()]['toc'];
    }

    public function getFullLabel(): string {
        return TZO::getStrings()[$this->getStringsKey()]['full_title'] ?? (!$this->appendix ? $this->getNumberWithDots().'_' : '').$this->getLabel();
    }

    public function getUrl(): string {
        return 'https://'.$_SERVER['HTTP_HOST'].'/tbc/'.($this->appendix ? 'appendix' : $this->part).'/';
    }

    public function getHtml(): string {
        if ($this->html !== null)
            return $this->html;

        $markdown_file = APP_ROOT.'/data/tzo/'.($this->appendix ? 'appendix' : $this->part).'.md';
        $md = file_get_contents($markdown_file);

        // images
        $md = preg_replace_callback(
            '#!\[]\('
            // 1 = filename
            . '(/images/tzo/[\w\d\-.]+)'
            // 2 = caption
            . '(?:\s+"((?:\\\\.|[^"\\\\])*)")?'
            . '\)#',
            function($m) {
                $file = $m[1];
                $name = basename($file);
                $caption = isset($m[2]) ? stripcslashes($m[2]) : '';
                $path = APP_ROOT.'/public/ic'.$file;
                list($w, $h) = getimagesize($path); // TODO cache image size
                return (
                    '<figure>'
                    .'<div class="img-wrapper"><img src="'.htmlescape($file).'" width="'.$w.'" height="'.$h.'" alt="'.substr($name, 0, strrpos($name, '.')).'"></div>'
                    .($caption != '' ? '<figcaption>'.htmlescape($caption).'</figcaption>' : '')
                    .'</figure>'
                );
            },
            $md
        );

        $html = MarkupUtil::markdownToHtml($md,
            use_image_previews: false);

        // links
        $html = preg_replace_callback(
            '/<center>\s*\(\[([^]]+)]\(([^)]+)\)\)\s*<\/center>/',
            function ($matches) {
                $linkText = $matches[1];
                $url = $matches[2];
                return '<p style="text-align: center">(<a href="'.htmlescape($url).'">'.htmlescape($linkText).'</a>)</p>';
            },
            $html
        );

        // chapter markers
        $html = preg_replace_callback(
            '/<center>(?:# )?(\(\d+\)|([\w\s?]+))<\/center>/',
            function ($matches) {
                $number = $matches[1];
                return '<p class="tzo-chapter" id="chapter'.$number.'">'.$number.'</p>';
            },
            $html
        );

        $this->html = $html;
        return $html;
    }

    public function getDescriptionPreview(int $len): string {
        $text = trim(MarkupUtil::htmlToText($this->getHtml()));
        if (mb_strlen($text) >= $len)
            return mb_substr($text, 0, $len - 3).'...';
        return $text;
    }

    public function getFirstImageUrl(): ?string {
        if (!preg_match('/<img[^>]+src=[\'"]([^\'"]+)[\'"]/i', $this->getHtml(), $m))
            return null;
        $url = $m[1];
        if (!str_starts_with($url, 'https://'))
            $url = 'https://'.$_SERVER['HTTP_HOST'].$url;
        return $url;
    }

    public function getNavigationTreeHtml(): string {
        if ($this->part == 1 || $this->appendix)
            return '';

        $buf_last_line = [];
        $buf_all_lines = [];

        $part_row = intval($this->part > 10 ? floor($this->part / 10) : $this->part);
        $part_column = ($this->part < 10 ? $this->part*10 : $this->part) % 10;
        if ($part_column == 0)
            $part_column = 1;

        for ($cur_row = 1;
             $cur_row <= $part_row;
             $cur_row++)
        {
            $columns = 4 - abs($cur_row - 4);
            for ($cur_column = 1; $cur_column <= $columns; $cur_column++) {
                $cur_part = $cur_row;
                if ($columns > 1) {
                    $cur_part *= 10;
                    $cur_part += $cur_column;
                }
                $label = self::getPartLabel($cur_part);
                if ($cur_row == $part_row && $cur_column > $part_column) {
                    $label_len = strlen($label);
                    $label = $label[0].str_repeat('_', $label_len-1);
                }
                $cur_part_label = self::getPartNumberWithDots($cur_part).'_'.$label;
                if ($cur_row < $part_row || $cur_column < $part_column) {
                    $buf_last_line[] = '<a href="/tbc/'.$cur_part.'/">'.htmlescape($cur_part_label).'</a>';
                } else {
                    $buf_last_line[] = $cur_part_label;
                }
            }
            $buf_all_lines[] = implode(' <span>|</span> ', $buf_last_line);
            $buf_last_line = [];
        }

        return implode('<br>', $buf_all_lines);
    }
}