package com.palmnest.dao.redis.impl;

import com.palmnest.dao.redis.BaseRedisDao;
import org.springframework.beans.factory.annotation.Autowired;
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.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

@Repository("baseRedisDao")
@SuppressWarnings("unchecked")
public class BaseRedisDaoImpl implements BaseRedisDao {

	@Autowired
	private RedisTemplate redisTemplate;

	@Override
	public void flushAllCache() {
		redisTemplate.execute(new RedisCallback() {
			public Void doInRedis(RedisConnection connection)
					throws DataAccessException {
				connection.flushDb();
				return null;
			}
		});

	}

	@Override
	public String get(final String key) {
		return (String) redisTemplate.execute(new RedisCallback() {
			public String doInRedis(RedisConnection connection)
					throws DataAccessException {
				byte[] value = connection.get(key.getBytes());
				if (value != null) {
					return new String(connection.get(key.getBytes()));
				}
				return null;
			}
		});
	}

	@Override
	public void set(final String key, final String value) {
		this.set(key, value, 0L);
	}

	@Override
	public void set(String key, String value, long liveTime) {
		this.set(key.getBytes(), value.getBytes(), liveTime);
	}

	/**
	 * @param key
	 * @param value
	 * @param liveTime
	 */
	public void set(final byte[] key, final byte[] value, final long liveTime) {
		redisTemplate.execute(new RedisCallback() {
			public Long doInRedis(RedisConnection connection)
					throws DataAccessException {
				connection.set(key, value);
				if (liveTime > 0) {
					connection.expire(key, liveTime);
				}
				return 1L;
			}
		});
	}

	@Override
	public Set<String> getKeyValues(final String keyLike) {
		return (Set<String>) redisTemplate.execute(new RedisCallback() {
			public Set<String> doInRedis(RedisConnection connection)
					throws DataAccessException {
				Set<byte[]>      set    = connection.keys(keyLike.getBytes());
				Iterator<byte[]> it     = set.iterator();
				Set<String>      result = new HashSet<String>();
				if (!CollectionUtils.isEmpty(set)) {
					while (it.hasNext()) {
						result.add(new String(it.next()));
					}
				}
				return result;
			}
		});
	}

	@Override
	public void delKeyLike(String keyLike) {
		redisTemplate.execute(new RedisCallback() {
			public Long doInRedis(RedisConnection connection)
					throws DataAccessException {
				Set<byte[]>      set = connection.keys(keyLike.getBytes());
				Iterator<byte[]> it  = set.iterator();
				if (!CollectionUtils.isEmpty(set)) {
					while (it.hasNext()) {
						connection.del(it.next());
					}
				}
				return 1L;
			}
		});
	}

	@Override
	public void dele(final String key) {
		redisTemplate.execute(new RedisCallback() {
			public Long doInRedis(RedisConnection connection)
					throws DataAccessException {
				connection.del(key.getBytes());
				return 1L;
			}
		});
	}

	@Override
	public Long getDbsize() {
		return (Long) redisTemplate.execute(new RedisCallback() {
			public Long doInRedis(RedisConnection connection)
					throws DataAccessException {
				return connection.dbSize();
			}
		});
	}

	// bitmap 设置指定key指定位上的值
	public Boolean setBit(String key, Integer index, Boolean tag) {
		return (Boolean) redisTemplate.execute(new RedisCallback() {
			public Boolean doInRedis(RedisConnection connection)
					throws DataAccessException {
				return connection.setBit(key.getBytes(), index, tag);
			}
		});
//		return (Boolean) redisTemplate.execute((RedisCallback<Boolean>) con -> con.setBit(key.getBytes(), index, tag));
	}

	// 获取指定key指定位上的值
	public Boolean getBit(String key, Integer index) {
		return (Boolean) redisTemplate.execute(new RedisCallback() {
			public Boolean doInRedis(RedisConnection connection)
					throws DataAccessException {
				return connection.getBit(key.getBytes(), index);
			}
		});
//		return (Boolean) redisTemplate.execute((RedisCallback<Boolean>) con -> con.getBit(key.getBytes(), index));
	}

	/**
	 * 统计bitmap中,指定key,value值为1的个数
	 *
	 * @param key
	 * @return
	 */
	public Long bitCount(String key) {
		return (Long) redisTemplate.execute(new RedisCallback() {
			public Long doInRedis(RedisConnection connection)
					throws DataAccessException {
				return connection.bitCount(key.getBytes());
			}
		});
//		return (Long) redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes()));
	}

	// 指定key,指定区间value值为1的个数
	public Long bitCount(String key, int start, int end) {

		return (Long) redisTemplate.execute(new RedisCallback() {
			public Long doInRedis(RedisConnection connection)
					throws DataAccessException {
				return connection.bitCount(key.getBytes(), start, end);
			}
		});
//		return (Long) redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes(), start, end));
	}

	// 对一个或多个保存二进制位的字符串 key 进行位元操作，并将结果保存到 saveKey 上。
	public Long bitOp(RedisStringCommands.BitOperation op, String saveKey, String... desKey) {
		byte[][] bytes = new byte[desKey.length][];
		for (int i = 0; i < desKey.length; i++) {
			bytes[i] = desKey[i].getBytes();
		}

		return (Long) redisTemplate.execute(new RedisCallback() {
			public Long doInRedis(RedisConnection connection)
					throws DataAccessException {
				return connection.bitOp(op, saveKey.getBytes(), bytes);
			}
		});
//		return (Long) redisTemplate.execute((RedisCallback<Long>) con -> con.bitOp(op, saveKey.getBytes(), bytes));
	}
}
