package me.chyxion.tigon.mybatis.cache;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.Cache;
import org.springframework.util.Assert;
import java.util.concurrent.locks.ReadWriteLock;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.HashOperations;

/**
 * @version 0.0.1
 * @since 0.0.1
 * @author Shaun Chyxion <br>
 * chyxion@163.com <br>
 * Feb 23, 2016 6:03:39 PM
 */
@Slf4j
public final class RedisCache implements Cache {
    private final String id;
    private final RedisTemplate<String, Object> redisTpl;
    private final HashOperations<String, String, Object> hashOp;
    private final RedisCacheConfig config;

    /**
     * construct cache 
     * @param id cache id
     */
    public RedisCache(final String id) {
        Assert.hasText(id, "Redis cache instance id could not be null");
        log.info("Create redis cache [{}].", id);
        this.id = id;
        config = RedisCacheConfig.getInstance();
        redisTpl = config.getRedisTpl();
        hashOp = redisTpl.opsForHash();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getId() {
        return id;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getSize() {
        final Long size = hashOp.size(id);
        log.info("Get redis cache [{}] size [{}].", id, size);
        return size.intValue();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void putObject(final Object key, final Object value) {
        log.debug("Put redis cache [{}] object key [{}], value [{}].", id, key, value);
        hashOp.put(id, keyMd5(key), value);
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    public Object getObject(final Object key) {
        final Object value = hashOp.get(id, keyMd5(key));
        log.debug("Get redis cache object key [{}], value [{}].", key, value);
        return value;
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    public Object removeObject(final Object key) {
        log.info("Remove redis cache [{}] object key [{}].", id, key);
        return hashOp.delete(id, keyMd5(key));
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    public void clear() {
        log.info("Clear redis cache [{}].", id);
        redisTpl.delete(id);
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    public ReadWriteLock getReadWriteLock() {
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return "Redis cache [" + id + "]";
    }

    // --
    // private methods

    /**
     * return prefixed cache key
     * @param key cache key
     * @return prefixed key
     */
    private String keyMd5(final Object key) {
        final String md5Hex = DigestUtils.md5Hex(key instanceof String ? (String) key : String.valueOf(key));
        log.debug("Get redis cache [{}] key [{}] md5 hex [{}].", id, key, md5Hex);
        return md5Hex;
    }
}
