package net.w_horse.excelpojo.excel.cellseeker;

import java.lang.annotation.Annotation;
import java.util.LinkedHashMap;
import java.util.Map;

import net.w_horse.excelpojo.ExcelPOJOException;
import net.w_horse.excelpojo.annotation.ExcelPOJOAnnotationParser;
import net.w_horse.excelpojo.xml.tag.DataDirection;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;

public class MappedCellSeeker extends AbstractCellSeeker {
	private static final String DATA_DIRECTION_DOWN = DataDirection.DOWN.getValue();
	private static final String DATA_DIRECTION_RIGHT = DataDirection.RIGHT.getValue();

	private String previousLabel;
	private String position;
	private String retrieveFrom;
	private String terminate;
	private String margedLabel;
	private String dataDirection = DATA_DIRECTION_RIGHT;

	@Override
	public void set(Annotation annotation, ExcelPOJOAnnotationParser parser) {
		parser.setCellSeeker(annotation, this);
	}

	@Override
	public boolean verify() throws IllegalArgumentException, ExcelPOJOException {
		super.verify();
		if (((getPreviousLabel() == null) || getPreviousLabel().isEmpty())
			&& ((getPosition() == null) || getPosition().isEmpty())
		) {
			throw new IllegalArgumentException("Neither the label nor the position are specified.");
		}
		return true;
	}

	@Override
	public Map<String, Object> seekCellValue(Sheet sheet, Class<?> requiredType)
							throws ClassNotFoundException, LinkageError, IllegalArgumentException, ExcelPOJOException {
		return seekCellValue(sheet, new Offset(0, 0), new Offset(0, 0), new Offset(0, 0));
	}

	protected Map<String, Object> seekCellValue(Sheet sheet, Offset basePosition,
							Offset offset, Offset rowOffset
							) throws ClassNotFoundException, LinkageError, IllegalArgumentException, ExcelPOJOException {
		Map<String, Object> records = new LinkedHashMap<String, Object>();
		// Mapの先頭位置を検索
		Offset mapBasePosition = seekCellPosition(sheet, basePosition, offset);
		if (mapBasePosition == null) return records;

		int rowLimit = getRowLimit(sheet, mapBasePosition);
		for (int mapRowIndex = 0; mapRowIndex <= rowLimit; mapRowIndex++) {
			Offset offsetKey = getOffsetKey(mapRowIndex);
			Offset offsetValue = getOffsetValue(mapRowIndex).add(rowOffset);
			String key = getCellValue(sheet, mapBasePosition, offsetKey, String.class);
			if ((key == null) || key.isEmpty()) break;

			String value = getCellValue(sheet, mapBasePosition, offsetValue, String.class);
			records.put(key, value);
		}

		return records;
	}

	@Override
	protected Offset seekCellPosition(Sheet sheet) throws IllegalArgumentException, ExcelPOJOException {
		return seekCellPosition(sheet, new Offset(), new Offset());
	}

	@Override
	protected Offset seekCellPosition(Sheet sheet, Offset basePosition, Offset offset) throws IllegalArgumentException, ExcelPOJOException {
		AbstractCellSeeker mapBaseSeeker;
		if (!getPreviousLabel().isEmpty()) {
			LabeledCellSeeker seeker = new LabeledCellSeeker();
			seeker.setLabel(getPreviousLabel());
			seeker.setMargedLabel(getMargedLabel());
			seeker.setRetrieveFrom(getRetrieveFrom());
			seeker.setUse(getUse());
			seeker.setRange(getRange());
			mapBaseSeeker = seeker;
		} else {
			PointedCellSeeker seeker = new PointedCellSeeker();
			seeker.setPosition(getPosition());
			seeker.setUse(getUse());
			mapBaseSeeker = seeker;
		}
		return mapBaseSeeker.seekCellPosition(sheet, basePosition, offset);
	}

	@SuppressWarnings("unchecked")
	@Override
	public void setValue(Sheet sheet, Object value) throws IllegalArgumentException, ExcelPOJOException {
		Offset mapBasePosition = seekCellPosition(sheet);

		int mapRowIndex = 0;
		for (String key : ((Map<String, String>)value).keySet()) {
			String targetValue = ((Map<String, String>)value).get(key);
			Cell keyCell = createCell(sheet, mapBasePosition, getOffsetKey(mapRowIndex));
			setCellValue(keyCell, key, getConverter());
			Cell valueCell = createCell(sheet, mapBasePosition, getOffsetValue(mapRowIndex));
			setCellValue(valueCell, targetValue, getConverter());
			mapRowIndex++;
		}
	}

	@Override
	public void setValue(Sheet sheet, Offset basePosition,
			Offset offset, Object value) {
	}

	@SuppressWarnings("unchecked")
	protected void setValue(Sheet sheet, Offset basePosition,
			Offset offset, Offset rowOffset, Object value) throws IllegalArgumentException, ExcelPOJOException {
		Offset mapBasePosition = seekCellPosition(sheet, basePosition, offset);

		int mapRowIndex = 0;
		for (String key : ((Map<String, String>)value).keySet()) {
			Cell keyCell = createCell(sheet, mapBasePosition, getOffsetKey(mapRowIndex));
			setCellValue(keyCell, key, getConverter());
			Cell valueCell = createCell(sheet, mapBasePosition, getOffsetValue(mapRowIndex).add(rowOffset));
			setCellValue(valueCell, ((Map<String, String>)value).get(key), getConverter());
			mapRowIndex++;
		}
	}

	public void setPreviousLabel(String previousLabel) {
		this.previousLabel = previousLabel;
	}

	public String getPreviousLabel() {
		return previousLabel;
	}

	public void setPosition(String position) {
		this.position = position;
	}

	public String getPosition() {
		return position;
	}

	public void setRetrieveFrom(String retrieveFrom) {
		this.retrieveFrom = retrieveFrom;
	}

	public String getRetrieveFrom() {
		return retrieveFrom;
	}

	public void setTerminate(String terminate) {
		this.terminate = terminate;
	}

	public String getTerminate() {
		return terminate;
	}

	public void setMargedLabel(String margedLabel) {
		this.margedLabel = margedLabel;
	}

	public String getMargedLabel() {
		return margedLabel;
	}

	public void setDataDirection(String dataDirection) {
		this.dataDirection = dataDirection;
	}

	public String getDataDirection() {
		return dataDirection;
	}


	private int getRowLimit(Sheet sheet, Offset baseOffset) {
		if (getDataDirection().equals(DATA_DIRECTION_DOWN)) {
			return sheet.getLastRowNum() - baseOffset.rowIndex;
		}
        int colSizeMax = 0;
        for (int i = 0; i < sheet.getLastRowNum(); i++) {
            Row row = sheet.getRow(i);
            if (row == null) continue;

            int colSize = row.getLastCellNum();
            if (colSize > colSizeMax) {
                colSizeMax = colSize;
            }
        }
		return colSizeMax - baseOffset.colIndex;
	}

	private Offset getOffsetKey(int rowIndex) {
		Offset offset;
		if (getDataDirection().equals(DATA_DIRECTION_DOWN)) {
			offset = new Offset(rowIndex, 0);
		} else {
			offset = new Offset(0, rowIndex);
		}
		return offset;
	}
	private Offset getOffsetValue(int rowIndex) {
		Offset offset;
		if (getDataDirection().equals(DATA_DIRECTION_DOWN)) {
			offset = new Offset(rowIndex, 1);
		} else {
			offset = new Offset(1, rowIndex);
		}
		return offset;
	}

	public void setMargedRows(boolean connectedRows) {
	}


}
