/*******************************************************************************
 * Copyright 2017 Bstek
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy
 * of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 ******************************************************************************/
package com.bstek.ureport.export.builder.down;

import com.bstek.ureport.Range;
import com.bstek.ureport.definition.BlankCellInfo;
import com.bstek.ureport.definition.CellDefinition;
import com.bstek.ureport.parser.BuildUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author Jacky.gao
 * @since 2017年2月27日
 */
public class LeftParentCellCreator {
    public List<Range> buildParentCells(CellDefinition cell) {
        List<Range> rangeList = new ArrayList<>();
        Range childRange = buildChildrenCellRange(cell);
        List<CellDefinition> parentCells = new ArrayList<>();
        collectParentCells(cell, parentCells);
        buildParents(cell, parentCells, childRange, rangeList);
        return rangeList;
    }

    private void collectParentCells(CellDefinition cell, List<CellDefinition> parentCells) {
        CellDefinition leftParentCell = cell.getLeftParentCell();
        if (leftParentCell == null) {
            return;
        }
        parentCells.add(leftParentCell);
        collectParentCells(leftParentCell, parentCells);
    }

    private void buildParents(CellDefinition mainCell, List<CellDefinition> parentCells, Range childRange, List<Range> rangeList) {
        int rowNumberStart = mainCell.getRowNumber();
        int rowNumberEnd = BuildUtils.buildRowNumberEnd(mainCell, rowNumberStart);
        rangeList.add(new Range(rowNumberStart, rowNumberEnd));

        int start = childRange.getStart(), end = childRange.getEnd();
        Map<String, BlankCellInfo> newBlankCellsMap = mainCell.getNewBlankCellsMap();
        boolean increase = true;
        for (CellDefinition parentCell : parentCells) {
            String parentCellName = parentCell.getName();
            int parentRowNumberStart = parentCell.getRowNumber();
            int parentRowNumberEnd = BuildUtils.buildRowNumberEnd(parentCell, parentRowNumberStart);
            int offset = parentRowNumberStart - rowNumberStart;
            int parentRowSpan = parentCell.getRowSpan();
            boolean isOut = assertOut(parentCell, mainCell, childRange);
            if (isOut) {
                increase = false;
                boolean doBlank = assertDoBlank(parentCell.getLeftParentCell(), parentCell, mainCell, childRange);
                if (doBlank) {
                    newBlankCellsMap.put(parentCellName, new BlankCellInfo(offset, parentRowSpan, true));
                    rangeList.add(new Range(parentRowNumberStart, parentRowNumberEnd));
                }
                continue;
            }
            if ((start != -1 && start < parentRowNumberStart) || end > parentRowNumberEnd) {
                newBlankCellsMap.put(parentCellName, new BlankCellInfo(offset, parentRowSpan, true));
                rangeList.add(new Range(parentRowNumberStart, parentRowNumberEnd));
                increase = false;
                continue;
            }
            if (increase) {
                mainCell.getIncreaseSpanCellNames().add(parentCellName);
            } else {
                newBlankCellsMap.put(parentCellName, new BlankCellInfo(offset, parentRowSpan, true));
                rangeList.add(new Range(parentRowNumberStart, parentRowNumberEnd));
            }
        }
    }

    private boolean assertDoBlank(CellDefinition nextParentCell, CellDefinition parentCell, CellDefinition mainCell, Range childRange) {
        if (nextParentCell == null) {
            return false;
        }
        boolean isOut = assertOut(nextParentCell, mainCell, childRange);
        if (isOut) {
            return assertDoBlank(nextParentCell.getLeftParentCell(), parentCell, mainCell, childRange);
        }
        int start = parentCell.getRowNumber(), end = BuildUtils.buildRowNumberEnd(parentCell, start);
        int nextStart = nextParentCell.getRowNumber();
        if (nextStart <= end) {
            return true;
        }
        return assertDoBlank(nextParentCell.getLeftParentCell(), parentCell, mainCell, childRange);
    }

    private boolean assertOut(CellDefinition parentCell, CellDefinition mainCell, Range childRange) {
        int start = parentCell.getRowNumber(), end = BuildUtils.buildRowNumberEnd(parentCell, start);
        int rangeStart = childRange.getStart(), rangeEnd = childRange.getEnd();
        if (rangeStart != -1) {
            if ((start >= rangeStart && start <= rangeEnd) || (end >= rangeStart && end <= rangeEnd)) {
                return false;
            }
        }
        int rowStart = mainCell.getRowNumber(), rowEnd = BuildUtils.buildRowNumberEnd(mainCell, rowStart);
        return (start < rowStart || start > rowEnd) && (end < rowStart || end > rowEnd) && (start > rowStart || end < rowEnd);
    }

    private Range buildChildrenCellRange(CellDefinition mainCell) {
        Range range = new Range();
        List<CellDefinition> childrenCells = mainCell.getRowChildrenCells();
        for (CellDefinition childCell : childrenCells) {
            int childRowNumberStart = childCell.getRowNumber();
            int childRowSpan = childCell.getRowSpan();
            childRowSpan = childRowSpan > 0 ? childRowSpan - 1 : childRowSpan;
            int childRowNumberEnd = childRowNumberStart + childRowSpan;
            if (range.getStart() == -1 || childRowNumberStart < range.getStart()) {
                range.setStart(childRowNumberStart);
            }
            if (childRowNumberEnd > range.getEnd()) {
                range.setEnd(childRowNumberEnd);
            }
        }
        return range;
    }
}
