<?php

namespace idoit\Module\Pro\Algorithm\Visitor;

use idoit\Module\Pro\Algorithm\Filter;
use isys_application;
use isys_tree;
use isys_tree_node;
use isys_tree_visitor_interface;
use isys_visualization_profile_model;

class TreeVisitor implements isys_tree_visitor_interface
{
    /**
     * This counter will be used to reset the node IDs.
     *
     * @var integer
     */
    private static $i = 0;

    /**
     * Filter array.
     *
     * @var Filter
     */
    private $filter;

    /**
     * Array which holds the profile configuration.
     *
     * @var array
     */
    private $m_profile = [];

    /**
     * Method for retrieving the "last inserted ID".
     *
     * @return int
     */
    public static function getCounter(): int
    {
        return static::$i;
    }

    /**
     * Setting the start ID for the data.
     *
     * @param int $p_id
     *
     * @return $this
     */
    public function setCounter(int $p_id): self
    {
        static::$i = $p_id;

        return $this;
    }

    /**
     * @param isys_tree|isys_tree_node $p_node
     *
     * @return array
     */
    public function visit(isys_tree $p_node): array
    {
        $l_return = [];

        if ($p_node instanceof isys_tree_node) {
            $l_data = $p_node->get_data();

            // Level cut off.
            if ($this->filter->getLevel() > 0 && $p_node->level() > $this->filter->getLevel() + 1) {
                $l_return[] = $this->remove($p_node);
            }

            // Filter by relation type.
            if (\count($this->filter->getRelationTypes()) && isset($l_data['data']['relation']['type'])) {
                foreach ($this->filter->getRelationTypes() as $l_hideRelation) {
                    if (\defined($l_hideRelation) && \constant($l_hideRelation) == $l_data['data']['relation']['type']) {
                        $this->remove($p_node);
                    }
                }
            }

            // Object type cut off.
            if (\count($this->filter->getObjectTypes()) > 0 && \in_array($l_data['data']['objTypeID'], $this->filter->getObjectTypes(), false)) {
                $l_return[] = $this->remove($p_node);
            }

            $profileModel = isys_visualization_profile_model::instance(isys_application::instance()->container->get('database'));

            // Here we append the "profile" specific data to the node.
            if (is_array($this->m_profile['rows'])) {
                foreach ($this->m_profile['rows'] as $l_profile) {
                    $l_data['content'][$l_profile['option']] = $profileModel->get_profile_options_content($p_node, $l_profile['option']);
                }
            }

            unset($l_data['children']);

            // We need to set the ID to something new, because object-IDs confuse D3 :(
            $l_data['id'] = ++static::$i;

            if ($p_node->get_parent()) {
                // Set the generated parent ID.
                $l_data['parent'] = $p_node->get_parent()
                    ->get_data('id');
            }

            $p_node->set_data($l_data->toArray());
        }

        $l_child_cache = [];

        // Iterate through childs and call accept as well.
        foreach ($p_node->get_childs() as $l_child) {
            $l_return = array_merge($l_return, $l_child->accept($this));
        }

        return $l_return;
    }

    /**
     * @param isys_tree_node $p_node
     *
     * @return  isys_tree_node
     */
    private function remove(isys_tree_node $p_node): isys_tree
    {
        $l_descendants = $p_node->descendants(true);

        array_walk($l_descendants, function (isys_tree_node $p_desc) {
            // Remove node.
            $p_desc->remove();
        });

        return $p_node;
    }

    /**
     * Construct visitor with special filter. Filter structure.
     *
     * @param Filter $filter
     * @param array  $p_profile
     */
    public function __construct(Filter $filter, array $p_profile)
    {
        $this->filter = $filter;
        $this->m_profile = $p_profile;
    }
}
