<?php

namespace Heanup\Frame\Package;

/**
 * 使用 redis sorted sets
 */
class Set extends \Heanup\Frame\Package
{
    const CONFIG_CLASS = 'Redis\\Core';
    const PREFIX = '';
    const EXPIRE = 0; // 3 天


    /**
     * 获取列表，从$start 到 $end，默认获取全部
     * 从大到小获取!!
     * @param $id
     * @param $lastId
     * @param $limit
     * @return array
     */
    public static function get($id, $lastId, $limit = 20, $withscores = false)
    {
        $key = self::getKey($id);
        try {
            if ($lastId <= 0) {
                $options = ($limit > 0) ? array('limit' => array(0, $limit)) : array();
                if ($withscores) {
                    $options['withscores'] = true;
                }
                $result = self::getConnection()->zRevRangeByScore($key, PHP_INT_MAX, 0, $options);
            } else {
                if ($withscores) {
                    $options['withscores'] = true;
                }
                // 获取$lastId对应的score, 如果没有找到,则返回空
                $score = self::getConnection()->zScore($key, $lastId);

                if ($score === false) {
                    return array();
                }
                if ($limit <= 0) {
                    $result = self::getConnection()->zRevRangeByScore($key, $score, 0, $options);
                    if ($result) {
                        if ($lastId == current($result)) {
                            array_shift($result);
                        }
                    }
                } else {
                    // 排除第一个元素, 多取出一个元素, 然后删除
                    $limit = $limit + 1;
                    $options = array('limit' => array(0, $limit));
                    if ($withscores) {
                        $options['withscores'] = true;
                    }
                    $result = self::getConnection()->zRevRangeByScore($key, $score, 0, $options);

                    if ($result) {
                        if ($lastId == current($result)) {
                            array_shift($result);
                        } elseif (count($result) >= $limit) {
                            array_pop($result);
                        }
                    }
                }
            }
        } catch (\Exception $ex) {
            $result = array();
        }
        return $result;
    }

    public static function getsByIndex($id, $lastId, $limit = 20, $withscores = false)
    {
        $key = self::getKey($id);
       
        try {
            if ($lastId <= 0) { 
                $start = 0;
                $stop = $limit-1; 
                $result = self::getConnection()->zRevRange($key, $start, $stop, $withscores);
            } else {
                $last_index = self::getConnection()->zRevRank($key, $lastId);
                if ($last_index) {
                    $start = $last_index;
                    $stop = ($start + $limit);
                    $result = self::getConnection()->zRevRange($key, $start, $stop, $withscores);
                    if ($result) {
                        if ($lastId == current($result)) {
                            array_shift($result);
                        }
                    }
                } 
            }
        } catch (\Exception $ex) {
            $result = array();
        }
        return $result;
    }

    public static function getByPage($id, $page, $limit = 20, $withscores = false)
    {
        $key = self::getKey($id);
        try {
            $options = array('limit' => array($page * $limit, $limit));
            if ($withscores) {
                $options['withscores'] = true;
            }
            $result = self::getConnection()->zRevRangeByScore($key, PHP_INT_MAX, 0, $options);

        } catch (\Exception $ex) {
            $result = array();
        }
        return $result;
    }

    /**
     * 获取所有的ID列表
     * @param $id
     * @return array
     */
    public static function getAll($id, $withscores = true)
    {
        $key = self::getKey($id);
        //89de3bf93d9670af:
        $options = array();
        if ($withscores) {
            $options['withscores'] = true;
        }
        try {
            $result = self::getConnection()->zRevRangeByScore($key, PHP_INT_MAX, 0, $options);
        } catch (\Exception $ex) {
            $result = array();
        }
        return $result;
    }

    /**
     * 获取 $value 的索引值
     */
    public static function getIndex($id, $value)
    {
        $key = self::getKey($id);
        try {
            $result = self::getConnection(true)->zRank($key, $value);
        } catch (\Exception $ex) {
            $result = false;
        }
        return $result;
    }

    /**
     * 添加元素, 注意score相同时可能会有问题, 建议使用 addBatch()
     * @param $id
     * @param int $score
     * @param int $value
     * @return bool
     */
    public static function add($id, $score, $value)
    {
        $key = self::getKey($id);
        try {
            $result = self::getConnection(true)->zAdd($key, $score, $value);
            // 可选过期
            $class = get_called_class();
            if ($class::EXPIRE) {
                self::getConnection(true)->expire($key, $class::EXPIRE);
            }
        } catch (\Exception $ex) {
            $result = false;
        }
        return $result;
    }

    /**
     * 批量添加, 重新计算权重
     * @param $id
     * @param array $data key为列表id,value为score,会重新排序,然后计算score
     * @return bool|int
     */
    public static function addBatch($id, Array $data)
    {
        asort($data); // 升序排列
        $i = 1;
        foreach ($data as $k => $score) {
            $score = $i * 10000;
            self::add($id, $score, $k);
            $i++;
        }
        return true;
    }

    /**
     * 移除元素
     * @param $id
     * @param int $value
     * @return int
     */
    public static function remove($id, $value)
    {
        $key = self::getKey($id);
        try {
            $result = self::getConnection(true)->zDelete($key, $value);
        } catch (\Exception $ex) {
            $result = false;
        }
        return $result;
    }

    /**
     * 更新集合内容, 先删除(不判断结果),然后插入
     * @param $id
     * @param $score
     * @param $value
     * @return bool|int
     */
    public static function update($id, $score, $value)
    {
        self::remove($id, $value);
        $result = self::add($id, $score, $value);
        return $result;
    }

    /**
     * 移除指定范围内元素
     * @param $id
     * @param int $start
     * @param int $stop
     * @return int
     */
    public static function removeByLex($id, $start, $stop)
    {
        $key = self::getKey($id);
        try {
            $result = self::getConnection(true)->zDeleteRangeByRank($key, $start, $stop);
        } catch (\Exception $ex) {
            $result = false;
        }
        return $result;
    }

    /**
     * 获取列表长度
     * @param $id
     * @param int $start
     * @param int $end
     * @return int
     */
    public static function count($id, $start = 0, $end = PHP_INT_MAX)
    {
        $key = self::getKey($id);
        try {
            $result = self::getConnection(true)->zCount($key, $start, $end);
        } catch (\Exception $ex) {
            $result = 0;
        }
        return $result;
    }

    /**
     * 删除
     * @param $id
     * @return bool
     */
    public static function delete($id)
    {
        $key = self::getKey($id);
        try {
            $result = self::getConnection(true)->delete($key);
        } catch (\Exception $ex) {
            $result = false;
        }
        return $result;
    }

    /**
     * 获取连接池
     * @param bool $isMaster
     * @return \Redis
     */
    protected static function getConnection($isMaster = false)
    {
        $class = get_called_class();
        $configClass = $class::CONFIG_CLASS;
        $redis = new \Heanup\Frame\Library\Redis($configClass);
        $connection = $redis->getConnection($isMaster);
        return $connection;
    }

}