package net.w_horse.excelpojo.excel.cellseeker;

import java.util.regex.Pattern;

import net.w_horse.excelpojo.ExcelPOJOBridge;
import net.w_horse.excelpojo.ExcelPOJOException;
import net.w_horse.excelpojo.converter.CellFieldConverter;
import net.w_horse.excelpojo.converter.NothingConverter;
import net.w_horse.excelpojo.excel.CellNotFoundException;
import net.w_horse.excelpojo.excel.ExcelUtils;
import net.w_horse.excelpojo.xml.tag.Use;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.util.StringUtils;

public abstract class AbstractCellSeeker implements CellSeeker {
	private ExcelPOJOBridge excelPOJOBridge;
	private String use = Use.NONE.getValue();
	private String range;
	private CellFieldConverter converter = new NothingConverter();

	protected abstract Offset seekCellPosition(Sheet sheet) throws CellNotFoundException, IllegalArgumentException, ExcelPOJOException;
	protected abstract Offset seekCellPosition(Sheet sheet, Offset basePosition, Offset offset) throws CellNotFoundException, IllegalArgumentException, ExcelPOJOException;

	@Override
	public boolean verify() throws IllegalArgumentException, ExcelPOJOException {
		Pattern pattern = Pattern.compile("\\(\\s*-?[0-9]+\\s*,\\s*-?[0-9]+\\s*\\)-\\(\\s*-?[0-9]+\\s*,\\s*-?[0-9]+\\s*\\)");
		if (StringUtils.hasLength(getRange()) && !pattern.matcher(getRange()).matches()) {
			throw new IllegalArgumentException("There is a mistake in the format of the range specification. \n"
					+ "Format:(startRowIndex, startColIndex)-(endRowIndex, endColIndex)  rowIndex={1,2,3,...}, colIndex={1,2,3,...}");
		}
		return true;
	}

	protected <T>T getCellValue(Sheet sheet, Offset cellPosition, Class<T> requiredType) throws ExcelPOJOException {
		return getCellValue(sheet, cellPosition, new Offset(), requiredType);
	}

	protected <T>T getCellValue(Sheet sheet, Offset basePosition, Offset offset, Class<T> requiredType) throws ExcelPOJOException {
		return getCellValue(getCell(sheet, basePosition, offset), requiredType);
	}

	protected <T>T getCellValue(Cell cell, Class<T> requiredType) throws ExcelPOJOException {
		return ExcelUtils.getCellValue(cell, requiredType, getConverter());
	}
	protected Cell createCell(Sheet sheet, Offset basePosition, Offset offset) {
		Row row = sheet.getRow(basePosition.rowIndex + offset.rowIndex);
		if (row == null) {
			row = sheet.createRow(basePosition.rowIndex + offset.rowIndex);
		}
		Cell cell = row.getCell(basePosition.colIndex + offset.colIndex);
		if (cell == null) {
			cell = row.createCell(basePosition.colIndex + offset.colIndex);
		}
		return cell;
	}

	protected Cell getCell(Sheet sheet, Offset cellPosition) {
		return getCell(sheet, cellPosition, new Offset());
	}
	protected Cell getCell(Sheet sheet, Offset basePosition, Offset offset) {
		if (basePosition == null) return null;

		return createCell(sheet, basePosition, offset);
	}

	protected void setCellValue(Cell cell, Object value, CellFieldConverter converter) {
		ExcelUtils.setCellValue(cell, value, converter);
	}

	@Override
	public void setExcelPOJOBridge(ExcelPOJOBridge excelPOJOBridge) {
		this.excelPOJOBridge = excelPOJOBridge;
	}
	@Override
	public ExcelPOJOBridge getExcelPOJOBridge() {
		return excelPOJOBridge;
	}

	@Override
	public void setUse(String use) {
		this.use = use;
	}

	@Override
	public String getUse() {
		return use;
	}

	@Override
	public void setRange(String range) {
		this.range = range;
	}
	@Override
	public String getRange() {
		return range;
	}

	@Override
	public void setConverter(CellFieldConverter converter) {
		this.converter = converter;
	}
	@Override
	public CellFieldConverter getConverter() {
		return converter;
	}

	protected class Offset {
		public int rowIndex = 0;
		public int colIndex = 0;

		public Offset() {
		}
		public Offset(Offset offset) {
			this.rowIndex = offset.rowIndex;
			this.colIndex = offset.colIndex;
		}
		public Offset(int rowIndex, int colIndex) {
			this.rowIndex = rowIndex;
			this.colIndex = colIndex;
		}

		public Offset add(Offset offset) {
			this.rowIndex += offset.rowIndex;
			this.colIndex += offset.colIndex;

			return this;
		}
		public Offset negate() {
			return new Offset(-this.rowIndex, -this.colIndex);
		}
	}

	protected Offset getRangeStartCell() {
		return convRangeString2Offset(true);
	}
	protected Offset getRangeEndCell() {
		return convRangeString2Offset(false);
	}
	private Offset convRangeString2Offset(boolean start) {
		String[] cellPositions = getRange().split("\\)-\\(");
		String cellPositionString = (start ? cellPositions[0] : cellPositions[1]);
		String[] xy = (start
						? cellPositionString.substring(1).split(",")
						: cellPositionString.substring(0, cellPositionString.length() -1).split(","));
		int rowIndex = Integer.valueOf(xy[0].trim()) -1;
		int colIndex = Integer.valueOf(xy[1].trim()) -1;

		return new Offset(rowIndex, colIndex);
	}
	protected String convertOffset2RangeString(Offset basePosition, Offset range) {
		int startRowIndex = basePosition.rowIndex;
		int startColumnIndex = basePosition.colIndex;
		int endRowIndex = (range.rowIndex < 0 ? -1 : startRowIndex + range.rowIndex);
		int endColumnIndex = (range.colIndex < 0 ? -1 : startColumnIndex + range.colIndex);

		String startCellStr = String.format("(%d,%d)", startRowIndex +1, startColumnIndex +1);
		String endCellStr = String.format("(%d,%d)", endRowIndex +1, endColumnIndex +1);

		return startCellStr + "-" + endCellStr;
	}
	protected int getStartRowIndex(int sheetFirstRowNum, int basePositionRowIndex) {
		int rowIndex = (sheetFirstRowNum > basePositionRowIndex
							? sheetFirstRowNum : basePositionRowIndex);
		if (StringUtils.hasLength(getRange()) && (getRangeStartCell().rowIndex > 0)) {
			rowIndex = (rowIndex > getRangeStartCell().rowIndex
							? rowIndex : getRangeStartCell().rowIndex);
		}
		return rowIndex;
	}
	protected int getLimitRowIndex(int sheetLastRowNum) {
		int limitRowIndex = sheetLastRowNum;
		if (StringUtils.hasLength(getRange()) && (getRangeEndCell().rowIndex > 0)) {
			limitRowIndex = (limitRowIndex < getRangeEndCell().rowIndex
								? limitRowIndex : getRangeEndCell().rowIndex);
		}
		return limitRowIndex;
	}
	protected int getStartColIndex(int rowFirstCellNum, int basePositionColumnIndex) {
		int colIndex = (rowFirstCellNum > basePositionColumnIndex
							? rowFirstCellNum : basePositionColumnIndex);
		if (StringUtils.hasLength(getRange()) && (getRangeStartCell().colIndex > 0)) {
			colIndex = (colIndex > getRangeStartCell().colIndex
							? colIndex : getRangeStartCell().colIndex);
		}
		return colIndex;
	}
	protected int getLimitColumnIndex(int rowLastCellNum) {
		int colIndexLimit = rowLastCellNum;
		if (StringUtils.hasLength(getRange()) && (getRangeEndCell().colIndex > 0)) {
			colIndexLimit = (colIndexLimit < getRangeEndCell().colIndex
								? colIndexLimit : getRangeEndCell().colIndex);
		}
		return colIndexLimit;
	}

}
