package net.w_horse.excelpojo.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.HashMap;

import net.w_horse.excelpojo.ExcelPOJOBridge;
import net.w_horse.excelpojo.excel.AbstractCellSeeker;
import net.w_horse.excelpojo.excel.AbstractRepeatsSeeker;
import net.w_horse.excelpojo.excel.ConstantValueCellSeeker;
import net.w_horse.excelpojo.excel.HorizontalRepeatsSeeker;
import net.w_horse.excelpojo.excel.LabeledCellSeeker;
import net.w_horse.excelpojo.excel.MappedCellSeeker;
import net.w_horse.excelpojo.excel.PointedCellSeeker;
import net.w_horse.excelpojo.excel.VerticalRepeatsSeeker;
import net.w_horse.excelpojo.xml.tag.DataDirection;
import net.w_horse.excelpojo.xml.tag.RetrieveFrom;
import net.w_horse.excelpojo.xml.tag.Use;

import org.springframework.util.ClassUtils;

/**
 * アノテーションを解析してExcel検索オブジェクトの情報をセットするクラス
 *
 * @author kawahara
 *
 */
public class ExcelPOJOAnnotationParser {
	private String targetClass = "";
	/**
	 *
	 *
	 * @param targetClassProperties
	 * @param targetClass
	 * @throws LinkageError
	 * @throws ClassNotFoundException
	 */
	public void setTargetClassProperties(
			HashMap<String, AbstractCellSeeker> targetClassProperties,
			Class<?> targetClass) throws ClassNotFoundException, LinkageError {

		for (Field field : targetClass.getDeclaredFields()) {
			for (Annotation annotation : field.getAnnotations()) {
				// 表の場合は要素（POJO）についても未定義のフィールドをセットする
				if ((annotation instanceof VerticalRepeats)
					&& (targetClassProperties.containsKey(field.getName()))
				) {
					VerticalRepeatsSeeker seeker = (VerticalRepeatsSeeker) targetClassProperties.get(field.getName());
					setRepeatssetRepeatsSeekerField(seeker.getExcelPOJOBridge(),
									getRepeatsAnnnotationInfo((VerticalRepeats)annotation),
									RetrieveFrom.BOTTOM.getValue(),
									DataDirection.RIGHT.getValue());
				} else if ((annotation instanceof HorizontalRepeats)
					&& (targetClassProperties.containsKey(field.getName()))
				) {
					HorizontalRepeatsSeeker seeker = (HorizontalRepeatsSeeker) targetClassProperties.get(field.getName());
					setRepeatssetRepeatsSeekerField(seeker.getExcelPOJOBridge(),
									getRepeatsAnnnotationInfo((HorizontalRepeats)annotation),
									RetrieveFrom.RIGHT.getValue(),
									DataDirection.DOWN.getValue());
				}

				// もしseekerがセットされていなければ、
				// 下記の処理で新規作成されてセットされる。
				if (!targetClassProperties.containsKey(field.getName())) {
					AbstractCellSeeker seeker = buildCellSeeker(annotation);
					seeker.set(annotation, this);
					targetClassProperties.put(field.getName(), seeker);
				}
			}
		}
	}

	// CellSeekerごとに異なる内容をセットする
	public void setCellSeeker(Annotation annotation, LabeledCellSeeker seeker) {
		LabeledCell annotationLabeledCell = (LabeledCell)annotation;
		seeker.setLabel(annotationLabeledCell.label());
		seeker.setRetrieveFrom(annotationLabeledCell.retrieveFrom().getValue());
		seeker.setMargedLabel(String.valueOf(annotationLabeledCell.margedLabel()));
		seeker.setMargedRows(annotationLabeledCell.margedRows());
		seeker.setUse(annotationLabeledCell.use().getValue());
	}
	public void setCellSeeker(Annotation annotation, PointedCellSeeker seeker) {
		PointedCell annotationPointedCell = (PointedCell)annotation;
		seeker.setPosition(annotationPointedCell.position());
		seeker.setUse(annotationPointedCell.use().getValue());
	}
	public void setCellSeeker(Annotation annotation, ConstantValueCellSeeker seeker) {
		ConstantValue annotationConstantValue = (ConstantValue)annotation;
		seeker.setValue(annotationConstantValue.value());
	}
	public void setCellSeeker(Annotation annotation, VerticalRepeatsSeeker seeker) throws ClassNotFoundException, LinkageError {
		HashMap<String, String> annotationInfoMap = null;
		if (annotation instanceof Bean) {
			annotationInfoMap = getRepeatsAnnnotationInfo((Bean)annotation);
		} else {
			annotationInfoMap = getRepeatsAnnnotationInfo((VerticalRepeats)annotation);
		}
		setRepeatsSeeker(annotationInfoMap,
						seeker,
						RetrieveFrom.BOTTOM.getValue(),
						DataDirection.RIGHT.getValue());
	}
	public void setCellSeeker(Annotation annotation, HorizontalRepeatsSeeker seeker) throws ClassNotFoundException, LinkageError {
		HashMap<String, String> annotationInfoMap = null;
		if (annotation instanceof Bean) {
			annotationInfoMap = getRepeatsAnnnotationInfo((Bean)annotation);
		} else {
			annotationInfoMap = getRepeatsAnnnotationInfo((HorizontalRepeats)annotation);
		}
		setRepeatsSeeker(annotationInfoMap,
						seeker,
						RetrieveFrom.RIGHT.getValue(),
						DataDirection.DOWN.getValue());
	}

	public void setCellSeeker(Annotation annotation, MappedCellSeeker seeker) {
		MappedCell annotationMappedCell = (MappedCell)annotation;
		seeker.setPreviousLabel(annotationMappedCell.previousLabel());
		seeker.setPosition(annotationMappedCell.position());
		seeker.setRetrieveFrom(annotationMappedCell.retrieveFrom().getValue());
		seeker.setDataDirection(annotationMappedCell.dataDirection().getValue());
		seeker.setMargedLabel(String.valueOf(annotationMappedCell.margedLabel()));
		seeker.setUse(annotationMappedCell.use().getValue());
	}

	private static final String ANN_ATTR_TARGET_CLASS = "targetClass";
	private static final String ANN_ATTR_LABEL = "label";
	private static final String ANN_ATTR_POSITION = "position";
	private static final String ANN_ATTR_TERMINATE = "terminate";
	private static final String ANN_ATTR_RETRIEVE_FROM = RetrieveFrom.getTagName();
	private static final String ANN_ATTR_MARGED_ROWS = "margedRows";
	private static final String ANN_ATTR_USE = Use.getTagName();
	private static final String ANN_ATTR_LIST_CLASS = "listClass";
	private HashMap<String, String> getRepeatsAnnnotationInfo(VerticalRepeats annotation) {
		HashMap<String, String> annotationInfoMap = new HashMap<String, String>();
		annotationInfoMap.put(ANN_ATTR_TARGET_CLASS, annotation.targetClass());
		annotationInfoMap.put(ANN_ATTR_LABEL, annotation.label());
		annotationInfoMap.put(ANN_ATTR_POSITION, annotation.position());
		annotationInfoMap.put(ANN_ATTR_TERMINATE, annotation.terminate());
		annotationInfoMap.put(ANN_ATTR_RETRIEVE_FROM, annotation.retrieveFrom().getValue());
		annotationInfoMap.put(ANN_ATTR_MARGED_ROWS, String.valueOf(annotation.margedRows()));
		annotationInfoMap.put(ANN_ATTR_USE, annotation.use().getValue());
		annotationInfoMap.put(ANN_ATTR_LIST_CLASS, annotation.listClass());
		return annotationInfoMap;
	}
	private HashMap<String, String> getRepeatsAnnnotationInfo(HorizontalRepeats annotation) {
		HashMap<String, String> annotationInfoMap = new HashMap<String, String>();
		annotationInfoMap.put(ANN_ATTR_TARGET_CLASS, annotation.targetClass());
		annotationInfoMap.put(ANN_ATTR_LABEL, annotation.label());
		annotationInfoMap.put(ANN_ATTR_POSITION, annotation.position());
		annotationInfoMap.put(ANN_ATTR_TERMINATE, annotation.terminate());
		annotationInfoMap.put(ANN_ATTR_RETRIEVE_FROM, annotation.retrieveFrom().getValue());
		annotationInfoMap.put(ANN_ATTR_MARGED_ROWS, String.valueOf(annotation.margedRows()));
		annotationInfoMap.put(ANN_ATTR_USE, annotation.use().getValue());
		annotationInfoMap.put(ANN_ATTR_LIST_CLASS, annotation.listClass());
		return annotationInfoMap;
	}
	private HashMap<String, String> getRepeatsAnnnotationInfo(Bean annotation) {
		HashMap<String, String> annotationInfoMap = new HashMap<String, String>();
		annotationInfoMap.put(ANN_ATTR_TARGET_CLASS, getTargetClass());
		annotationInfoMap.put(ANN_ATTR_LABEL, annotation.label());
		annotationInfoMap.put(ANN_ATTR_POSITION, annotation.position());
		annotationInfoMap.put(ANN_ATTR_TERMINATE, annotation.terminate());
		annotationInfoMap.put(ANN_ATTR_RETRIEVE_FROM, annotation.retrieveFrom().getValue());
		annotationInfoMap.put(ANN_ATTR_MARGED_ROWS, String.valueOf(annotation.margedRows()));
		annotationInfoMap.put(ANN_ATTR_USE, annotation.use().getValue());
		annotationInfoMap.put(ANN_ATTR_LIST_CLASS, annotation.listClass());
		return annotationInfoMap;
	}
	private void setRepeatsSeeker(HashMap<String, String> annotationInfoMap,
								AbstractRepeatsSeeker seeker,
								String defaultRetrieveFrom, String defaultDataDirection
							) throws ClassNotFoundException, LinkageError {
		seeker.setLabel(annotationInfoMap.get(ANN_ATTR_LABEL));
		seeker.setPosition(annotationInfoMap.get(ANN_ATTR_POSITION));
		seeker.setTerminate(annotationInfoMap.get(ANN_ATTR_TERMINATE));
		String retrievedFrom = annotationInfoMap.get(ANN_ATTR_RETRIEVE_FROM);
		seeker.setRetrieveFrom((retrievedFrom.isEmpty() ? defaultRetrieveFrom : retrievedFrom));
		seeker.setMargedRows(Boolean.valueOf(annotationInfoMap.get(ANN_ATTR_MARGED_ROWS)));
		seeker.setUse(annotationInfoMap.get(ANN_ATTR_USE));
		seeker.setListClass(annotationInfoMap.get(ANN_ATTR_LIST_CLASS));

		ExcelPOJOBridge excelPOJOBridge = new ExcelPOJOBridge();
		excelPOJOBridge.setTargetClass(annotationInfoMap.get(ANN_ATTR_TARGET_CLASS));

		// 読み込み情報の取得
		setRepeatssetRepeatsSeekerField(excelPOJOBridge, annotationInfoMap, defaultRetrieveFrom, defaultDataDirection);
		seeker.setExcelPOJOBridge(excelPOJOBridge);
	}

	private void setRepeatssetRepeatsSeekerField(ExcelPOJOBridge excelPOJOBridge,
											HashMap<String, String> annotationInfoMap,
											String defaultRetrieveFrom, String defaultDataDirection
										) throws ClassNotFoundException, LinkageError {
		// 読み込み情報の取得
		HashMap<String, AbstractCellSeeker> elementClassProperties = excelPOJOBridge.getTargetClassProperties();
		Class<?> elementClass = ClassUtils.forName(annotationInfoMap.get(ANN_ATTR_TARGET_CLASS));
		for (Field field : elementClass.getDeclaredFields()) {
			for (Annotation fieldAnnotation : field.getAnnotations()) {
				if (elementClassProperties.containsKey(field.getName())) {
					continue;	// XMLでセットされていれば無視
				}

				AbstractCellSeeker fieldSeeker = buildCellSeeker(fieldAnnotation);
				fieldSeeker.set(fieldAnnotation, this);
				// 表から取得させるため、取得元情報を上書きする。
				if (fieldSeeker instanceof LabeledCellSeeker) {
					((LabeledCellSeeker)fieldSeeker).setRetrieveFrom(defaultRetrieveFrom);
				} else if (fieldSeeker instanceof MappedCellSeeker) {
					((MappedCellSeeker)fieldSeeker).setRetrieveFrom(defaultRetrieveFrom);
					((MappedCellSeeker)fieldSeeker).setDataDirection(defaultDataDirection);
				}
				elementClassProperties.put(field.getName(), fieldSeeker);
			}
		}
	}

	private AbstractCellSeeker buildCellSeeker(Annotation annotation) {
		AbstractCellSeeker seeker = null;
		if (annotation instanceof LabeledCell) {
			seeker = new LabeledCellSeeker();
		} else if (annotation instanceof PointedCell) {
			seeker = new PointedCellSeeker();
		} else if (annotation instanceof ConstantValue) {
			seeker = new ConstantValueCellSeeker();
		} else if (annotation instanceof VerticalRepeats) {
			seeker = new VerticalRepeatsSeeker();
		} else if (annotation instanceof HorizontalRepeats) {
			seeker = new HorizontalRepeatsSeeker();
		} else if (annotation instanceof MappedCell) {
			seeker = new MappedCellSeeker();
		}
		return seeker;
	}

	public String getTargetClass() {
	    return targetClass;
	}
	public void setTargetClass(String targetClass) {
	    this.targetClass = targetClass;
	}

}
