package com.ljh.excel.parser;

import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.apache.poi.ss.usermodel.BuiltinFormats;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

import com.ljh.excel.annotation.ColumnAnnotation;
import com.ljh.excel.test.User;

public class Parser extends BaseParser {
	/**
	 * See org.xml.sax.helpers.DefaultHandler javadocs
	 */
	/**
	 */
	private CellDataType nextDataType = CellDataType.SSTINDEX;

	private final DataFormatter formatter = new DataFormatter();
	private short formatIndex;

	private String formatString;

	public Parser(Class cl) {
		cls = cl;
	}

	public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException {
		ContentHandler handler = new SheetHandler(sst);
		xmlReader.setContentHandler(handler);
		return xmlReader;
	}

	class SheetHandler extends DefaultHandler {
		private SharedStringsTable sst;
		private String lastContents;
		private boolean nextIsString;

		private SheetHandler(SharedStringsTable sst) {
			this.sst = sst;

		}

		String lastTag = "";
		String con = "";
		int i=0;
		@Override
		public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
			// c => cell
			if (name.equals("c")) {
				setNextDataType(attributes);

				String attributest = attributes.getValue("r");
				String attributestr = attributest.substring(1).replaceAll("[A-Z]*", "");
				con = attributest.replaceAll("[0-9]*", "");
				if (attributestr != null && !attributestr.equals("")
						&& (lastTag.equals("") || !attributestr.equals(lastTag))) {
					lastTag = attributestr;

					// System.out.println("");

					if (bean != null) {
						beanList.add(bean);

//						if(i%2000==0){
//							System.gc();
//						}
//						System.out.println("bean:"+(i++)+"->" + bean);
						System.out.println("bean:"+(i++)+"->" );
					}
					try {
						bean = cls.newInstance();
					} catch (InstantiationException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

				}
				// Print the cell reference
				// System.out.print("(->"+attributes.getValue("r") + " - ");
				// Figure out if the value is an index in the SST
				String cellType = attributes.getValue("t");
				if (cellType != null && cellType.equals("s")) {
					nextIsString = true;
				} else {
					nextIsString = false;
				}
			}
			// Clear contents cache
			lastContents = "";
		}

		public void endElement(String uri, String localName, String name) throws SAXException {
			// Process the last contents as required.
			// Do now, as characters() may be called more than once
			if (nextIsString) {
				int idx = Integer.parseInt(lastContents);
				lastContents =new WeakReference<XSSFRichTextString>(new XSSFRichTextString(sst.getEntryAt(idx))).get().toString();
				nextIsString = false;
			}

			// v => contents of a cell
			// Output after we've seen the string contents
			if (name.equals("v") || "t".equals(name)) {
				Class c = bean.getClass();
				Field[] fs = c.getDeclaredFields();

				for (Field f : fs) {
					f.setAccessible(true);
					ColumnAnnotation canno = f.getAnnotation(ColumnAnnotation.class);
					if (canno != null && canno.value().equals(con)) {
						String fname = "set" + f.getName().substring(0, 1).toUpperCase() + f.getName().substring(1);
						try {
							Method m = c.getDeclaredMethod(fname, new Class[] { String.class });
							m.setAccessible(true);
							// v => ��Ԫ���ֵ�����Ԫ�����ַ���v��ǩ��ֵΪ���ַ���SST�е�����
							String value = getDataValue(lastContents.trim(), "");
							System.out.println(fname+lastContents.trim());
							m.invoke(bean, value.trim());
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			}
		}

		public void characters(char[] ch, int start, int length) throws SAXException {
			lastContents += new String(ch, start, length);
		}
	}

	/**
	 */
	enum CellDataType {
		BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER, DATE, NULL
	}

	/**
	 * 
	 * @param attributes
	 */
	public void setNextDataType(Attributes attributes) {
		try {
			nextDataType = CellDataType.NUMBER;
			formatIndex = -1;
			formatString = null;
			String cellType = attributes.getValue("t");
			String cellStyleStr = attributes.getValue("s");

			if ("b".equals(cellType)) {
				nextDataType = CellDataType.BOOL;
			} else if ("e".equals(cellType)) {
				nextDataType = CellDataType.ERROR;
			} else if ("inlineStr".equals(cellType)) {
				nextDataType = CellDataType.INLINESTR;
			} else if ("s".equals(cellType)) {
				nextDataType = CellDataType.SSTINDEX;
			} else if ("str".equals(cellType)) {
				nextDataType = CellDataType.FORMULA;
			}

			if (cellStyleStr != null) {
				int styleIndex = Integer.parseInt(cellStyleStr);
				XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);
				formatIndex = style.getDataFormat();
				formatString = style.getDataFormatString();

				if ("m/d/yy".equals(formatString) || "m/d/yy h:mm".equals(formatString)) {
					nextDataType = CellDataType.DATE;
					formatString = "yyyy/MM/dd hh:mm:ss";
				}

				if (formatString == null) {
					nextDataType = CellDataType.NULL;
					formatString = BuiltinFormats.getBuiltinFormat(formatIndex);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	@SuppressWarnings("deprecation")
	public String getDataValue(String value, String thisStr) {
		switch (nextDataType) {
		case BOOL:
			char first = value.charAt(0);
			thisStr = first == '0' ? "FALSE" : "TRUE";
			break;
		case ERROR:
			thisStr = "\"ERROR:" + value.toString() + '"';
			break;
		case FORMULA:
			thisStr = '"' + value.toString() + '"';
			break;
		case INLINESTR:
			XSSFRichTextString rtsi = new XSSFRichTextString(value.toString());

			thisStr = rtsi.toString();
			rtsi = null;
			break;
		case SSTINDEX:
			String sstIndex = value.toString();
			try {
				int idx = Integer.parseInt(sstIndex);
				XSSFRichTextString rtss = new XSSFRichTextString(sst.getEntryAt(idx));
				thisStr = rtss.toString();
				rtss = null;
			} catch (Exception ex) {
				thisStr = value.toString();
			}
			break;
		case NUMBER:
			if (formatString != null) {
				thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString).trim();
			} else {
				thisStr = value;
			}

			thisStr = thisStr.replace("_", "").trim();
			break;
		case DATE:
			thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString);

			// thisStr = thisStr.replace(" ", "T");
			break;
		default:
			thisStr = " ";

			break;
		}

		return thisStr;
	}
}
