package cn.report.excel.easyexcel.api.write.property;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import cn.report.excel.easyexcel.api.annotation.write.style.ColumnWidth;
import cn.report.excel.easyexcel.api.annotation.write.style.ContentLoopMerge;
import cn.report.excel.easyexcel.api.annotation.write.style.ContentRowHeight;
import cn.report.excel.easyexcel.api.annotation.write.style.HeadFontStyle;
import cn.report.excel.easyexcel.api.annotation.write.style.HeadRowHeight;
import cn.report.excel.easyexcel.api.annotation.write.style.HeadStyle;
import cn.report.excel.easyexcel.api.annotation.write.style.OnceAbsoluteMerge;
import cn.report.excel.easyexcel.api.enums.HeadKindEnum;
import cn.report.excel.easyexcel.api.metadata.CellRange;
import cn.report.excel.easyexcel.api.metadata.Head;
import cn.report.excel.easyexcel.api.metadata.Holder;
import cn.report.excel.easyexcel.api.metadata.property.ColumnWidthProperty;
import cn.report.excel.easyexcel.api.metadata.property.ExcelHeadProperty;
import cn.report.excel.easyexcel.api.metadata.property.FontProperty;
import cn.report.excel.easyexcel.api.metadata.property.LoopMergeProperty;
import cn.report.excel.easyexcel.api.metadata.property.OnceAbsoluteMergeProperty;
import cn.report.excel.easyexcel.api.metadata.property.RowHeightProperty;
import cn.report.excel.easyexcel.api.metadata.property.StyleProperty;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;

/**
 * Define the header attribute of excel
 *
 * @author jipengfei
 */
@Getter
@Setter
@EqualsAndHashCode
public class ExcelWriteHeadProperty extends ExcelHeadProperty {

    private RowHeightProperty headRowHeightProperty;
    private RowHeightProperty contentRowHeightProperty;
    private OnceAbsoluteMergeProperty onceAbsoluteMergeProperty;

    public ExcelWriteHeadProperty(Holder holder, Class<?> headClazz, List<List<String>> head) {
        super(holder, headClazz, head);
        if (getHeadKind() != HeadKindEnum.CLASS) {
            return;
        }
        this.headRowHeightProperty =
            RowHeightProperty.build(headClazz.getAnnotation(HeadRowHeight.class));
        this.contentRowHeightProperty =
            RowHeightProperty.build(headClazz.getAnnotation(ContentRowHeight.class));
        this.onceAbsoluteMergeProperty =
            OnceAbsoluteMergeProperty.build(headClazz.getAnnotation(OnceAbsoluteMerge.class));

        ColumnWidth parentColumnWidth = headClazz.getAnnotation(ColumnWidth.class);
        HeadStyle parentHeadStyle = headClazz.getAnnotation(HeadStyle.class);
        HeadFontStyle parentHeadFontStyle = headClazz.getAnnotation(HeadFontStyle.class);


        for (Map.Entry<Integer, Head> entry : getHeadMap().entrySet()) {
            Head headData = entry.getValue();
            if (headData == null) {
                throw new IllegalArgumentException(
                    "Passing in the class and list the head, the two must be the same size.");
            }
            Field field = headData.getField();

            ColumnWidth columnWidth = field.getAnnotation(ColumnWidth.class);
            if (columnWidth == null) {
                columnWidth = parentColumnWidth;
            }
            headData.setColumnWidthProperty(ColumnWidthProperty.build(columnWidth));


            HeadStyle headStyle = field.getAnnotation(HeadStyle.class);
            if (headStyle == null) {
                headStyle = parentHeadStyle;
            }
            headData.setHeadStyleProperty(StyleProperty.build(headStyle));

            HeadFontStyle headFontStyle = field.getAnnotation(HeadFontStyle.class);
            if (headFontStyle == null) {
                headFontStyle = parentHeadFontStyle;
            }
            headData.setHeadFontProperty(FontProperty.build(headFontStyle));

            headData.setLoopMergeProperty(LoopMergeProperty.build(field.getAnnotation(ContentLoopMerge.class)));
        }
    }

    /**
     * Calculate all cells that need to be merged
     *
     * @return cells that need to be merged
     */
    public List<CellRange> headCellRangeList() {
        List<CellRange> cellRangeList = new ArrayList<CellRange>();
        Set<String> alreadyRangeSet = new HashSet<String>();
        List<Head> headList = new ArrayList<Head>(getHeadMap().values());
        for (int i = 0; i < headList.size(); i++) {
            Head head = headList.get(i);
            List<String> headNameList = head.getHeadNameList();
            for (int j = 0; j < headNameList.size(); j++) {
                if (alreadyRangeSet.contains(i + "-" + j)) {
                    continue;
                }
                alreadyRangeSet.add(i + "-" + j);
                String headName = headNameList.get(j);
                int lastCol = i;
                int lastRow = j;
                for (int k = i + 1; k < headList.size(); k++) {
                    String key = k + "-" + j;
                    if (headList.get(k).getHeadNameList().get(j).equals(headName) && !alreadyRangeSet.contains(key)) {
                        alreadyRangeSet.add(key);
                        lastCol = k;
                    } else {
                        break;
                    }
                }
                Set<String> tempAlreadyRangeSet = new HashSet<>();
                outer:
                for (int k = j + 1; k < headNameList.size(); k++) {
                    for (int l = i; l <= lastCol; l++) {
                        String key = l + "-" + k;
                        if (headList.get(l).getHeadNameList().get(k).equals(headName) && !alreadyRangeSet.contains(
                            key)) {
                            tempAlreadyRangeSet.add(l + "-" + k);
                        } else {
                            break outer;
                        }
                    }
                    lastRow = k;
                    alreadyRangeSet.addAll(tempAlreadyRangeSet);
                }
                if (j == lastRow && i == lastCol) {
                    continue;
                }
                cellRangeList
                    .add(new CellRange(j, lastRow, head.getColumnIndex(), headList.get(lastCol).getColumnIndex()));
            }
        }
        return cellRangeList;
    }
}
