package com.community.commom.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 单机redis 操作工具
 *
 * @author gengxiang
 * @create 2017-09-06 18:04
 **/
@Component
public class SingleRedisTemplate extends RedisTemplate {
    private Logger log = LoggerFactory.getLogger(RedisTemplate.class);

    /**
     * 从jedis连接池中获取获取jedis对象
     */
    public Jedis getJedis() {
        return redisPool.getResource();
    }

    /**
     * 回收jedis
     */
    public void returnJedis(Jedis jedis) {
        if (jedis != null)
            jedis.close();
    }

    @Override
    public String set(String key, Object value) {
        String result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.set(key.getBytes(defaultCharset), serializer.serialize(value));
        } catch (Exception e) {
            log.error("redis set error, key:" + key, e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    @Override
    public Long setnx(String key, Object value) {
        Long result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.setnx(key.getBytes(defaultCharset), serializer.serialize(value));
        } catch (Exception e) {
            log.error("redis setnx error, key:" + key, e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    @Override
    public Long expire(String key, int seconds) {
        Long result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.expire(key.getBytes(defaultCharset), seconds);
        } catch (Exception e) {
            log.error("redis expire error, key:" + key, e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    @Override
    public Long ttl(String key) {
        Long result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.ttl(key.getBytes(defaultCharset));
        } catch (Exception e) {
            log.error("redis ttl error, key:" + key, e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    @Override
    public String get(String key) {
        return get(key, String.class);
    }

    @Override
    public <T> T get(String key, Class<T> tClass) {
        byte[] result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.get(key.getBytes(defaultCharset));
        } catch (Exception e) {
            log.error("redis set error, key:" + key, e);
        } finally {
            returnJedis(jedis);
        }
        return serializer.deserialize(result, tClass);
    }

    @Override
    public Long del(String key) {
        Long result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.del(key.getBytes(defaultCharset));
        } catch (Exception e) {
            log.error("redis del error, key:" + key);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    @Override
    public Long del(String... keys) {
        Long result = 0L;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            for (String key : keys)
                result += jedis.del(key.getBytes(defaultCharset));
        } catch (Exception e) {
            log.error("redis del error, key:" + Arrays.toString(keys));
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    /**
     * Get the value of a hash field
     *
     * @param key   key
     * @param field hash field
     * @param value value
     * @return result
     */
    @Override
    public Long hset(String key, String field, Object value) {
        Long result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.hset(key.getBytes(defaultCharset), field.getBytes(defaultCharset), serializer.serialize(value));
        } catch (Exception e) {
            log.error("redis hset error, key:" + key + ", field:" + field, e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    @Override
    public <T> String hmset(String key, Map<String, T> map) {
        String result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            Map<byte[], byte[]> data = new HashMap<byte[], byte[]>();
            for (Map.Entry<String, T> entry : map.entrySet())
                data.put(entry.getKey().getBytes(defaultCharset), serializer.serialize(entry.getValue()));
            result = jedis.hmset(key.getBytes(defaultCharset), data);
        } catch (Exception e) {
            log.error("redis hmset error, key:" + key, e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    @Override
    public <T> Map<String, T> hmget(String key, List<String> fields, Class<T> tClass) {
        Map<String, T> result = new HashMap<>();
        Jedis jedis = null;
        try {
            jedis = getJedis();
            List<byte[]> byteList = new ArrayList<>(fields.size());
            for (String field : fields) {
                byte[] s = field.getBytes(defaultCharset);
                byteList.add(s);
            }
            List<byte[]> strings = jedis.hmget(key.getBytes(defaultCharset), byteList.toArray(new byte[fields.size()][]));
            for (int i = 0; i < fields.size(); i++) {
                result.put(fields.get(i), serializer.deserialize(strings.get(i), tClass));
            }
        } catch (Exception e) {
            log.error("redis hmset error, key:" + key, e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    /**
     * del the value of a hash field
     *
     * @param key   key
     * @param field hash field
     * @return result
     */
    @Override
    public Long hdel(String key, String field) {
        Long result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.hdel(key.getBytes(defaultCharset), field.getBytes(defaultCharset));
        } catch (Exception e) {
            log.error("redis hset error, key:" + key + ", field:" + field, e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    /**
     * Set the string value of a hash field
     *
     * @param key    key
     * @param field  hash field
     * @param tClass generics class type
     * @param <T>    generics class
     * @return value
     */
    @Override
    public <T> T hget(String key, String field, Class<T> tClass) {
        byte[] result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.hget(key.getBytes(defaultCharset), field.getBytes(defaultCharset));
        } catch (Exception e) {
            log.error("redis hget error, key:" + key + ", field:" + field, e);
        } finally {
            returnJedis(jedis);
        }
        return serializer.deserialize(result, tClass);
    }

    @Override
    public <T> Map<String, T> hgetAll(String key, Class<T> tClass) {
        Map<String, T> result = new HashMap<String, T>();
        Jedis jedis = null;
        try {
            jedis = getJedis();
            Map<byte[], byte[]> map = jedis.hgetAll(key.getBytes(defaultCharset));
            for (Map.Entry<byte[], byte[]> entry : map.entrySet()) {
                result.put(new String(entry.getKey(), defaultCharset), serializer.deserialize(entry.getValue(), tClass));
            }
        } catch (Exception e) {
            log.error("redis hGetAll error, key:" + key, e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    /**
     * Get all the fields in a hash
     *
     * @param key key
     * @return all the fields set
     */
    @Override
    public Set<String> hkeys(String key) {
        Set<String> result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.hkeys(key);
        } catch (Exception e) {
            log.error("redis hkeys error, key:" + key, e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    /**
     * Get a range of elements from a list
     *
     * @param key    key
     * @param tClass generic class type
     * @param start  start number, the first is 0
     * @param stop   stop number, the last is -1
     * @param <T>    generic class
     * @return list of results
     */
    @Override
    public <T> List<T> lrange(String key, Class<T> tClass, long start, long stop) {
        List<T> result = new ArrayList<>();
        Jedis jedis = null;
        try {
            jedis = getJedis();
            List<byte[]> list = jedis.lrange(key.getBytes(defaultCharset), start, stop);
            for (byte[] bytes : list) {
                result.add(serializer.deserialize(bytes, tClass));
            }
        } catch (Exception e) {
            log.error("redis lrange error key:" + key, e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    /**
     * Get all of elements from a list
     *
     * @param key    key
     * @param tClass generic class type
     * @param <T>    generic class
     * @return list of results
     */
    @Override
    public <T> List<T> lrange(String key, Class<T> tClass) {
        return lrange(key, tClass, 0L, -1L);
    }

    /**
     * Prepend one or multiple values to a list
     *
     * @param key    key
     * @param values values
     * @return result
     */
    @Override
    public Long lpush(String key, Object... values) {
        Long result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            List<byte[]> list = new ArrayList<>();
            for (Object o : values) {
                list.add(serializer.serialize(o));
            }
            result = jedis.lpush(key.getBytes(defaultCharset), list.toArray(new byte[list.size()][]));
        } catch (Exception e) {
            log.error("redis lpush error key:" + key, e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    /**
     * Prepend one or multiple values to a list
     *
     * @param key    key
     * @param values values
     * @return result
     */
    @Override
    public Long lpush(String key, List values) {
        return lpush(key, values.toArray());
    }

    /**
     * Append one or multiple values to a list
     *
     * @param key    key
     * @param values values
     * @return result
     */
    @Override
    public Long rpush(String key, Object... values) {
        Long result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            List<byte[]> list = new ArrayList<>();
            for (Object o : values) {
                list.add(serializer.serialize(o));
            }
            result = jedis.rpush(key.getBytes(defaultCharset), list.toArray(new byte[list.size()][]));
        } catch (Exception e) {
            log.error("redis rpush error key:" + key, e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    /**
     * Append one or multiple values to a list
     *
     * @param key    key
     * @param values values
     * @return result
     */
    @Override
    public Long rpush(String key, List values) {
        return rpush(key, values.toArray());
    }

    /**
     * redis setnx实现分布式锁
     *
     * @param key key
     * @return true:获取到锁, false: 未获取到锁
     */
    @Override
    public Boolean getLock(String key) {
        Long result;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            for (int i = 0; i < maxLockTime / lockRetryTime + 1; i++) {
                Long now = System.currentTimeMillis();
                result = jedis.setnx(key, String.valueOf(now + maxLockTime));
                if (result == 1L
                        || now > Long.parseLong(jedis.get(key))
                        && now > Long.parseLong(jedis.getSet(key, String.valueOf(now + maxLockTime)))) {
                    jedis.expire(key, (int) (maxLockTime / 1000));
                    return true;
                } else Thread.sleep(lockRetryTime);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            returnJedis(jedis);
        }
        return false;
    }

    /**
     * redis  释放分布式锁
     *
     * @param key key
     */
    @Override
    public void releaseLock(String key) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            jedis.del(key);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            returnJedis(jedis);
        }
    }

    /**
     * 缓存是否存在
     *
     * @param key 键
     * @return
     */
    @Override
    public boolean exists(String key) {
        boolean result = false;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.exists(key);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }


    /**
     * 缓存是否存在
     *
     * @param key 键
     * @return
     */
    @Override
    public boolean hexists(String key, String field) {
        boolean result = false;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.hexists(key, field);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    /**
     * 获取hash缓存的个数
     *
     * @param key 键
     * @return
     */
    @Override
    public Long hlen(String key) {
        Long result = 0L;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.hlen(key);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    /**
     * 计数器递减
     *
     * @param key
     * @return
     */
    @Override
    public Long decr(String key) {
        Long result = 0L;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.decr(key);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    /**
     * 计数器递增
     *
     * @param key
     * @return
     */
    @Override
    public Long incr(String key) {
        Long result = 0L;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.incr(key);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }

    @Override
    public Long hincrBy(String key, String field, long value) {
        Long result = 0L;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.hincrBy(key, field, value);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            returnJedis(jedis);
        }
        return result;
    }
}
