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

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel;

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

import com.teamcenter.rac.util.Registry;

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

public class KExcelTable {

	private static final Registry REG = Registry.getRegistry(KExcelTable.class);

	public static class Config {
		private String colName;
		private String excelColName;
		private String additionalInfo;
		private int prefWidth;

		public Config(String colName, String excelColName, String additionalInfo, int prefWidth) {
			this.colName = colName;
			this.excelColName = excelColName;
			this.additionalInfo = additionalInfo;
			this.prefWidth = prefWidth;
			if (this.prefWidth <= 0) {
				this.prefWidth = 100;
			}
		}

		public String getColName() {
			return colName;
		}

		public String getExcelColName() {
			return excelColName;
		}

		public String getAdditionalInfo() {
			return additionalInfo;
		}

		public int getPrefWidth() {
			return prefWidth;
		}

	}

	private List<Config> configs;
	private List<String> excelCols = new ArrayList<String>();
	private List<String> tableCols = new ArrayList<String>();
	private List<String> additionalInfos = new ArrayList<String>();
	private List<Integer> prefWidth = new ArrayList<Integer>();
	private Object[][] data;
	private String filePath;
	private DefaultTableModel tableModel;
	private JTable table;
	private Map<Integer, String> dataTitleMap = new LinkedHashMap<Integer, String>();
	private boolean showSelect;
	public static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy/MM/dd");

	public JTable getTable() {
		return table;
	}

	public KExcelTable(List<Config> configs, String filePath) throws Exception {
		this(configs, filePath, false);
	}

	public KExcelTable(List<Config> configs, String filePath, boolean showSelect) throws Exception {
		this.configs = configs;
		this.filePath = filePath;
		this.showSelect = showSelect;
		parseConfigs();
		readExcel();
		initTable();
	}

	private void readExcel() throws Exception {
		File excel = new File(filePath);
		if (!excel.exists()) {
			throw new Exception("File not exist: " + filePath);
		}
		InputStream in = new FileInputStream(excel);
		XSSFWorkbook wb = new XSSFWorkbook(in);
		in.close();
		if (wb.getNumberOfSheets() <= 0) {
			throw new Exception(String.format(REG.getString("nodatainfile.ERROR1"), excel.getName()));
		}
		XSSFSheet sheet = wb.getSheetAt(0);
		int titleRow = POIUtil.getTitleRow(sheet, 0, showSelect ? excelCols.subList(1, excelCols.size()).toArray(new String[] {}) : excelCols.toArray(new String[] {}));
		if (titleRow < 0) {
			throw new Exception(String.format(REG.getString("nodatainfile.ERROR1"), excel.getName()));
		}
		LinkedHashMap<Integer, String> titleData = POIUtil.readRow(null, sheet, titleRow, false);
		int colCnt = excelCols.size();
		for (int i = 0; i < colCnt; i++) {
			String colName = excelCols.get(i);
			for (Integer key : titleData.keySet()) {
				String val = titleData.get(key);
				if (colName.equals(val)) {
					System.out.println("excel col: " + key + " = " + tableCols.get(i));
					this.dataTitleMap.put(key, tableCols.get(i));
					break;
				}
			}
		}
		int lastRow = sheet.getLastRowNum();
		List<Object[]> excelData = new ArrayList<Object[]>();
		for (int i = titleRow + 1; i <= lastRow; i++) {
			LinkedHashMap<Integer, String> rowData = POIUtil.readRow(SDF, sheet, i, this.dataTitleMap.keySet().toArray(new Integer[] {}));
			if (rowData == null || rowData.size() == 0) {
				continue;
			}
			Object[] values = rowData.values().toArray(new Object[] {});
			if (showSelect) {
				List<Object> ll = new ArrayList<>();
				ll.add(i == lastRow);
				ll.addAll(Arrays.asList(values));
				excelData.add(ll.toArray(new Object[] {}));
			} else {
				excelData.add(values);
			}
		}
		this.data = excelData.toArray(new Object[][] {});
	}

	private void parseConfigs() throws Exception {
		if (showSelect) {
			excelCols.add(UUID.randomUUID().toString());
			tableCols.add("");
			additionalInfos.add("");
			prefWidth.add(50);
		}
		for (Config config : configs) {
			excelCols.add(config.excelColName);
			tableCols.add(config.colName);
			additionalInfos.add(config.additionalInfo);
			prefWidth.add(config.prefWidth);
		}
	}

	@SuppressWarnings("serial")
	private void initTable() throws Exception {
		this.tableModel = new DefaultTableModel();
		this.table = new JTable(tableModel) {
			@Override
			public boolean isCellEditable(int row, int column) {
				if (showSelect && column == 0) {
					return true;
				}
				return false;
			}

			@Override
			public Class<?> getColumnClass(int column) {
				if (showSelect && column == 0) {
					return Boolean.class;
				}
				return super.getColumnClass(column);
			}
		};
		table.getTableHeader().setReorderingAllowed(false);
		table.setRowHeight(table.getRowHeight() + 7);
		tableModel.setDataVector(data, tableCols.toArray(new String[] {}));
		if (tableCols.size() > (showSelect ? 5 : 4)) {
			table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		}
		int colCnt = prefWidth.size();
		TableColumnModel cm = table.getColumnModel();
		for (int i = 0; i < colCnt; i++) {
			cm.getColumn(i).setPreferredWidth(prefWidth.get(i));
		}
	}

	public LinkedHashMap<Config, String> getSelectedData() {
		if (showSelect) {
			return getMultiSelectedData();
		}
		int rowNum = table.getSelectedRow();
		if (rowNum < 0) {
			return null;
		}
		int colCnt = this.configs.size();
		LinkedHashMap<Config, String> res = new LinkedHashMap<KExcelTable.Config, String>();
		for (int i = 0; i < colCnt; i++) {
			res.put(configs.get(i), (String) table.getValueAt(rowNum, i));
		}
		return res;
	}

	private LinkedHashMap<Config, String> getMultiSelectedData() {
		int rowCnt = table.getRowCount();
		int colCnt = this.configs.size();
		LinkedHashMap<Config, String> res = new LinkedHashMap<KExcelTable.Config, String>();
		for (int r = 0; r < rowCnt; r++) {
			boolean selected = (boolean) table.getValueAt(r, 0);
			if (!selected) {
				continue;
			}
			for (int i = 0; i < colCnt; i++) {
				Config config = configs.get(i);
				if (res.containsKey(config)) {
					String value = res.get(config);
					res.put(config, value + (String) table.getValueAt(r, i + 1));
				} else {
					res.put(config, (String) table.getValueAt(r, i + 1));
				}
			}
		}
		return res;
	}

	public void performSearch(Map<String, String> searchInfo) {
		if (searchInfo == null || searchInfo.size() == 0) {
			tableModel.setDataVector(data, tableCols.toArray(new String[] {}));
		} else {
			Map<Integer, String> filterInfo = new HashMap<Integer, String>();
			int ind = showSelect ? 1 : 0;
			Map<Integer, Integer> colIndMap = new HashMap<Integer, Integer>();
			for (Integer col : this.dataTitleMap.keySet()) {
				String colName = this.dataTitleMap.get(col);
				if (searchInfo.containsKey(colName)) {
					String searchValue = searchInfo.get(colName);
					filterInfo.put(col, searchValue);
					colIndMap.put(col, ind);
					System.out.println(">> Filter: " + col + " = " + searchValue);
				}
				ind++;
			}
			List<Object[]> filteredData = new ArrayList<Object[]>();
			int len = KUtil.getLen(data);
			for (int i = 0; i < len; i++) {
				Object[] rowData = data[i];
				boolean filterOK = true;
				for (Integer col : filterInfo.keySet()) {
					String rowValue = (String) rowData[colIndMap.get(col)];
					String searchValue = filterInfo.get(col);
					if (rowValue == null) {
						rowValue = "";
					}
					if (!rowValue.contains(searchValue)) {
						filterOK = false;
						break;
					}
				}
				if (filterOK) {
					filteredData.add(rowData);
				}
			}
			tableModel.setDataVector(filteredData.toArray(new Object[][] {}), tableCols.toArray(new Object[] {}));
		}
		int colCnt = prefWidth.size();
		TableColumnModel cm = table.getColumnModel();
		for (int i = 0; i < colCnt; i++) {
			cm.getColumn(i).setPreferredWidth(prefWidth.get(i));
		}
	}

}