package com.elab.redis;

import com.elab.redis.utils.StringSerializerUtils;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RHyperLogLog;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.*;

import java.util.*;

/**
 * 缓存直接操作类
 *
 * @author ： liukx
 * @time ： 2020/7/8 - 16:53
 */
public class CacheTemplate {

    private Logger logger = LoggerFactory.getLogger(CacheTemplate.class);

    private RedissonClient redissonClient;

    private Long timeOut;

    private RedisTemplate<String, Object> redisTemplate;

    public ValueOperations string() {
        return redisTemplate.opsForValue();
    }

    public RedissonClient getRedissonClient() {
        return redissonClient;
    }

    public void setRedissonClient(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public HashOperations map() {
        return redisTemplate.opsForHash();
    }

    public ListOperations list() {
        return redisTemplate.opsForList();
    }

    public SetOperations set() {
        return redisTemplate.opsForSet();
    }

    public ZSetOperations zset() {
        return redisTemplate.opsForZSet();
    }

    public boolean bitAdd(String key, Integer value) {
        return string().setBit(key, value, true);
    }

    /**
     * 在特定的位置设置值
     *
     * @param key
     * @param offset
     * @return
     */
    public boolean bitSet(String key, long offset) {
        return bitSet(key, offset, true);
    }

    /**
     * 在特定的位置设置值
     *
     * @param key    key
     * @param offset 偏移位置
     * @param value  设置是否存在
     * @return
     */
    public boolean bitSet(String key, long offset, boolean value) {
        return string().setBit(key, offset, value);
    }

    /**
     * 获取特定偏移量的位置是否存在值
     *
     * @param key
     * @param value
     * @return
     */
    public boolean bitGet(String key, long value) {
        return string().getBit(key, value);
    }

    /**
     * 统计特定key的bitMap的And模式
     * <p>
     * 偏亮位置中的值都为1则为true,否则false.
     *
     * @param newKey
     * @param offset
     * @param keys
     * @return
     */
    public boolean bitAndGet(String newKey, long offset, String... keys) {
        // 如果不存在的key则创建
        bitBiTop(RedisStringCommands.BitOperation.AND, newKey, false, keys);
        return bitGet(newKey, offset);
    }

    /**
     * 统计某个key中的bit为1的总数
     *
     * @param key
     * @return
     */
    public Long bitCount(String key) {

        byte[] keyByte = StringSerializerUtils.serialize(key);

        return redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
                return redisConnection.bitCount(keyByte);
            }
        });
    }

    public boolean bitBiTop(RedisStringCommands.BitOperation bitOperation, String newKey, List<String> keyList) {
        String[] keys = new String[keyList.size()];
        keyList.toArray(keys);
        return bitBiTop(bitOperation, newKey, true, keys);
    }

    /**
     * 创建一个新的key来与Keys其他key做运算
     *
     * @param bitOperation -> RedisStringCommands.BitOperation.AND || RedisStringCommands.BitOperation.XOR
     * @param isReplace    如果key已经存在是否替换
     * @param newKey       新的key的名称
     * @param keys         运算的keys
     * @return
     */
    public boolean bitBiTop(RedisStringCommands.BitOperation bitOperation, String newKey, boolean isReplace,
        String... keys) {

        if (keys == null || keys.length < 2) {
            return false;
        }

        if (!isReplace && redisTemplate.hasKey(newKey)) {
            return true;
        }

        byte[] newKeyBytes = StringSerializerUtils.serialize(newKey);
        byte[][] keysBytes = StringSerializerUtils.serializeMulti(keys);

        return redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
                return redisConnection.bitOp(bitOperation, newKeyBytes, keysBytes);
            }
        }) > 0 ? true : false;
    }

    /**
     * 批量key获取对应的值
     *
     * @param keys
     * @param offset
     * @return
     */
    public Map<String, Boolean> bitRangeGet(List<String> keys, long offset) {
        Map<String, Boolean> resultMap = new LinkedHashMap<>();
        for (String key : keys) {
            boolean result = bitGet(key, offset);
            resultMap.put(key, result);
        }
        return resultMap;
    }

    /**
     * 用于大数据计数，适用于对数据准确度不高的大数据计数场景
     * 底层是Set结构： 添加值
     *
     * @param key
     * @param value
     * @return
     */
    public boolean hyperLogAdd(String key, Object value) {
        RHyperLogLog<Object> hyperLogLog = redissonClient.getHyperLogLog(key);
        return hyperLogLog.add(value);
    }

    /**
     * 获取总数
     *
     * @param key
     * @return
     */
    public long hyperLogCount(String key) {
        RHyperLogLog<Object> hyperLogLog = redissonClient.getHyperLogLog(key);
        return hyperLogLog.count();
    }

    /**
     * 获取HyperLogLog对象
     * <p>
     * >> 用于大数据计数，适用于对数据准确度不高的大数据计数场景
     *
     * @param key
     * @return
     */
    public RHyperLogLog<Object> hyperLogObject(String key) {
        return redissonClient.getHyperLogLog(key);
    }

    /**
     * 创建布隆过滤器
     *
     * @param key
     * @param expectedInsertions
     * @param falseProbability
     * @return
     */
    public boolean bloomFilterCreate(String key, Integer expectedInsertions, double falseProbability) {
        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(key);
        return bloomFilter.tryInit(expectedInsertions, falseProbability);
    }

    /**
     * 布隆过滤器 添加值
     *
     * @param key
     * @param obj
     * @return
     */
    public boolean bloomFilterAdd(String key, Object obj) {
        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(key);
        return bloomFilter.add(obj);
    }

    /**
     * 布隆过滤器 包含的值
     *
     * @param key
     * @param object
     * @return
     */
    public boolean bloomFilterContains(String key, Object object) {
        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(key);
        return bloomFilter.contains(object);
    }

    public RBloomFilter<Object> bloomFilter(String key) {
        return redissonClient.getBloomFilter(key);
    }

}

