package com.hfkj.service.impl;


import com.alibaba.fastjson.JSONObject;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.CreateCache;
import com.hfkj.common.exception.ErrorCode;
import com.hfkj.common.exception.ErrorHelp;
import com.hfkj.common.exception.SysCode;
import com.hfkj.common.utils.HttpsUtils;
import com.hfkj.common.utils.RedisUtil;
import com.hfkj.dao.SecDictionaryMapper;
import com.hfkj.dao.SecRegionMapper;
import com.hfkj.entity.SecDictionary;
import com.hfkj.entity.SecDictionaryExample;
import com.hfkj.entity.SecRegion;
import com.hfkj.entity.SecRegionExample;
import com.hfkj.service.CommonService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;


@Service("iCommonService")
public class CommonServiceImpl implements CommonService {

	private long READ_STEP = 30*60*1000; //半小时刷新一次缓存的字典数据
	
	private ReentrantLock lock = new ReentrantLock();
	
	private ReentrantLock regionLock = new ReentrantLock();
	
	private long lastReadTime = 0;
	
	private long regionLastReadTime = 0;
	
	@Resource
	private SecRegionMapper regionMapper;

	@Resource
	private RedisUtil redisUtil;
	
	@Resource
	private SecDictionaryMapper dicMapper;

	private Map<String, Map<String, SecDictionary>> dicCache = new HashMap<String,Map<String, SecDictionary>>();

	@CreateCache(name = "cities:", cacheType = CacheType.REMOTE)
	private Cache<Long, SecRegion> citiesCache;
	@CreateCache(name = "region:", cacheType = CacheType.REMOTE)
	private Cache<Long,List<SecRegion>> regionsCache;
	@CreateCache(name = "regionStreet:", cacheType = CacheType.REMOTE)
	private Cache<Long,List<SecRegion>> streetCache;
	@CreateCache(name = "regionCommunity:", cacheType = CacheType.REMOTE)
	private Cache<Long,List<SecRegion>> communityCache;
	@CreateCache(name = "regionSingle:", cacheType = CacheType.REMOTE)
	private Cache<Long, SecRegion> singleRegionCache;

	@Override
	public Map<String, List<SecDictionary>> getDictionaries() {
		Object dictionary = redisUtil.get("SEC_DICTIONARY");
		if (dictionary == null) {
			Map<String,List<SecDictionary>> secDictionaryCache = new HashMap<>();
			SecDictionaryExample example = new SecDictionaryExample();
			example.createCriteria().andStatusEqualTo(1);
			example.setOrderByClause(" sort_id asc ");
			List<SecDictionary> dicList = dicMapper.selectByExample(example);
			for(SecDictionary dic : dicList) {
				List<SecDictionary> dictionaries = secDictionaryCache.get(dic.getCodeType());
				if(dictionaries != null) {
					List<SecDictionary> list = secDictionaryCache.get(dic.getCodeType());
					list.add(dic);
				} else {
					List<SecDictionary> list = new ArrayList<>();
					list.add(dic);
					secDictionaryCache.put(dic.getCodeType(), list);
				}
			}
			return secDictionaryCache;
		}
		return (Map<String, List<SecDictionary>>)dictionary;
	}

	/**
	 * @param codeType
	 * @param codeValue
	 * @throws
	 * @Title: getDictionary
	 * @Description: 精确查找数据字典值
	 * @author: 机器猫
	 * @param: @param codeType
	 * @param: @param codeValue
	 * @param: @return
	 * @param: @throws Exception
	 * @return: SysDictionary
	 */
	@Override
	public String getDictionaryCodeName(String codeType, String codeValue) {
		refreshDic();
		if(StringUtils.isBlank(codeType)){
			throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR, "");
		}
		Map<String, SecDictionary> m = dicCache.get(codeType);
		if (null == m) {
			return "";
		}
		SecDictionary sd = m.get(codeValue);
		if (null == sd) {
			return "";
		}
		return sd.getCodeName();
	}

	/**
	 * @param codeType
	 * @param codeValue
	 * @throws
	 * @Title: mappingSysCode
	 * @Description: 根据codetype，codevalue获取系统字典数据
	 * @author: 机器猫
	 * @param: @param codeType
	 * @param: @param codeValue
	 * @param: @return
	 * @param: @throws Exception
	 * @return: SysDictionary
	 */
	@Override
	public SecDictionary mappingSysCode(String codeType, String codeValue) {
		refreshDic();
		if(StringUtils.isBlank(codeType)){
			throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR, "");
		}
		Map<String, SecDictionary> m = dicCache.get(codeType);
		if (null == m) {
			return null;
		}
		SecDictionary sd = m.get(codeValue);
		return sd;
	}

	/**
	 * 根据codeType获取该类的所有字典数据
	 *
	 * @param codeType
	 * @return
	 */
	@Override
	public List<SecDictionary> getDictionarys(String codeType) {
		refreshDic();
		if (StringUtils.isEmpty(codeType)) {
			throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR, "codeType is null");
		}
		Map<String, SecDictionary> maps = dicCache.get(codeType);
		if (null == maps) {
			return new ArrayList<>();
		}
		List<SecDictionary> rtn = new ArrayList<>();
		rtn.addAll(maps.values());
		return rtn;
	}

	@Override
	public List<SecDictionary> getDictionarysAndExt(String codeType, String ext1) {
		refreshDic();
		if (StringUtils.isEmpty(codeType)) {
			throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR, "codeType is null");
		}
		Map<String, SecDictionary> maps = dicCache.get(codeType);
		if (null == maps) {
			return new ArrayList<>();
		}
		List<SecDictionary> rtn = new ArrayList<>();
        for(SecDictionary dictionary : maps.values()){
            if (ext1.equals(dictionary.getExt1())) {
                rtn.add(dictionary);
            }
        }
		return rtn;
	}

	@Override
	public List<JSONObject> getIdAndName(String codeType) {
		refreshDic();
		if (StringUtils.isEmpty(codeType)) {
			throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR, "codeType is null");
		}
		Map<String, SecDictionary> maps = dicCache.get(codeType);
		if (null == maps) {
			return new ArrayList<>();
		}
		List<JSONObject> rtn = new ArrayList<>();
		for(SecDictionary dictionary : maps.values()){
			JSONObject jo = new JSONObject();
			jo.put("codeValue",dictionary.getCodeValue());
			jo.put("codeName",dictionary.getCodeName());
			rtn.add(jo);
		}
		return rtn;
	}

	private void getDicFormDB(){
		SecDictionaryExample example = new SecDictionaryExample();
		example.createCriteria().andStatusEqualTo(1);
		example.setOrderByClause(" sort_id asc");
		List<SecDictionary> dicList = dicMapper.selectByExample(example);
		for(SecDictionary dic : dicList){
			if(dicCache.get(dic.getCodeType()) != null){
				Map<String, SecDictionary> typeList = dicCache.get(dic.getCodeType());
				typeList.put(dic.getCodeValue(), dic);
			}else{
				Map<String, SecDictionary> temp = new HashMap<String, SecDictionary>();
				temp.put(dic.getCodeValue(), dic);
				dicCache.put(dic.getCodeType(), temp);
			}
		}
		lastReadTime = System.currentTimeMillis();
	}
	
	private void refreshDic() {
	    if (System.currentTimeMillis() - lastReadTime < READ_STEP) {
	        return;
        }
        if (lock.isLocked()) { //说明有线程已经在刷数据了
            if (null != dicCache && dicCache.size() > 0) { //如果有数据说明已经初始化过了，直接返回老数据
                return;
            }
            while (null == dicCache || dicCache.size()<1) { //说明初始化刷数据 等待10毫秒
                try {
                    Thread.sleep(10);
                    //此处是否需要添加一个超时机制？
                } catch (InterruptedException ie) {
                    //忽略
                }
            }
            return;
        }
        try {
        	lock.lock();
            if (System.currentTimeMillis() - lastReadTime < READ_STEP) {
                return;
            }
            getDicFormDB();
        } finally {
            if (lock.isLocked()) {
            	lock.unlock();
            }
        }
    }

	@Override
	public List<SecRegion> getCities() {
		refreshRegion();
		SecRegionExample example = new SecRegionExample();
		example.createCriteria().andStatusEqualTo(1).andParentIdIsNull();
		return regionMapper.selectByExample(example);
	}

	private void refreshRegion() {
		if (regionsCache.get(500000L) != null) {
			return;
		}
		if (regionLock.isLocked()) { // 说明有线程已经在刷数据了
            if (null != regionsCache) { //如果有数据说明已经初始化过了，直接返回老数据
                return;
            }
            while (null == regionsCache) { //说明初始化刷数据 等待10毫秒
                try {
                    Thread.sleep(10);
                    //此处是否需要添加一个超时机制？
                } catch (InterruptedException ie) {
                    //忽略
                }
            }
            return;
        }
        try {
        	regionLock.lock();
            if (System.currentTimeMillis() - regionLastReadTime < READ_STEP) {
                return;
            }
            getRegionFromDB();
        } finally {
            if (regionLock.isLocked()) {
            	regionLock.unlock();
            }
        }
	}
	private void getRegionFromDB(){
		SecRegionExample example = new SecRegionExample();
		example.createCriteria().andStatusEqualTo(1).andParentIdIsNull();
		List<SecRegion> citiesList = regionMapper.selectByExample(example);
		for(SecRegion city : citiesList){	// 省
			citiesCache.put(city.getRegionId(), city);
			singleRegionCache.put(city.getRegionId(), city);
			List<SecRegion> regions = getRegions(city.getRegionId());
			regionsCache.put(city.getRegionId(),regions);
			for(SecRegion region : regions){	// 市
				singleRegionCache.put(region.getRegionId(), region);
				List<SecRegion> streets = getRegions(region.getRegionId());
				streetCache.put(region.getRegionId(), streets);
				for(SecRegion street : streets){ 	// 去
					singleRegionCache.put(street.getRegionId(), street);
					List<SecRegion> communities = getRegions(street.getRegionId());
					communityCache.put(street.getRegionId(),communities);
					for (SecRegion community : communities){
						singleRegionCache.put(community.getRegionId(),community);
					}
				}
			}
		}
		regionLastReadTime = System.currentTimeMillis();
	}

	private List<SecRegion> getRegions(Long parentId) {
		SecRegionExample example = new SecRegionExample();
		example.createCriteria().andStatusEqualTo(1).andParentIdEqualTo(parentId);
		return regionMapper.selectByExample(example);
	}
	@Override
	public List<SecRegion> getRegionsByParentId(Long parentId) {
		if(parentId == null){
			throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR);
		}
		refreshRegion();
		if(null != regionsCache.get(parentId)){
			return regionsCache.get(parentId);
		}else if(null != streetCache.get(parentId)){
			return streetCache.get(parentId);
		}else if(null != communityCache.get(parentId)){
			return communityCache.get(parentId);
		}else{
			return null;
		}
	}

	@Override
	public Map<String, SecRegion> getParentInfoByRegionId(Long regionId) {
		if(regionId == null){
			throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR);
		}
		refreshRegion();
		Map<String, SecRegion> map = new HashMap<String, SecRegion>();
		Stack<SecRegion> regionStack = getRegionStack(regionId,new Stack<>());
		if (regionStack.size() == 4){
			map.put("city",regionStack.pop());
			map.put("region",regionStack.pop());
			map.put("street",regionStack.pop());
			map.put("community",regionStack.pop());
		}else if (regionStack.size() == 3){
			map.put("city",regionStack.pop());
			map.put("region",regionStack.pop());
			map.put("street",regionStack.pop());
		}else if (regionStack.size() == 2){
			map.put("city",regionStack.pop());
			map.put("region",regionStack.pop());
		}else if (regionStack.size() == 1){
			map.put("city",regionStack.pop());
		}
		return map;
	}

	@Override
	public String getRegionName(Long regionId) {
		String regionName = "";
		Map<String, SecRegion> map = getParentInfoByRegionId(regionId);
		if (map.get("city") != null) {
			regionName += map.get("city").getRegionName();
		}
		if (map.get("region") != null) {
			regionName += map.get("region").getRegionName();
		}
		if (map.get("street") != null) {
			regionName += map.get("street").getRegionName();
		}
		if (map.get("community") != null) {
			regionName += map.get("community").getRegionName();
		}
		return regionName;
	}

	private Stack<SecRegion> getRegionStack(Long regionId, Stack<SecRegion> regionStack){
		SecRegion region = singleRegionCache.get(regionId);
		if (region != null){
			regionStack.push(region);
			getRegionStack(region.getParentId(),regionStack);
		}
		return regionStack;
	}

	@Override
	public SecRegion getRegionsById(Long regionId) {
		return regionMapper.selectByPrimaryKey(regionId);
	}

	@Override
	public Boolean findValue(String codeType, String codeValue) {
		SecDictionaryExample example = new SecDictionaryExample();
		example.createCriteria().andCodeTypeEqualTo(codeType).andCodeValueEqualTo(codeValue);

		List<SecDictionary> list = dicMapper.selectByExample(example);

		return list.size() > 0;
	}

	@Override
	public List<SecDictionary> mappingSysNameOl(String codeType) {
		SecDictionaryExample example = new SecDictionaryExample();
		example.createCriteria().andCodeTypeEqualTo(codeType);
		return dicMapper.selectByExample(example);
	}
}
