<?php

namespace app\common\arw\adjfut\src;

class Traverse
{
    // 当前id key名称
    private $current = 'id';
    // 父id key名称
    private $parent = 'pid';
    // 子集名称
    private $children = 'children';
    // 数据
    private $data = [];
    // 格式化函数
    private $format = null;
    // 是否递归循环
    private $loop = false;

    public function __construct(string $current, string $parent, string $children = 'children')
    {
        $this->current = $current;
        $this->parent = $parent;
        $this->children = $children;
    }

    /**
     *   获取子集
     *   @access public
     *   @param array $data 要遍历的数组
     *   @param integer|string $id 要查找哪个id下的子集
     *   @param bool $loop 是否循环遍历
     *   @return array
     */
    public function next(array $data = [], $id = 0, bool $loop = false): array
    {
        $this->setData($data);
        $this->setLoop($loop);
        return $this->generateNext($id);
    }

    /**
     *   获取父集
     *   @access public
     *   @param array $data 要遍历的数组
     *   @param integer|string $id 要查找哪个id下的父集
     *   @param bool $loop 是否循环遍历
     *   @return array
     */
    public function prev(array $data = [], $id = 0, bool $loop = false): array
    {
        $this->setData($data);
        $this->setLoop($loop);
        return $this->generatePrev($id);
    }

    /**
     * 获取无限层级树状图
     *
     * @param array $data 要遍历的数组
     * @param integer|string $parent_id 起点id
     * @param callable $format 自定义树数据
     * @return array
     * @date 2020-04-09
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function tree(array $data, $parent_id, callable $format): array
    {
        $this->setData($data);
        $this->setFormat($format);
        return $this->generateTree($parent_id);
    }

    /**
     * 获取全部子集数据
     *
     * @param integer|string $id
     * @return array
     * @date 2022-08-08
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function getCurrentData($id): array
    {
        $data = array_filter($this->data, function ($v) use ($id) {
            return $v[$this->current] == $id;
        });
        $data = array_values($data);
        return $data;
    }

    /**
     * 获取全部父集数据
     *
     * @param integer|string $id
     * @return array
     * @date 2022-08-08
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function getParentData($id): array
    {
        $data = array_filter($this->data, function ($v) use ($id) {
            return $v[$this->parent] == $id;
        });
        $data = array_values($data);
        return $data;
    }

    /**
     * 设置循环
     *
     * @param boolean $loop
     * @return self
     * @date 2022-08-08
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function setLoop(bool $loop): self
    {
        $this->loop = $loop;
        return $this;
    }

    /**
     * 设置数据
     *
     * @param array $data
     * @return self
     * @date 2022-08-08
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function setData(array $data): self
    {
        $this->data = $data;
        return $this;
    }

    /**
     * 设置格式化函数
     *
     * @param callable $cb
     * @return self
     * @date 2022-08-08
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function setFormat(callable $cb): self
    {
        $this->format = $cb;
        return $this;
    }

    /**
     * 生成树
     *
     * @param integer|string $parent_id
     * @return array
     * @date 2022-08-08
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function generateTree($parent_id): array
    {
        $data = $this->getParentData($parent_id);
        foreach ($data as &$value) {
            $children = $this->generateTree($value[$this->current]);
            $value[$this->children] = $children;
            if (is_callable($this->format)) {
                $formatReturn = call_user_func($this->format, $value);
                if (!isset($formatReturn[$this->children])) {
                    $formatReturn[$this->children] = $children;
                }
                if ($formatReturn) {
                    $value = $formatReturn;
                }
            }
            unset($value);
        }
        return $data;
    }

    /**
     * 获取子集
     *
     * @param integer|string $id
     * @return array
     * @date 2022-08-08
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function generateNext($id): array
    {
        $re = [];
        $data = $this->getParentData($id);
        foreach ($data as $value) {
            $current = $value[$this->current];
            $re[] = $current;
            if ($this->loop) {
                $re = array_merge($re, $this->generateNext($current));
            }
        }
        return array_unique($re);
    }


    /**
     * 获取父集
     *
     * @param integer|string $id
     * @return array
     * @date 2022-08-08
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function generatePrev($id): array
    {
        $re = [];
        $data = $this->getCurrentData($id);
        foreach ($data as $value) {
            $parent = $value[$this->parent];
            $re[] = $parent;
            if ($this->loop) {
                $re = array_merge($re, $this->generatePrev($parent));
            }
        }
        return array_unique($re);
    }
}
