<?php
/**
 * @package         Quick Index
 * @version         3.4.0PRO
 * 
 * @author          Peter van Westen <info@regularlabs.com>
 * @link            http://regularlabs.com
 * @copyright       Copyright © 2023 Regular Labs All Rights Reserved
 * @license         http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
 */

namespace RegularLabs\Plugin\System\QuickIndex;

defined('_JEXEC') or die;

use Joomla\CMS\Language\Text as JText;
use RegularLabs\Library\Html as RL_Html;
use RegularLabs\Library\RegEx as RL_RegEx;

class Index
{
    var $headings;
    var $settings;

    public function __construct(Settings $settings, $html, $start_page = 0)
    {
        $this->settings = $settings;
        $this->headings = new Headings($this->settings, $html, $start_page);
    }

    public function get()
    {
        $headings = $this->headings->get();

        if ( ! $headings)
        {
            return '';
        }

        return $this->build($headings);
    }

    public function getListTagsByLevel($level, $class = '')
    {
        $class = $class ?: $this->settings->get('class_list');
        $class = $class . ' index-level-' . $level;

        $ordered_style = $this->settings->getListStyle($level);
        $class         .= ' ' . $this->settings->getListStyleClass($ordered_style);

        return $ordered_style
            ? ['<ol class="' . trim($class) . '" type="' . $ordered_style . '">', '</ol>']
            : ['<ul class="' . trim($class) . '">', '</ul>'];

    }

    public function replaceInContents(&$string)
    {
        $this->headings->replace($string);
    }

    private function build($headings)
    {
        $this->removeNoIndexLevels($headings);

        $html   = [];
        $html[] = '<div class="rl_quickindex ' . $this->settings->get('class') . '" role="directory">';

        if ($this->settings->get('title') != '')
        {
            $tag = $this->settings->get('title_heading') == 'p'
                ? 'p'
                : 'h' . (int) $this->settings->get('title_heading');

            $html[] = '<' . $tag . ' class="' . $this->settings->get('class_index_title') . '">' . JText::_($this->settings->get('title')) . '</' . $tag . '>';
        }

        $html[] = '<div class="' . $this->settings->get('class_wrapper') . '">';

        $lowest_heading = $this->getLowestLevelHeading($headings);
        $previous_level = $lowest_heading ? $lowest_heading->level - 1 : 0;
        $first_is_set   = false;

        foreach ($headings as $heading)
        {
            /* @var Heading $heading */
            if ( ! $heading->isTitle() && ! $first_is_set)
            {
                $html[]         = $this->getOpeningTags($heading, $previous_level);
                $previous_level = $heading->level;
                $first_is_set   = true;
            }

            $html[] = $this->getItem($heading, $previous_level);

            $previous_level = $heading->level;
        }

        if ($lowest_heading)
        {
            $html[] = $this->getFinalClosingTags($lowest_heading, $previous_level);
        }

        $html[] = '</div>';
        $html[] = '</div>';

        $html = implode('', $html);

        // remove empty lists
        $html = RL_RegEx::replace('<(ul|ol)[^>]*>\s*</\1>', '', $html);

        return $html;
    }

    private function getClosingTags($heading, $previous_level = 0)
    {
        if ( ! $previous_level || $previous_level < $heading->level)
        {
            return '';
        }

        if ($previous_level == $heading->level)
        {
            return count($heading->number) > 1 ? '</li>' : '';
        }

        $tags = [];

        for ($i = $previous_level; $i >= $heading->level + 1; $i--)
        {
            [$start, $end] = $this->getListTagsByLevel($i);

            $tags[] = $end;
            $tags[] = '</li>';
        }

        return implode('', $tags);
    }

    private function getFinalClosingTags($heading, $previous_level = 0)
    {
        [$start, $end] = $this->getListTagsByLevel($heading->level);

        return $this->getClosingTags($heading, $previous_level)
            . $end;
    }

    private function getItem(Heading $heading, $previous_level = 0)
    {
        $html = [];

        $html[] = $this->getListTags($heading, $previous_level);

        $item = $this->removeLinks($heading->text);

        if ($this->settings->get('remove_html'))
        {
            $item = RL_Html::removeHtmlTags($item);
        }

        if ($this->settings->get('add_links'))
        {
            $item = '<a href="' . $heading->url . '" data-heading="' . $heading->id . '" class="index-link">' . $item . '</a>';
        }

        // check if heading should be an index title
        if ($heading->isTitle())
        {
            $title_level = min(max($heading->level + $this->settings->get('title_heading_offset'), 1), 6);

            $html[] = '<h' . $title_level . ' class="' . $this->settings->get('class_list_title') . '">'
                . $item
                . '</h' . $title_level . '>';

            return implode('', $html);
        }

//        $number = end($heading->number);
//        $html[] = '   <li value="' . $number . '">' . $item . '</li>';

        $html[] = '<li class="' . $this->settings->get('class_list_item') . '">' . $item;

        return implode('', $html);
    }

    private function getListTags($heading, $previous_level = 0)
    {
        return $this->getClosingTags($heading, $previous_level)
            . '' . $this->getOpeningTags($heading, $previous_level);
    }

    private function getLowestLevelHeading($headings)
    {
        /* @var Heading $lowest_heading */
        $lowest_heading = null;

        foreach ($headings as $heading)
        {
            /* @var Heading $heading */
            if ($heading->isTitle())
            {
                continue;
            }

            if ($lowest_heading && $lowest_heading->level < $heading->level)
            {
                continue;
            }

            $lowest_heading = $heading;
        }

        return $lowest_heading;
    }

    private function getOpeningTags($heading, $previous_level = 0)
    {
        if ($heading->isTitle())
        {
            return '';
        }

        if ($previous_level == $heading->level)
        {
            return '';
        }

        $previous_level = ! is_null($previous_level) ? $previous_level : $heading->level - 1;
        if ($previous_level == $heading->level)
        {
            return '';
        }

        $tags = [];

        for ($i = $previous_level + 1; $i <= $heading->level; $i++)
        {
            [$start, $end] = $this->getListTagsByLevel($i);

            array_push($tags, $start);
        }

        return implode('', $tags);
    }

    private function removeLinks($string)
    {
        if (empty($string))
        {
            return $string;
        }

        return RL_RegEx::replace('<a[^>]*>(.*?)</a>', '\1', $string);
    }

    private function removeNoIndexLevels(&$headings)
    {
        foreach ($headings as $i => $heading)
        {
            if ($heading->isInIndexLevels())
            {
                continue;
            }

            unset($headings[$i]);
        }
    }

}
