package cn.net.connor.std.processedit.word;

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import com.teamcenter.rac.aif.kernel.AIFComponentContext;
import com.teamcenter.rac.aifrcp.AIFUtility;
import com.teamcenter.rac.kernel.TCComponent;
import com.teamcenter.rac.kernel.TCComponentBOMLine;
import com.teamcenter.rac.kernel.TCComponentDataset;
import com.teamcenter.rac.kernel.TCComponentItemRevision;
import com.teamcenter.rac.kernel.TCException;
import com.teamcenter.rac.kernel.TCProperty;
import com.teamcenter.rac.kernel.TCSession;
import com.teamcenter.rac.util.Registry;

import cn.net.connor.std.aifrcp.util.POIUtil;
import cn.net.connor.std.aifrcp.util.PropLocation;

public class KUtil2 {
	
	private static final Registry REG = Registry.getRegistry(KUtil2.class);
	
	public static final TCSession session;
 
 	static {
 		session = (TCSession) AIFUtility.getCurrentApplication().getSession();
 	}

	public static List<TCComponent> getChildComponentsContainsStr(TCComponent rev, String dsType, String rel, String dsNameSubStr) throws Exception {
		if (rev == null) {
			return null;
		}
		if (dsNameSubStr == null) {
			dsNameSubStr = "";
		}
		rev.refresh();
		List<TCComponent> res = new ArrayList<TCComponent>();
		TCComponent[] kids = rev.getRelatedComponents(rel);
		for (TCComponent c : kids) {
			if (c.getType().equals(dsType) && c.getProperty("object_name").contains(dsNameSubStr)) {
				res.add(c);
			}
		}
		return res;
	}
	
	public static List<TCComponentDataset> getDatasets(TCComponent rev, String dsType, String rel) throws Exception {
		if (rev == null) {
			return null;
		}
		rev.refresh();
		List<TCComponentDataset> res = new ArrayList<TCComponentDataset>();
		TCComponent[] kids = rev.getRelatedComponents(rel);
		for (TCComponent c : kids) {
			if (c.getType().equals(dsType)) {
				res.add((TCComponentDataset) c);
			}
		}
		return res;
	}
	
	public static <E> List<List<E>> groupListWithGapAdded(List<E> list, int maxCount, E gapFiller) {
		List<List<E>> res = new ArrayList<List<E>>();
		int cnt = list == null ? 0 : list.size();
		if (maxCount <= 0) {
			maxCount = cnt;
		}
		int pageCounter = 0;
		List<E> cList = new ArrayList<E>();
		res.add(cList);
		for (int i = 0; i < cnt; i++) {
			if (pageCounter == maxCount) {
				cList = new ArrayList<>();
				res.add(cList);
				pageCounter = 0;
			}
			if (cList != null) {
				E item = list.get(i);
				if (cList.size() == 0 && item == gapFiller) {
					continue;
				}
				cList.add(list.get(i));
				pageCounter++;
			}
		}
		list.clear();
		list = null;
		return res;
	}
	
	public static <E> List<List<E>> groupList(List<E> originList, int maxCount, int gap, E gapFiller) {
		List<List<E>> res = new ArrayList<List<E>>();
		int cnt = originList == null ? 0 : originList.size();
		List<E> list = new ArrayList<E>();
		for (int i = 0; i < cnt; i++) {
			list.add(originList.get(i));
			if (i != cnt - 1) {// add gap
				for (int j = 0; j < gap; j++) {
					list.add(gapFiller);
				}
			}
		}
		cnt = list == null ? 0 : list.size();
		if (maxCount <= 0) {
			maxCount = cnt;
		}
		int pageCounter = 0;
		List<E> cList = new ArrayList<E>();
		res.add(cList);
		for (int i = 0; i < cnt; i++) {
			if (pageCounter == maxCount) {
				cList = new ArrayList<>();
				res.add(cList);
				pageCounter = 0;
			}
			if (cList != null) {
				E item = list.get(i);
				if (cList.size() == 0 && item == gapFiller) {
					continue;
				}
				cList.add(list.get(i));
				pageCounter++;
			}
		}
		list.clear();
		list = null;
		return res;
	}

	public static <E> List<List<Map<String, E>>> groupList(List<Map<String, E>> originList, int maxCount, int gap, Map<String, E> gapFiller, String gapKey) {
 		List<List<Map<String, E>>> res = new ArrayList<List<Map<String, E>>>();
 		int cnt = originList == null ? 0 : originList.size();
 		List<Map<String, E>> list = new ArrayList<Map<String, E>>();
 		boolean addGap = false;
 		for (int i = 0; i < cnt; i++) {
 			Map<String, E> item = originList.get(i);
 			list.add(item);
 			if (i != cnt - 1) {// add gap
 				Map<String, E> nextItem = originList.get(i + 1);
 				addGap = KUtil2.isEmpty(gapKey) || (nextItem != null && nextItem.containsKey(gapKey));
 				for (int j = 0; j < gap; j++) {
 					if (addGap) {
 						list.add(gapFiller);
 					}
 				}
 			}
 		}
 		cnt = list == null ? 0 : list.size();
 		if (maxCount <= 0) {
 			maxCount = cnt;
 		}
 		int pageCounter = 0;
 		List<Map<String, E>> cList = new ArrayList<Map<String, E>>();
 		res.add(cList);
 		for (int i = 0; i < cnt; i++) {
 			if (pageCounter == maxCount) {
 				cList = new ArrayList<>();
 				res.add(cList);
 				pageCounter = 0;
 			}
 			if (cList != null) {
 				Map<String, E> item = list.get(i);
 				if (cList.size() == 0 && item == gapFiller) {
 					continue;
 				}
 				cList.add(list.get(i));
 				pageCounter++;
 			}
 		}
 		list.clear();
 		list = null;
 		return res;
 	}
	

	public static String getMarkedPropValue(String config, Map<String, TCComponent> markedComponents, Map<TCComponent, List<TCComponent>> relatedComponents) throws Exception {
		if (markedComponents == null || KUtil2.isEmpty(config)) {
			return "";
		}
		StringBuilder res = new StringBuilder();
		String[] propConfigSplit = config.split("\\+");
		int len = KUtil2.getLen(propConfigSplit);
		for (int i = 0; i < len; i++) {
			String propConfig = propConfigSplit[i];
			Map<String, String> replaceMap = null;
			// rev.object_desc:被替换值1=替换值1;被替换值2=替换值2
			// rev.object_desc:EXCEL-uid-0-2 从第一列替换到第二列
			{
				int ind = propConfig.lastIndexOf(':');
				if (ind > 0) {
					String replaceConfig = propConfig.substring(ind + 1);
					propConfig = propConfig.substring(0, ind);
					if (replaceConfig.toUpperCase().startsWith("EXCEL")) {
						String[] split = replaceConfig.split("-");
						if (KUtil2.getLen(split) == 4) {
							int fromCol = -1;
							int toCol = -1;
							String uid = split[1].trim();
							fromCol = Integer.parseInt(split[2].trim());
							toCol = Integer.parseInt(split[3].trim());
							replaceMap = readReplaceMapFromExcel(uid, fromCol, toCol);
						}
					} else {
						replaceMap = new HashMap<String, String>();
						String[] split = replaceConfig.split(";");
						for (String string : split) {
							ind = string.indexOf('=');
							if (ind > 0) {
								replaceMap.put(string.substring(0, ind), string.substring(ind + 1));
							}
						}
					}
				}
			}
			String propFrom = null, propName = null;
			String defaultValue = "";// rev.object_desc.默认值
			String childTypes = ""; // rev.IMAN_specification.childTypes.childPropName-splitter
									// childTypes配置*或不配置取所有类型
			String childPropName = "";
			String childPropSpliter = ";";
			String[] split = propConfig.split("\\.", -1);
			if (KUtil2.getLen(split) >= 2) {
				propFrom = split[0].trim().toUpperCase();
				propName = split[1].trim();
			}
			if (KUtil2.getLen(split) >= 3) {
				defaultValue = split[2].trim();
				childTypes = defaultValue;
			}
			if (KUtil2.getLen(split) >= 4) {
				childPropName = split[3].trim();
				int ind = childPropName.lastIndexOf('-');
				if (ind > 0) {
					childPropSpliter = childPropName.substring(ind + 1);
					childPropName = childPropName.substring(0, ind);
				}
			}
			if (KUtil2.isEmpty(propFrom) || KUtil2.isEmpty(propName)) {
				if (propConfig.contains("\\n")) {
					propConfig = propConfig.replace("\\n", "\n");
				}
				res.append(propConfig);
				continue;
			}
			if (!markedComponents.containsKey(propFrom)) {
				if (propFrom.startsWith("T")) {
					return "";
				}
				loadSpecialLocations(propFrom, propName, res, markedComponents, relatedComponents);
				continue;
			}
			TCComponent comp = markedComponents.get(propFrom);
			// 检查表格属性 属性位置.表格属性名称-表格行属性名称[-连接符]
			int ind = propName.indexOf('-');
			if (ind > 0) {
				String tableProp = propName.substring(0, ind).trim();
				String tableRowProp = propName.substring(ind + 1).trim();
				if (!KUtil2.isEmpty(tableProp) && !KUtil2.isEmpty(tableRowProp)) {
					TCProperty prop = comp.getTCProperty(tableProp.trim());
					if (prop == null) {
						throw new Exception(String.format(REG.getString("propnotdefinedintype.ERROR2"), comp.getType(), propName));
					}
					String rowSplitter = "#ROW#";
					ind = tableRowProp.indexOf('-');
					if (ind > 0) {
						rowSplitter = tableRowProp.substring(ind + 1);
						tableRowProp = tableRowProp.substring(0, ind);
					}
					TCComponent[] tableRows = prop.getReferenceValueArray();
					int rowCnt = KUtil2.getLen(tableRows);
					String[] value = new String[rowCnt];
					for (int j = 0; j < rowCnt; j++) {
						value[j] = tableRows[j].getPropertyDisplayableValue(tableRowProp);
					}
					res.append(String.join(rowSplitter, value));
				}
				continue;
			}
			TCProperty prop = comp.getTCProperty(propName.trim());
			if (prop == null) {
				throw new Exception(String.format(REG.getString("propnotdefinedintype.ERROR2"), comp.getType(), propName));
			}
			if (prop.isReferenceType() && !KUtil2.isEmpty(childPropName)) {
				TCComponent[] arr = prop.getReferenceValueArray();
				int childCnt = KUtil2.getLen(arr);
				boolean addSplitter = false;
				for (int j = 0; j < childCnt; j++) {
					TCComponent child = arr[j];
					if (childTypes.length() < 2 || KUtil2.inArray(child.getType(), childTypes, ";")) {
						if (addSplitter) {
							res.append(childPropSpliter);
						}
						String propValue = child.getPropertyDisplayableValue(childPropName);
						if (propValue != null && replaceMap != null && replaceMap.containsKey(propValue)) {
							propValue = replaceMap.get(propValue);
						}
						res.append(propValue);
						addSplitter = true;
					}
				}
			} else {
				String value = prop.getDisplayableValue();
				if ("NAME".equalsIgnoreCase(defaultValue)) {
					value = prop.getPropertyDisplayName();
				} else if (value == null || "".equals(value)) {
					value = defaultValue;
				}
				if (value != null && replaceMap != null && replaceMap.containsKey(value)) {
					value = replaceMap.get(value);
				}
				res.append(value);
			}
		}
		return res.toString();
	}
	
	
	private static void loadSpecialLocations(String propFrom, String propName, StringBuilder res, Map<String, TCComponent> markedComponents, Map<TCComponent, List<TCComponent>> relatedComponents)
			throws Exception {
		if (markedComponents.containsKey(PropLocation.BOMLINE)) {
			if (propFrom.startsWith("#")) { // #设备对象类型1|类型2#bomline.bl_item_object_name[-连接符]
				propFrom = propFrom.substring(1);
				String splitter = ";";
				int ind = propName.lastIndexOf('-');
				if (ind > 0) {
					splitter = propName.substring(ind + 1);
					propName = propName.substring(0, ind);
				}
				String[] targetTypes;
				ind = propFrom.indexOf('#');
				if (ind > 0) {
					targetTypes = propFrom.substring(0, ind).split("\\|");
					propFrom = propFrom.substring(ind + 1).toUpperCase();
				} else {
					targetTypes = null;
					propFrom = propFrom.toUpperCase();
					// propFrom = PropLocation.BOMLINE;
				}
				TCComponent bomLine = markedComponents.get(PropLocation.BOMLINE);
				AIFComponentContext[] cc = bomLine.getChildren();
				int cLineCnt = KUtil2.getLen(cc);
				if (cLineCnt > 0) {
					System.out.println(bomLine + " -> Read ChildLine Type: " + Arrays.toString(targetTypes));
				}
				for (int j = 0; j < cLineCnt; j++) {
					TCComponentBOMLine cLine = (TCComponentBOMLine) cc[j].getComponent();
					System.out.println(">>>>> " + cLine + "|" + cLine.getStringProperty("bl_item_object_type"));
					String cItemType = cLine.getStringProperty("bl_item_object_type").toUpperCase();
					if (targetTypes == null || KUtil2.inArray(cItemType, targetTypes) >= 0) {
						Map<String, TCComponent> markedComponents2 = new HashMap<String, TCComponent>();
						PropLocation.initLocationMap(markedComponents2, cLine);
						if (markedComponents2.containsKey(propFrom)) {
							String value = markedComponents2.get(propFrom).getPropertyDisplayableValue(propName);
							System.out.println(propName + " = " + value);
							if (res.length() > 0) {
								res.append(splitter);
							}
							res.append(value);
						}
						// res.append(cLine.getProperty(propName));
						// break;
					}
				}
			} else if (propFrom.equalsIgnoreCase("pline")) {
				TCComponent bomLine = markedComponents.get(PropLocation.BOMLINE);
				TCComponentBOMLine pLine = ((TCComponentBOMLine) bomLine).parent();
				if (pLine != null) {
					res.append(pLine.getProperty(propName));
				}
			}
		}
		if (markedComponents.containsKey(PropLocation.CBOMLINE)) {
			if (propFrom.equalsIgnoreCase("parent")) {
				TCComponent bomLine = markedComponents.get(PropLocation.CBOMLINE);
				TCComponentBOMLine pLine = ((TCComponentBOMLine) bomLine).parent();
				if (pLine != null) {
					res.append(pLine.getProperty(propName));
				}
			} else if (propFrom.equalsIgnoreCase("grandparent")) {
				TCComponent bomLine = markedComponents.get(PropLocation.CBOMLINE);
				TCComponentBOMLine pLine = ((TCComponentBOMLine) bomLine).parent();
				if (pLine != null && pLine.parent() != null) {
					res.append(pLine.parent().getProperty(propName));
				}
			}
		}
		if (markedComponents.containsKey(PropLocation.REV)) {
			if (propFrom.equalsIgnoreCase("pagecount")) {
				String dsNamePrefix = propName;
				if (!KUtil2.isEmpty(dsNamePrefix)) {
					TCComponent rev = markedComponents.get(PropLocation.REV);
					System.out.println("read page count: " + rev + " -> " + dsNamePrefix);
					List<TCComponent> datasets = KUtil2.getChildComponentsContainsStr(rev, "MSWordX", KWord.REL_REV_DOC, dsNamePrefix);
					System.out.println(">> find datasets: " + datasets);
					TCComponent latestDataset = getLatestComponent(datasets);
					System.out.println(">> latest: " + latestDataset);
					if (latestDataset != null) {
						res.append("" + KWordUtil.getPageCnt((TCComponentDataset) latestDataset));
					}
				}
			} else if ("sp".equalsIgnoreCase(propFrom)) {
				TCComponent rev = markedComponents.get(PropLocation.REV);
				if (relatedComponents != null && relatedComponents.containsKey(rev)) {
					List<TCComponent> relatedComps = relatedComponents.get(rev);
					int size = relatedComps == null ? 0 : relatedComps.size();
					for (int j = 0; j < size; j++) {
						TCComponent reletedComp = relatedComps.get(j);
						res.append(reletedComp.getProperty(propName));
						if (j != size - 1) {
							res.append(";");
						}
					}
				}
			} else if ("quantity".equalsIgnoreCase(propFrom)) {
				TCComponent rev = markedComponents.get(PropLocation.REV);
				TCComponentBOMLine bomline = (TCComponentBOMLine) markedComponents.get(PropLocation.BOMLINE);
				TCComponentBOMLine topLine = getTopLine(bomline);
				System.out.println("Read Total quantity of " + rev + " from " + topLine);
				if (topLine != null) {
					double totalCount = getTotalQuantity(topLine, (TCComponentItemRevision) rev, 0);
					System.out.println("Total Quantity: " + totalCount);
					res.append(POIUtil.DF.format(totalCount));
				}
			}
		}
	}
	
	public static boolean isEmpty(String str) {
		return str == null || "".equals(str.trim());
	}

	public static int inArray(int target, int[] arr) {
		int len = arr == null ? 0 : arr.length;
		for (int i = 0; i < len; i++) {
			if (target == arr[i]) {
				return i;
			}
		}
		return -1;
	}
	
	private static TCComponentBOMLine getTopLine(TCComponentBOMLine bomline) throws Exception {
 		if (bomline == null) {
 			return null;
 		}
 		TCComponentBOMLine pline = bomline.parent();
 		if (pline == null) {
 			return bomline;
 		}
 		return getTopLine(pline);
 	}
 
 	public static double getTotalQuantity(TCComponentBOMLine parentLine, TCComponentItemRevision rev, double counter) throws Exception {
 		if (parentLine == null || rev == null) {
 			return counter;
 		}
 		parentLine.refresh();
 		if (rev.equals(parentLine.getItemRevision())) {
 			String quantity = parentLine.getProperty("bl_quantity");
 			if (KUtil2.isEmpty(quantity)) {
 				counter += 1;
 			} else {
 				double num = Double.parseDouble(quantity);
 				counter += num;
 			}
 			System.out.println(">> Add From " + parentLine + " -> [" + quantity + "]");
 		}
 		AIFComponentContext[] cc = parentLine.getChildren();
 		int len = KUtil2.getLen(cc);
 		for (int i = 0; i < len; i++) {
 			TCComponentBOMLine cLine = (TCComponentBOMLine) cc[i].getComponent();
 			counter += getTotalQuantity(cLine, rev, 0);
 		}
 		return counter;
 	}
 
 	public static TCComponent getLatestComponent(List<TCComponent> comps) {
 		int size = comps == null ? 0 : comps.size();
 		if (size == 0) {
 			return null;
 		}
 		if (size == 1) {
 			return comps.get(0);
 		}
 		TCComponent res = comps.get(0);
 		try {
 			Date maxDate = res.getDateProperty("creation_date");
 			for (int i = 1; i < size; i++) {
 				TCComponent temp = comps.get(i);
 				Date tempDate = temp.getDateProperty("creation_date");
 				if (tempDate.after(maxDate)) {
 					maxDate = tempDate;
 					res = temp;
 				}
 			}
 		} catch (Exception e) {
 			e.printStackTrace();
 		}
 		return res;
 	}

	public static boolean inArray(String str, String arr, String splitter) {
		if (KUtil2.isEmpty(arr) || KUtil2.isEmpty(splitter)) {
			return false;
		}
		if (KUtil2.isEmpty(str)) {
			return true;
		}
		return inArray(str, arr.split(splitter)) >= 0;
	}

	public static int inArray(String target, String[] arr) {
		if (target == null) {
			return -1;
		}
		int len = arr == null ? 0 : arr.length;
		for (int i = 0; i < len; i++) {
			if (target.equals(arr[i])) {
				return i;
			}
		}
		return -1;
	}
	
	public static int getLen(Object[] arr) {
		return arr == null ? 0 : arr.length;
	}

	public static String getValueInArray(String[] arr, int index) {
		int len = arr == null ? 0 : arr.length;
		if (index >= len) {
			return "";
		}
		return arr[index];
	}

	public static String getValueInArray(String arrStr, int index, String splitter) {
		if (KUtil2.isEmpty(arrStr)) {
			return "";
		}
		String[] split = arrStr.split(splitter, -1);
		int len = getLen(split);
		if (len > index && index >= 0) {
			return split[index];
		}
		return "";
	}
	
	private static Map<String, Map<String, String>> REPLACE_MAP_CACHE = new HashMap<String, Map<String, String>>();
	 
 	private static Map<String, String> readReplaceMapFromExcel(String uid, int fromCol, int toCol) throws Exception {
 		if (KUtil2.isEmpty(uid) || fromCol < 0 || toCol < 0) {
 			return null;
 		}
 		if (REPLACE_MAP_CACHE.containsKey(uid)) {
 			return REPLACE_MAP_CACHE.get(uid);
 		}
 		System.out.println("Read replace: " + uid + " - " + fromCol + " - " + toCol);
 		TCComponentDataset dataset = null;
 		try {
 			dataset = (TCComponentDataset)session.stringToComponent(uid);
 		} catch (Exception e) {
 			e.printStackTrace();
 		}
 		if(dataset==null) {
 			throw new TCException("Dataset not found: "+uid);
 		}
 		File file = KUtil2.getDatasetFile(dataset);
 		FileInputStream in = new FileInputStream(file);
 		XSSFWorkbook wb = new XSSFWorkbook(in);
 		in.close();
 		file.delete();
 		int sheetCnt = wb.getNumberOfSheets();
 		if(sheetCnt==0) {
 			throw new TCException("No sheet found: "+dataset);
 		}
 		Map<String, String> res = new LinkedHashMap<String, String>();
 		XSSFSheet sheet = wb.getSheetAt(0);
 		int lastRowNum = sheet.getLastRowNum();
 		for(int i=0; i<=lastRowNum; i++) {
 			XSSFRow row = sheet.getRow(i);
 			if(row==null) {
 				continue;
 			}
 			String from = POIUtil.getValue(row, fromCol);
 			String to = POIUtil.getValue(row, toCol);
 			if(!KUtil2.isEmpty(from)) {
 				res.put(from, to);
 			}
 		}
 		REPLACE_MAP_CACHE.put(uid, res);
 		System.out.println(">> "+res);
 		return res;
 	}
 	
 	public static File getDatasetFile(TCComponentDataset dataset) throws Exception {
 		File[] files = dataset.getFiles("", System.getenv("TEMP"));
 		if (files == null || files.length == 0) {
 			throw new Exception(String.format(REG.getString("norefindataset.ERROR1"), dataset.toString()));
 		}
 		System.out.println(">> Download file (" + files.length + "): " + files[0].getAbsolutePath());
 		return files[0];
 	}
	
	

}