package nc.vo.bd.accessor;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;

import nc.pubitf.bd.accessor.IAccessorQueryService;
import nc.pubitf.bd.accessor.IAccessorVisibleUtil;
import nc.pubitf.bd.accessor.IGeneralAccessor;
import nc.vo.bd.access.tree.AbastractTreeCreateStrategy;
import nc.vo.bd.access.tree.BDTreeCreator;
import nc.vo.bd.pub.BDCacheFactory;
import nc.vo.bd.pub.BDCacheMiscUtil;
import nc.vo.cache.ICache;
import nc.vo.pub.BusinessException;

import nccloud.commons.collections.CollectionUtils;
import nccloud.commons.lang.StringUtils;

/**
 * 树型结构的数据访问器
 * 
 * @author liujian
 * 
 */
public class HierachicalDataAccessor implements IGeneralAccessor {

	static class CoreData implements Serializable{
		private static final long serialVersionUID = 1L;

		private Set<String> cachedOrgs = new HashSet<String>();

		private DefaultTreeModel model;

		private Map<String, DefaultMutableTreeNode> pk_treenode_map = new HashMap<String, DefaultMutableTreeNode>();

		private String version;

		CoreData(List<IBDData> datas, String version) {
			// 构造树结构
			model = BDTreeCreator.createTree(datas.toArray(new IBDData[0]),
					new TreeStrategy());
			// 构造主键_树结点_map, 并记录组织集合
			pk_treenode_map = hashlizeTreeModel(model);
			this.version = version;

		}

		/**
		 * 返回所有缓存的IHierachicalData数据.
		 * 
		 * @return
		 */
		List<IBDData> extractDatas() {
			Map<String, DefaultMutableTreeNode> map = pk_treenode_map;
			Collection<DefaultMutableTreeNode> vs = map.values();
			List<IBDData> datas = new ArrayList<IBDData>();

			for (DefaultMutableTreeNode node : vs) {
				datas.add((IBDData) node.getUserObject());
			}
			return datas;
		}

		DefaultTreeModel getModel() {
			return model;
		}

		/**
		 * 根据主键查询树结点
		 * 
		 * @param pk
		 * @return
		 */
		DefaultMutableTreeNode getNodeByPk(String pk) {
			DefaultMutableTreeNode node = pk_treenode_map.get(pk);
			return node;
		}

		String getVersion() {
			return version;
		}

		/**
		 * 构造主键_树结点_map, 并记录组织集合
		 * 
		 * @param m
		 * @return
		 */
		@SuppressWarnings("unchecked")
		private Map<String, DefaultMutableTreeNode> hashlizeTreeModel(
				DefaultTreeModel m) {
			DefaultMutableTreeNode root = (DefaultMutableTreeNode) m.getRoot();

			Enumeration<DefaultMutableTreeNode> e = root.preorderEnumeration();
			e.nextElement();
			Map<String, DefaultMutableTreeNode> result = new HashMap<String, DefaultMutableTreeNode>();
			while (e.hasMoreElements()) {
				DefaultMutableTreeNode node = e.nextElement();
				if (node.getUserObject() instanceof IBDData) {
					IBDData data = (IBDData) node.getUserObject();
					data.setLevel(node.getLevel());
					// data.setLeaf(node.isLeaf());
					result.put(data.getPk(), node);
					cachedOrgs.add(data.getPk_org());
				}
			}
			return result;
		}

		/**
		 * 指定组织的数据是否已经缓存.
		 * 
		 * @param pk_org
		 * @return
		 */
		public boolean isOrgCached(String pk_org) {
			return cachedOrgs.contains(pk_org);
		}
	}

	public static class TreeStrategy extends AbastractTreeCreateStrategy {

		@Override
		public String getCodeRule() {
			return "2/2/2"; // 现在除科目外的所有档案均为主键树，此方法无意义
		}

		@Override
		public Object getCodeValue(Object obj) {
			return null;
		}

		@Override
		public Object getNodeId(Object obj) {
			if (obj instanceof IBDData) {
				IBDData data = (IBDData) obj;
				return data.getPk();
			}
			return null;

		}

		@Override
		public Object getParentNodeId(Object obj) {
			if (obj instanceof IBDData) {
				IBDData data = (IBDData) obj;
				return data.getParentPk();
			}
			return null;
		}

		@Override
		public boolean isCodeTree() {
			return false;
		}

	}

	private static final String COREDATA_CACHE_KEY = "CoreDataCacheKey";

	private String beanid;

	private CoreData coreData = null;

	private Map<String, CoreData> coreDataCacheMap = null;

	private IGeneralAccessor generalAccesor;

	private Set<String> loadedOrg = new HashSet<String>();

	private IAccessorQueryService queryService = new HierachicalDataCacheService();

	private ReadWriteLock rwlock = new ReentrantReadWriteLock();

	private IAccessorVisibleUtil visibleUitl = AccessorVisibleScopeUtil
			.getInstance();

	protected HierachicalDataAccessor() {
	}

	public HierachicalDataAccessor(String beanid) {
		this.beanid = beanid;
		generalAccesor = new GeneralAccessor(beanid);
		initCoreData();
	}

	public void clearCacheData() {
		setCoreData(new CoreData(new ArrayList<IBDData>(), ""));
		generalAccesor.clearCacheData();
	}

	private IBDData clone(IBDData data) {
		if (data == null)
			return null;
		return data.clone();
	}

	private List<IBDData> clone(List<IBDData> datas) {
		if (datas == null || datas.size() == 0)
			return null;
		List<IBDData> result = new ArrayList<IBDData>();
		for (IBDData data : datas) {
			result.add(clone(data));
		}
		return result;
	}

	private ArrayList<IBDData> combineDatas(List<IBDData> datasInCache,
			List<IBDData> datas) {
		Map<String, IBDData> pk_data_map = new HashMap<String, IBDData>();
		for (IBDData data : datasInCache) {
			pk_data_map.put(data.getPk(), data);
		}
		for (IBDData data : datas) {
			pk_data_map.put(data.getPk(), data);
		}

		ArrayList<IBDData> result = new ArrayList<IBDData>();
		result.addAll(pk_data_map.values());

		return result;
	}

	@SuppressWarnings("unused")
	private ArrayList<String> extractPKs(List<IBDData> datas) {
		ArrayList<String> pks = new ArrayList<String>();
		for (IBDData data : datas) {
			pks.add(data.getPk());
		}
		return pks;
	}

	private DefaultMutableTreeNode findNodeByPkLoadDataIfNecessary(
			String pk_org, String pk_doc) {
		if (pk_doc == null)
			return null;
		CoreData core = getCoreData();

		DefaultMutableTreeNode node = core.getNodeByPk(pk_doc);

		// pk_doc找到对应数据而且(pk_org的数据已经在缓存中 或者 之前已经尝试加载过pk_org或其等价组织的数据)
		if (node != null
				&& (core.isOrgCached(pk_org) || loadedOrg
						.contains(getVisibleUitl().getEquivalentPkOrg(beanid,
								pk_org)))) {
			return node;
		} else {

			// 原因有三
			// 1.查询了未缓存的组织的数据
			// 2.版本过期,有新增数据
			// 3.错误的pk
			synchronized (this) {
				node = core.getNodeByPk(pk_doc);

				if (node == null) {
					loadDatasByPkOrg(pk_org);
					// 很重要，必须重新获取core
					core = getCoreData();
					node = core.getNodeByPk(pk_doc);
					// if(node==null) 我们需要黑名单
				}
			}

			return node;
		}
	}

	public List<IBDData> getChildDocs(String pk_org, String pk_doc,
			boolean isIncludeSelf) {

		DefaultMutableTreeNode node = findNodeByPkLoadDataIfNecessary(pk_org,
				pk_doc);

		if (node == null)
			return new ArrayList<IBDData>();

		@SuppressWarnings("unchecked")
		Enumeration<DefaultMutableTreeNode> e = node.preorderEnumeration();
		// 排除自身
		e.nextElement();

		List<IBDData> datas = new ArrayList<IBDData>();
		if (isIncludeSelf) {
			datas.add((IBDData) node.getUserObject());
		}
		while (e.hasMoreElements()) {
			DefaultMutableTreeNode tempNode = (DefaultMutableTreeNode) e
					.nextElement();
			datas.add((IBDData) tempNode.getUserObject());

		}
		datas = getVisibleUitl()
				.filterDataByVisibleScope(datas, pk_org, beanid);

		return clone(datas);

	}

	@Override
	public String getClassTypeID() {
		return this.beanid;
	}

	CoreData getCoreData() {
		// 加读锁
		rwlock.readLock().lock();
		try {
			CoreData result = coreData;
			return result;
		} finally {
			rwlock.readLock().unlock();
		}

	}

	@SuppressWarnings("unchecked")
	private Map<String, CoreData> getCoreDataCacheMap() {
		if (coreDataCacheMap == null) {
			String regionName = ACCESSOR_REGIONNAME_PREFIX + this.beanid;
			ICache cache = BDCacheFactory.getCacheWithFileStratery(regionName);
			coreDataCacheMap = cache.toMap();
		}
		return coreDataCacheMap;
	}

	@Override
	public IBDData getDocByCode(String pk_org, String docCode) {
		IBDData[] data = getDocByCodes(pk_org, new String[] { docCode });
		if (data != null && data.length > 0)
			return data[0];
		return null;
	}

	private List<IBDData> getDocByCodeInCache(List<IBDData> cacheData, String code) {
		List<IBDData> dataList = new ArrayList<IBDData>();
		if (cacheData == null || cacheData.size() == 0
				|| StringUtils.isEmpty(code))
			return null;
		for (IBDData datum : cacheData) {
			if (code.equals(datum.getCode()))
				dataList.add(clone(datum));
//				return clone(datum);
		}
		return dataList;
	}

	@Override
	public IBDData[] getDocByCodes(String pk_org, String[] docCodes) {
		if (StringUtils.isEmpty(pk_org) || docCodes == null
				|| docCodes.length == 0)
			return new IBDData[0];
		List<IBDData> result = new ArrayList<IBDData>();
		if (!loadedOrg.contains(getVisibleUitl().getEquivalentPkOrg(beanid,
				pk_org))) {
			loadDatasByPkOrg(pk_org);
		}
		CoreData core = getCoreData();
		// 本组织可见的已缓存数据
//		List<IBDData> cacheData = getVisibleUitl().filterDataByVisibleScope(
//				core.extractDatas(), pk_org, this.beanid);
//		for (int i = 0; i < result.length; i++) {
//			result[i] = getDocByCodeInCache(cacheData, docCodes[i]);
//		}
		List<IBDData> coredata = core.extractDatas();
		
		List<IBDData> cacheData = getVisibleUitl().filterDataByVisibleScope(core.extractDatas(), pk_org, this.beanid);
		
		for (int i = 0; i < docCodes.length; i++) {
			List<IBDData> bdDatas = getDocByCodeInCache(cacheData, docCodes[i]);
			if(bdDatas == null || bdDatas.size() == 0){
				result.add(null);
			}else{
				result.addAll(bdDatas);
			}
			
		}
		// 本组织可见的已缓存数据
//		result  = getVisibleUitl().filterDataByVisibleScope(
//				result, pk_org, this.beanid);
		return result.toArray(new IBDData[0]);
	}

	@Override
	public IBDData[] getDocByNamesWithMainLang(String pk_org, String[] docNames) {
		if (StringUtils.isEmpty(pk_org) || docNames == null
				|| docNames.length == 0)
			return new IBDData[0];
		IBDData[] result = new IBDData[docNames.length];
		if (!loadedOrg.contains(getVisibleUitl().getEquivalentPkOrg(beanid,
				pk_org))) {
			loadDatasByPkOrg(pk_org);
		}
		CoreData core = getCoreData();
		// 本组织可见的已缓存数据
		List<IBDData> cacheData = getVisibleUitl().filterDataByVisibleScope(
				core.extractDatas(), pk_org, this.beanid);
		for (int i = 0; i < result.length; i++) {
			result[i] = getDocByNameWithMainLanguageInCache(cacheData,
					docNames[i]);
		}
		return result;
	}

	@Override
	public IBDData getDocByNameWithMainLang(String pk_org, String docName) {
		IBDData[] data = getDocByNamesWithMainLang(pk_org,
				new String[] { docName });
		if (data != null && data.length > 0)
			return data[0];
		return null;
	}

	private IBDData getDocByNameWithMainLanguageInCache(
			List<IBDData> cacheData, String name) {
		if (cacheData == null || cacheData.size() == 0
				|| StringUtils.isEmpty(name))
			return null;
		for (IBDData datum : cacheData) {
			if (name.equals(datum.getName().getText()))
				return clone(datum);
		}
		return null;
	}

	public IBDData getDocByPk(String docPk) {
		// 先从树上找，没有就委托给generalAccessor
		IBDData result = getIBDDataFromTree(docPk);

		if (result == null) {
			result = generalAccesor.getDocByPk(docPk);
		}
		return result;
	}

	public IBDData[] getDocbyPks(String[] docPks) {
		if (docPks == null)
			return new IBDData[0];
		LinkedHashMap<String, IBDData> pk_doc_map = new LinkedHashMap<String, IBDData>();
		List<String> notMatchPKs = new ArrayList<String>();
		IBDData[] resultsFromTree = getIBDDatasFromTree(docPks);

		for (int i = 0; i < docPks.length; i++) {
			String pk = docPks[i];
			IBDData data = resultsFromTree[i];
			pk_doc_map.put(pk, data);
			if (data == null) {
				notMatchPKs.add(pk);
			}
		}

		IBDData[] resultFromGA = generalAccesor.getDocbyPks(notMatchPKs
				.toArray(new String[0]));

		if (resultFromGA != null) {
			for (IBDData data : resultFromGA) {
				if (data != null) {
					pk_doc_map.put(data.getPk(), data);
				}
			}
		}
		IBDData[] data = new IBDData[docPks.length];
		for (int i = 0; i < docPks.length; i++) {
			data[i] = clone(pk_doc_map.get(docPks[i]));
		}
		return data;
	}
	
	

	@Override
	public List<IBDData> getFatherDocs(String pk_org, String pk_doc,
			boolean isIncludeSelf) {
		DefaultMutableTreeNode node = findNodeByPkLoadDataIfNecessary(pk_org,
				pk_doc);
		if (node == null)
			return new ArrayList<IBDData>();

		List<IBDData> datas = new ArrayList<IBDData>();
		TreeNode[] path = node.getPath();
		for (int i = path.length - 1; i >= 0; i--) {
			DefaultMutableTreeNode obj = (DefaultMutableTreeNode) path[i];
			if (obj != null && obj.getUserObject() != null
					&& obj.getUserObject() instanceof IBDData) {
				if (path[i] != node) {
					datas.add((IBDData) obj.getUserObject());
				} else if (isIncludeSelf) {
					datas.add((IBDData) obj.getUserObject());
				}
			}
		}
		return clone(datas);
	}

	@Override
	public List<IBDData> getFirstLevelDocs(String pk_org) {

		if (!loadedOrg.contains(getVisibleUitl().getEquivalentPkOrg(beanid,
				pk_org))) {
			loadDatasByPkOrg(pk_org);
		}
		List<IBDData> dataList = new ArrayList<IBDData>();
		CoreData core = getCoreData();
		DefaultMutableTreeNode root = (DefaultMutableTreeNode) core.getModel()
				.getRoot();
		int childCount = root.getChildCount();
		for (int i = 0; i < childCount; i++) {
			DefaultMutableTreeNode node = (DefaultMutableTreeNode) root
					.getChildAt(i);
			if (node != null && node.getUserObject() != null
					&& node.getUserObject() instanceof IBDData) {
				dataList.add((IBDData) node.getUserObject());
			}
		}
		return clone(getVisibleUitl().filterDataByVisibleScope(dataList,
				pk_org, beanid));
	}

	private IBDData getIBDDataFromTree(String docPk) {
		IBDData[] datas = getIBDDatasFromTree(new String[] { docPk });
		return datas[0];
	}

	private IBDData[] getIBDDatasFromTree(String[] docPks) {
		CoreData cd = getCoreData();
		IBDData[] results = new IBDData[docPks.length];
		for (int i = 0; i < docPks.length; i++) {
			String docPk = docPks[i];
			DefaultMutableTreeNode node = cd.getNodeByPk(docPk);
			IBDData result = null;
			if (node != null) {
				result = ((IBDData) node.getUserObject());
			}
			results[i] = clone(result);
		}
		return results;
	}

	@Override
	public IAccessorQueryService getQueryService() {
		return this.queryService;
	}

	@Override
	public IAccessorVisibleUtil getVisibleUitl() {
		return this.visibleUitl;
	}

	private void initCoreData() {
		String dsName = BDCacheMiscUtil.getCurrentDatasourceName();
		this.coreData = getCoreDataCacheMap().get(dsName + COREDATA_CACHE_KEY);
		if (this.coreData == null) {
			setCoreData(new CoreData(new ArrayList<IBDData>(), ""));
		}
	}

	@Override
	public boolean isHaslevel() {
		return true;
	}

	@Override
	public boolean isLeaf(String pk_org, String pk_doc) {
//		DefaultMutableTreeNode node = findNodeByPkLoadDataIfNecessary(pk_org,
//				pk_doc);
//		if (node != null)
//			return node.isLeaf();
//		return false;
		List<IBDData> children = this.getChildDocs(pk_org, pk_doc, false);
		return CollectionUtils.isEmpty(children);
	}

	/**
	 * 根据管控模式中指定的可见性加载，从pk_org角度所能看到的数据。
	 * 
	 * @param pk_org
	 * @throws BusinessException 
	 */
	// TODO: 没有定义管控模式，怎么处理？
	// TODO: 当组织能看到集团的数据， 加载不同组织时，需要避免重复加载集团数据
	// TODO: 加载数据出来后要放到treemodel里去
	protected synchronized void loadDatasByPkOrg(String pk_org)  {

		// 把pk_org根据可见性转换成更高等级的pk_org

		pk_org = getVisibleUitl().getEquivalentPkOrg(beanid, pk_org);

		HierachicalDataCacheLoadingResult result = getQueryService()
				.loadDatasByPkOrgFromBD(pk_org, beanid,
						getCoreData() == null ? "" : getCoreData().getVersion());

		List<IBDData> datas = result.getDatas();

		if (datas.size() > 0) {

			boolean outOfData = !StringUtils.equals(getCoreData().getVersion(),
					result.getNewVersion());

			if (outOfData) {
				// sortDatas((ArrayList<IBDData>) datas);
				setCoreData(new CoreData(datas, result.getNewVersion()));
				loadedOrg.clear();
			} else {
				List<IBDData> datasInCache = getCoreData().extractDatas();
				ArrayList<IBDData> combinedDatas = combineDatas(datasInCache,
						datas);
				// sortDatas(combinedDatas);
				setCoreData(new CoreData(combinedDatas, result.getNewVersion()));
			}
		}
		loadedOrg.add(pk_org);

	}

	void setBeanid(String beanid) {
		this.beanid = beanid;
		generalAccesor = new GeneralAccessor(beanid);
		initCoreData();
	}

	private void setCoreData(CoreData coreData) {
		rwlock.writeLock().lock();
		try {
			this.coreData = coreData;
			String dsName = BDCacheMiscUtil.getCurrentDatasourceName();
			getCoreDataCacheMap().put(dsName + COREDATA_CACHE_KEY, this.coreData);
		} finally {
			rwlock.writeLock().unlock();
		}

	}

	public void setQueryService(IAccessorQueryService service) {
		this.queryService = service;
	}

	public void setVisibleUitl(IAccessorVisibleUtil visibleUitl) {
		this.visibleUitl = visibleUitl;
	}
}
