package com.data.cache;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import com.data.bean.BaseBean;
import com.taurus.core.plugin.redis.Redis;
import com.taurus.core.util.StringUtil;

import redis.clients.jedis.Jedis;

/**
 * base cache class.
 *
 */
public abstract class BaseCache {
	private static final String VER_KEY= "cache_ver";
	private static final String MAIN_KEY= "id";
	protected ConcurrentMap<String, BaseBean>			mapByKey	=   new ConcurrentHashMap<>();
	protected ConcurrentMap<Integer, BaseBean>			mapById		= 	new ConcurrentHashMap<>();
	protected String key;
	protected String cacheKey;
	protected int fillSize;
	/**默认1秒*/
	protected int readTime = 1000;
	
	protected BaseCache(String key,String cacheKey) {
		this.key = key;
		this.cacheKey = cacheKey;
	}
	
	private long readVersion(String str_ver) {
		if(StringUtil.isEmpty(str_ver)) {
			return -1;
		}
		int cache_ver = Integer.parseInt(str_ver);
		return cache_ver;
	}
	
	/**
	 * 版本KEY更新
	 * @param jedis
	 * @param key
	 */
	public static void updateCacheVer(Jedis jedis,String key) {
		jedis.hincrBy(key, VER_KEY, 1);
	}
	
	/**
	 * new bean class 
	 * @return
	 */
	protected abstract BaseBean newBean();
	
	/**
	 * 主键KEY
	 * @return
	 */
	protected String mainKey() {
		return MAIN_KEY;
	}
	
	protected BaseBean fillBean(BaseBean bean){
		Jedis jedis = Redis.use(cacheKey).getJedis();
		try {
			String str_ver = null;
			long cache_ver = -1;
			if(System.currentTimeMillis() - bean.cache_time >= readTime) {
				List<String> list = jedis.hmget(bean.redis_key, mainKey(),VER_KEY);
				if(StringUtil.isEmpty(list.get(0))) {
					bean.del = true;
				}else {
					str_ver = list.get(1);
					cache_ver = readVersion(str_ver);
					if(bean.cache_ver > 0 && cache_ver==-1) {
						cache_ver = bean.cache_ver;
					}
					
					bean.cache_time = System.currentTimeMillis();
				}
			}else {
				cache_ver = bean.cache_ver;
			}
			
			if(!bean.del && (cache_ver == -1 || bean.cache_ver < cache_ver)) {
				Map<String,String> map = jedis.hgetAll(bean.redis_key);
				try {
					if(map==null || map.size() == 0){
						bean.del = true;
					}else {
						bean.fillData(map);
					}
				}catch (Exception e) {
					throw new RuntimeException(bean.redis_key, e);
				}
				
				if(!bean.del) {
					mapById.put(bean.id, bean);
					mapByKey.put(bean.redis_key, bean);
					bean.del = false;
					str_ver = map.get(VER_KEY);
					cache_ver = readVersion(str_ver);
					bean.cache_ver = bean.cache_ver > cache_ver ? bean.cache_ver : cache_ver;
				}
			}
			
			if(bean.del) {
				mapById.remove(bean.id);
				mapByKey.remove(bean.redis_key);
				return null;
			}
			
		}finally {
			jedis.close();
		} 
		return bean;
	}
	
	private String genKey(int id) {
		String s_id = fillSize>0?(String.format("%0"+fillSize+"d", id)):(id+StringUtil.Empty);
		String key = this.key + s_id;
		return key;
	}
	
	/**
	 * get bean by id
	 * @param id
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T extends BaseBean> T getBean(int id){
		String key = genKey(id);
		synchronized (key) {
			BaseBean bean = mapById.get(id);
			if(bean==null) {
				bean = newBean();
				bean.id = id;
				bean.redis_key = key;
			}else {
				if(bean.del) {
					return null;
				}
			}
			return (T) fillBean(bean);
		}
	}
	
	/**
	 * get bean by key
	 * @param key
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T extends BaseBean> T getBean(String key){
		synchronized (key) {
			BaseBean bean = mapByKey.get(key);
			if(bean==null) {
				bean = newBean();
				bean.redis_key = key;
			}else {
				if(bean.del) {
					return null;
				}
			}
			return (T) fillBean(bean);	
		}
	}
	
	/**
	 * delete bean by id
	 * @param id
	 * @return
	 */
	public boolean delBean(int id) {
		BaseBean bean = mapById.get(id);
		if(bean!=null) {
			bean.del = true;
			return true;
		}
		return false;
	}
	
	/**
	 * delete bean by key
	 * @param key
	 * @return
	 */
	public boolean delBean(String key) {
		BaseBean bean = mapByKey.get(key);
		if(bean!=null) {
			bean.del = true;
			return true;
		}
		return false;
	}
	
	/**
	 * clear expire data
	 */
	public synchronized void clearExpireData() {
		List<BaseBean> list = new ArrayList<>(mapById.values());
		for(BaseBean bean : list) {
			if(bean.del || System.currentTimeMillis() - bean.cache_time > 180000) {
				bean.del = true;
				mapById.remove(bean.id);
				mapByKey.remove(bean.redis_key);
			}
		}
	}
	
	/**
	 * clear all
	 */
	public synchronized void clear() {
		this.mapById.clear();
		this.mapByKey.clear();
	}
	
}
