<?php

/**
 * i-doit
 * Tree visitor class.
 *
 * @package     modules
 * @subpackage  analytics
 * @author      Leonard Fischer <lfischer@i-doit.com>
 * @version     1.0
 * @copyright   synetics GmbH
 * @license     http://www.i-doit.com/license
 * @since       i-doit 1.5.0
 */
class isys_visualization_tree_visitor implements isys_tree_visitor_interface
{
    /**
     * This counter will be used to reset the node IDs.
     *
     * @var  integer
     */
    private static $i = 0;

    /**
     * Filter array.
     *
     * @var  array
     */
    private $m_filter = [];

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

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

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

        return $this;
    }

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

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

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

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

            // Object type cut off.
            if (isset($this->m_filter['object-type']) && is_array($this->m_filter['object-type']) && count($this->m_filter['object-type']) > 0) {
                if (in_array($l_data['data']['objTypeID'], $this->m_filter['object-type'])) {
                    $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) {
            /*
            if ($p_node instanceof isys_tree_node && $l_child instanceof isys_tree_node)
            {
                if (isset($l_child_cache[$p_node->get_data('id') . '-' . $l_child->get_data('id')]))
                {
                    $l_child->remove();
                }

                $l_child_cache[$p_node->get_data('id') . '-' . $l_child->get_data('id')] = true;
            }
            */

            $l_return = array_merge($l_return, $l_child->accept($this));
        }

        // @todo  Try to avoid this, because the "show only it-service" option will not work
        if ($p_node->count() === 0) {
            if ($p_node->get_name() == isys_application::instance()->container->get('language')->get('LC__OBJ__ROOT_LOCATION')) {
                return [];
            }

            $l_parent = $p_node->get_parent();

            if (is_object($l_parent) && method_exists($l_parent, 'get_data')) {
                $l_data = $l_parent->get_data();

                if ($l_data['id'] == -1) {
                    return [];
                }
            }
        }

        return $l_return;
    }

    /**
     * @param   isys_tree_node $p_node
     *
     * @return  isys_tree_node
     */
    private function remove(isys_tree_node $p_node)
    {
        $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:
     * array(
     *   'level' => int,
     *   'relation-type' array of relation-types as string constants
     * )
     *
     * @param  array $p_filter
     * @param  array $p_profile
     */
    public function __construct(array $p_filter, array $p_profile = [])
    {
        $this->m_filter = $p_filter;
        $this->m_profile = $p_profile;
    }
}
