package com.qmth.boot.tools.excel.handler.impl;

import com.qmth.boot.tools.excel.convertor.ValueConvertors;
import com.qmth.boot.tools.excel.exception.ColumnNameException;
import com.qmth.boot.tools.excel.handler.RowDataHandler;
import com.qmth.boot.tools.excel.listener.ObjectListener;
import com.qmth.boot.tools.excel.model.FieldParam;
import com.qmth.boot.tools.excel.model.ObjectParam;
import com.qmth.boot.tools.excel.model.RowData;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class ObjectHandler<E> implements RowDataHandler {

    private ObjectListener<E> listener;

    private Class<E> objectType;

    private ObjectParam objectParam;

    private int columnNameRow;

    private String[] columnNames;

    public ObjectHandler(Class<E> objectType, int columnNameRow, ObjectListener<E> listener) {
        this.listener = listener;
        this.objectType = objectType;
        this.objectParam = ObjectParam.get(objectType);
        this.columnNameRow = columnNameRow;
    }

    @Override
    public String[] getColumnNames() {
        return columnNames;
    }

    @Override
    public void handle(RowData rowData) {
        if (rowData.getRow() == columnNameRow && columnNames == null) {
            buildColumnNames(rowData);
        } else if (rowData.getRow() > columnNameRow && columnNames != null) {
            convertObject(rowData);
        }
    }

    private void buildColumnNames(RowData rowData) {
        String[] names = rowData.getValues();
        Collection<String> errors = objectParam.validate(names);
        if (errors.isEmpty()) {
            columnNames = names;
        } else {
            throw new ColumnNameException(errors);
        }
    }

    private void convertObject(RowData rowData) {
        E obj;
        try {
            obj = objectType.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("Create instance error", e);
        }
        List<String> errorColumn = new ArrayList<>();
        for (int i = 0; i < columnNames.length; i++) {
            FieldParam param = objectParam.get(columnNames[i]);
            String value = rowData.getValue(i);
            if (param != null) {
                try {
                    Object parseValue = value != null ?
                            ValueConvertors.getConvertor(param.getField().getType()).parse(value, param) :
                            null;
                    if (parseValue == null && !param.getAnnotation().nullable()) {
                        errorColumn.add(columnNames[i]);
                    } else {
                        param.getField().set(obj, parseValue);
                    }
                } catch (Exception e) {
                    errorColumn.add(columnNames[i]);
                }
            }
        }
        if (errorColumn.isEmpty()) {
            listener.output(obj);
        } else {
            listener.error(rowData.getRow(), errorColumn);
        }
    }

}
