package net.w_horse.excelpojo.excel.cellseeker;

import java.lang.annotation.Annotation;

import net.w_horse.excelpojo.ExcelPOJOException;
import net.w_horse.excelpojo.annotation.ExcelPOJOAnnotationParser;
import net.w_horse.excelpojo.excel.CellNotFoundException;
import net.w_horse.excelpojo.excel.ExcelUtils;
import net.w_horse.excelpojo.xml.tag.RetrieveFrom;
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;

public class LabeledCellSeeker extends AbstractCellSeeker {

	private String label;
	private String retrieveFrom;
	private String margedLabel;
	private boolean margedRows = false;

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

	@Override
	public boolean verify() throws IllegalArgumentException, ExcelPOJOException {
		super.verify();
		if ((getLabel() == null) || getLabel().isEmpty()) {
			throw new IllegalArgumentException("The label is not specified.");
		}
		return true;
	}

	@Override
	public Object seekCellValue(Sheet sheet, Class<?> requiredType) throws ExcelPOJOException {
		return getCellValue(sheet, seekCellPosition(sheet), requiredType);
	}

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

	@Override
	protected Offset seekCellPosition(Sheet sheet, Offset basePosition, Offset offset) throws ExcelPOJOException {
		Offset newOffset = parseOffset(offset);
		int rowIndex = getStartRowIndex(sheet.getFirstRowNum(), basePosition.rowIndex);
		int rowIndexlimit = getLimitRowIndex(sheet.getLastRowNum());
		for (; rowIndex <= rowIndexlimit; rowIndex++) {
			Row row = sheet.getRow(rowIndex);
			if (row == null) continue;

			int colIndex = getStartColIndex(row.getFirstCellNum() -1, basePosition.colIndex);
			int colIndexLimit = getLimitColumnIndex(row.getLastCellNum());
			for (; colIndex <= colIndexLimit; colIndex++) {
				Cell cell = row.getCell(colIndex);
				if (cell == null) continue;

				if (ExcelUtils.getCellValue(cell, String.class).equals(getLabel())) {
					return new Offset(cell.getRowIndex() + newOffset.rowIndex,
								cell.getColumnIndex() + newOffset.colIndex);
				}
			}
		}
		if (Use.equalsIgnoreCase(Use.REQUIRED, getUse())) {
			throw new CellNotFoundException("label='" + getLabel() + "'");
		}

		return null;
	}
	@Override
	public void setValue(Sheet sheet, Object value) throws ExcelPOJOException {
		setCellValue(getCell(sheet, seekCellPosition(sheet)), value, getConverter());
	}

	@Override
	public void setValue(Sheet sheet, Offset basePosition,
			Offset offset, Object value) throws ExcelPOJOException {
		setCellValue(getCell(sheet, seekCellPosition(sheet, basePosition, offset)), value, getConverter());
	}

	@Override
	public Object clone() throws CloneNotSupportedException {
		LabeledCellSeeker copy = new LabeledCellSeeker();
		copy.setLabel(getLabel());
		copy.setRetrieveFrom(getRetrieveFrom());
		copy.setMargedLabel(getMargedLabel());
		copy.setMargedRows(isMargedRows());
		copy.setRange(getRange());
		copy.setUse(getUse());

		return copy;
	}

	public void setLabel(String label) {
		this.label = label;
	}

	public String getLabel() {
		return label;
	}

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

	public String getRetrieveFrom() {
		return retrieveFrom;
	}

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

	public String getMargedLabel() {
		return margedLabel;
	}

	public boolean isMargedRows() {
	    return margedRows;
	}

	public void setMargedRows(boolean margedRows) {
		this.margedRows = margedRows;
	}

	private Offset parseOffset(Offset offset) {
		Offset newOoffset;

		String retrieveFrom = getRetrieveFrom();
		int margedLabel = 0;
		if ((getMargedLabel() != null) && !getMargedLabel().isEmpty()) {
			margedLabel = Integer.parseInt(getMargedLabel());
		}
		if (RetrieveFrom.equalsIgnoreCase(RetrieveFrom.LEFT, retrieveFrom)) {
			newOoffset = new Offset(margedLabel, -1);
		} else if (RetrieveFrom.equalsIgnoreCase(RetrieveFrom.RIGHT, retrieveFrom)) {
			newOoffset = new Offset(margedLabel, 1);
		} else {	// RETRIEVE_FROM_BOTTOM
			newOoffset = new Offset(1, margedLabel);
		}
		newOoffset.rowIndex += offset.rowIndex;
		newOoffset.colIndex += offset.colIndex;

		return newOoffset;
	}


}
