package com.smppw.utils;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Pair;

import java.util.Comparator;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * range工具类
 *
 * @author luohq
 * @date 2020/08/31
 * @see [https://blog.csdn.net/luo15242208310/article/details/108355939](Java range工具类)
 */
public final class RangeUtils {

    private RangeUtils() {
    }

    /**
     * 整数相除，对商进行向上取整
     *
     * @param dividend /
     * @param divisor  /
     * @return /
     */
    private static Integer ceilDivide(Integer dividend, Integer divisor) {
        int remainder = dividend % divisor;
        int quotient = dividend / divisor;
        return (0 == remainder ? quotient : quotient + 1);
    }

    /**
     * 生成整数的rangeStream<br/>
     * 注：要求start < endExlude，且step > 0 <br/>
     * <p>
     * 例:
     * <ul>
     *     <li>rangeStream(0, 10, 2) ==> 0, 2, 4, 6, 8</li>
     *     <li>rangeStream(0, 10, 3) ==> 0, 3, 6, 9</li>
     *     <li>rangeStream(0, 10, 4) ==> 0, 4, 8</li>
     * </ul>
     *
     * @param start      起始边界
     * @param endExclude 结束边界
     * @param step       步长
     * @return Stream&lt;Integer&gt;
     */
    public static Stream<Integer> rangeStream(Integer start, Integer endExclude, Integer step) {
        //校验参数
        checkStartAndEndIndex(start, endExclude, step);
        //计算批次数量
        Integer stepCount = ceilDivide((endExclude - start), step);
        //生成range stream
        return IntStream.range(0, stepCount).mapToObj(stepIndex -> stepIndex * step);
    }

    /**
     * 生成整数的rangeStream且返回具体索引(stepIndex, startIndex, endIndex)<br/>
     * 注：要求start < endExlude，且step > 0 <br/>
     * <p>
     * 例:
     * <ul>
     *     <li>rangeStream(0, 10, 2) ==> (0,0,1), (1,2,3), (2,4,5), (3,6,7), (4,8,9)</li>
     *     <li>rangeStream(0, 10, 3) ==> (0,0,2), (1,3,5), (2,6,8), (3,9,9)</li>
     *     <li>rangeStream(0, 10, 4) ==> (0,0,3), (1,4,7), (2,8,9)</li>
     * </ul>
     *
     * @param start      起始边界
     * @param endExclude 结束边界
     * @param step       步长
     * @param rangeEnum  开闭区间枚举
     * @param group      组数，一共这么多个组
     * @return Stream&lt;RangeIndex(rangeEnum, startIndex, endIndex)&gt;
     */
    public static Stream<RangeIndex> rangeStreamWithIndex(Integer start, Integer endExclude, Integer step, RangeEnum rangeEnum, int group) {
        //校验参数
        checkStartAndEndIndex(start, endExclude, step);
        //计算批次数量
        Integer stepCount = ceilDivide((endExclude - start), step);
        //生成range stream
        return IntStream.range(0, stepCount).mapToObj(stepIndex -> {
            Integer stepStartIndex = start + stepIndex * step;
            int stepEndIndex = endExclude;
            boolean legal = false;
            if (stepIndex <= group - 1) {
                legal = true;
                if (stepIndex < group - 1) {
                    stepEndIndex = Math.min(stepStartIndex + step, endExclude);
                }
            }
            return new RangeIndex(rangeEnum, stepIndex, stepStartIndex, stepEndIndex, legal);
        });
    }

    /**
     * 执行list的rangeStream，并同过consumer处理集合拆分后的子range集合<br/>
     *
     * @param list               列表集合
     * @param start              起始边界
     * @param endExclude         结束边界
     * @param step               步长
     * @param handlerRangeStream 处理每个range的consumer
     */
    public static void rangeStream(List<Integer> list, Integer start, Integer endExclude, Integer step, RangeEnum rangeEnum,
                                   int group, Consumer<List<Integer>> handlerRangeStream) {
        //校验集合
        checkCollection(list);
        //校验非空
        checkEmpty(handlerRangeStream, "consumer");
        //执行rangeStream
        rangeStreamWithIndex(start, endExclude, step, rangeEnum, group).forEach(rangeIndex -> {
            List<Integer> subList = list.subList(rangeIndex.getStartIndex(), rangeIndex.getStopIndexExclude());
            handlerRangeStream.accept(subList);
        });
    }

    /**
     * 执行list的rangeStream，并同过biConsumer处理集合拆分后的子range集合，同时返回具体索引(stepIndex, startIndex, endIndex)<br/>
     *
     * @param list               列表集合
     * @param start              起始边界
     * @param endExclude         结束边界
     * @param step               步长
     * @param handlerRangeStream 处理每个range的biConsumer
     */
    public static void rangeStream(List<Integer> list, Integer start, Integer endExclude, Integer step, RangeEnum rangeEnum,
                                   int group, BiConsumer<RangeIndex, List<Integer>> handlerRangeStream) {
        //校验集合
        checkCollection(list);
        //校验非空
        checkEmpty(handlerRangeStream, "consumer");
        //执行rangeStream
        rangeStreamWithIndex(start, endExclude, step, rangeEnum, group).forEach(rangeIndex -> {
            List<Integer> subList = list.subList(rangeIndex.getStartIndex(), rangeIndex.getStopIndexExclude());
            handlerRangeStream.accept(rangeIndex, subList);
        });
    }

    /**
     * 执行list的rangeStream，并同过consumer处理子range集合<br/>
     * 注：start=0, endExclue=list.size()
     *
     * @param list               列表集合
     * @param step               步长
     * @param handlerRangeStream 处理每个range的consumer
     */
    public static void rangeStream(List<Integer> list, Integer step, RangeEnum rangeEnum, int group,
                                   Consumer<List<Integer>> handlerRangeStream) {
        //校验集合
        checkCollection(list);
        //执行rangeStream（遍历整个list）
        rangeStream(list, 0, list.size(), step, rangeEnum, group, handlerRangeStream);
    }

    /**
     * 执行list的rangeStream，并同过biConsumer处理子range集合，且返回具体索引(stepIndex, startIndex, endIndex)<br/>
     * 注：start=0, endExclue=list.size()
     *
     * @param list               列表集合
     * @param step               步长
     * @param handlerRangeStream 处理每个range的biConsumer
     */
    public static void rangeStream(List<Integer> list, Integer step, RangeEnum rangeEnum, int group,
                                   BiConsumer<RangeIndex, List<Integer>> handlerRangeStream) {
        //校验集合
        checkCollection(list);
        //执行rangeStream（遍历整个list）
        rangeStream(list, 0, list.size(), step, rangeEnum, group, handlerRangeStream);
    }

    /**
     * 验证起止索引、step
     *
     * @param start       /
     * @param stopExclude /
     * @param step        /
     */
    private static void checkStartAndEndIndex(Integer start, Integer stopExclude, Integer step) {
        if (null == start || null == stopExclude || null == step) {
            throw new RuntimeException("自定义rangeStream起止索引、步长不能为空！");
        }
        if (0 > start || 0 > stopExclude || 0 >= step) {
            throw new RuntimeException("自定义rangeStream起止索引、步长(不可等于0)均需要>=0！");
        }
//        if (start >= stopExclude || step > (stopExclude - start)) {
//            throw new RuntimeException("自定义rangeStream起止索引、步长不符合规范（start < stopExclude 且 step <= (stopExclude - start)）");
//        }
    }

    /**
     * 验证集合非空
     *
     * @param collection /
     */
    private static void checkCollection(List<Integer> collection) {
        if (null == collection || 0 >= collection.size()) {
            throw new RuntimeException("自定义rangeStream 待处理集合不能为空！");
        }
    }

    /**
     * 验证对象非空
     *
     * @param obj     /
     * @param objDesc /
     */
    private static void checkEmpty(Object obj, String objDesc) {
        if (null == obj) {
            throw new RuntimeException("自定义rangeStream 待处理".concat(objDesc).concat("不能为空！"));
        }
    }

    public static List<Pair<Double, Double>> groupNumberLimitCondition(int groupNumber, double space, double minValue, double maxValue) {
        if (minValue == maxValue && maxValue == 0d) {
            return ListUtil.of(Pair.of(0d, 0d));
        }
        if (maxValue < minValue) {
            double temp = maxValue;
            maxValue = minValue;
            minValue = temp;
        }
        int group = ceilDivide(groupNumber, 2);
        int step = (int) (space * 10);
        int max = (int) Math.ceil(maxValue * 100 * 10);
        int min = (int) Math.floor(minValue * 100 * 10);
        List<RangeIndex> tempList = ListUtil.list(true);
        if (min >= 0) {
            // 最小值都大于0
            tempList = rangeStreamWithIndex(min, max, step, RangeEnum.LO_RC, group)
                    .filter(RangeIndex::getLegal)
                    .collect(Collectors.toList());
        } else if (max < 0) {
            // 最大值小于0，从 Math.abs(max) -> Math.abs(min)，range 步长
            tempList = rangeStreamWithIndex(Math.abs(max), Math.abs(min), step, RangeEnum.LC_RO, group)
                    .filter(RangeIndex::getLegal)
                    .collect(Collectors.toList());
            // 截止点和开始点交换，然后结果集根据stepIndex降序
            tempList = tempList.stream().map(e -> new RangeIndex(e.getStepIndex(), e.getStopIndexExclude() * -1, e.getStartIndex() * -1))
                    .sorted(Comparator.reverseOrder()).collect(Collectors.toList());
        } else {
            // 最小值和最大值在0两边，分别处理小于0和大于0的区间段
            // 左边，从 0 -> Math.abs(min)
            List<RangeIndex> left = rangeStreamWithIndex(0, Math.abs(min), step, RangeEnum.LC_RO, group)
                    .filter(RangeIndex::getLegal)
                    .collect(Collectors.toList());
            // 截止点和开始点交换，然后结果集根据stepIndex降序
            left = left.stream().map(e -> new RangeIndex(e.getStepIndex(), e.getStopIndexExclude() * -1, e.getStartIndex() * -1))
                    .sorted(Comparator.reverseOrder()).collect(Collectors.toList());
            // 右边，从 0 -> max
            List<RangeIndex> right = rangeStreamWithIndex(0, max, step, RangeEnum.LO_RC, group)
                    .filter(RangeIndex::getLegal)
                    .collect(Collectors.toList());
            tempList.addAll(left);
            tempList.addAll(right);
        }
        return tempList.stream().map(e -> Pair.of(e.getStartIndex() / 10d, e.getStopIndexExclude() / 10d)).collect(Collectors.toList());
    }

    public static void main(String[] args) {
        int start = 0, endExclude = 200;

        List<Pair<Double, Double>> pairs = groupNumberLimitCondition(10, 0.6, -1.3, 0.2);
        System.out.println(pairs);

        //设置step=2, 3, 4
        IntStream.range(2, 5).forEach(step -> {
            System.out.printf("<==== 测试rangeStream start=%s, endExclude=%s, step=%s ====>%n", start, endExclude, step);
            String indexStr = RangeUtils.rangeStream(start, endExclude, step)
                    .map(String::valueOf)
                    .collect(Collectors.joining(","));
            System.out.printf("start=%s, endExclude=%s, step=%s, indexArray=%s\n%n", start, endExclude, step, indexStr);
        });
        //设置step=2, 3, 4
        IntStream.range(2, 5).forEach(step -> {
            System.out.printf("<==== 测试rangeStreamWithIndex start=%s, endExclude=%s, step=%s ====>%n", start, endExclude, step);
            String indexStr = RangeUtils.rangeStreamWithIndex(start, endExclude, step, RangeEnum.LO_RC, 3)
                    .filter(RangeIndex::getLegal)
                    .map(RangeIndex::toString)
                    .collect(Collectors.joining(","));
            System.out.printf("start=%s, endExclude=%s, step=%s, indexArray=%s\n%n", start, endExclude, step, indexStr);
        });


        int step1 = 17;
        System.out.printf("<==== 测试rangeStreamWithIndex start=%s, endExclude=%s, step=%s ====>%n", start, endExclude, step1);
        String indexStr = RangeUtils.rangeStreamWithIndex(start, endExclude, step1, RangeEnum.LO_RC, 6)
                .filter(RangeIndex::getLegal)
                .map(RangeIndex::toString)
                .collect(Collectors.joining(","));
        System.out.printf("start=%s, endExclude=%s, step=%s, indexArray=%s\n%n", start, endExclude, 5, indexStr);

        //定义待拆分集合
        List<Integer> numList = IntStream.range(0, 10).boxed().collect(Collectors.toList());
        //设置step=2, 3, 4
        IntStream.range(2, 5).forEach(step -> {
            System.out.printf("<==== 测试rangeStream（仅获取拆分后列表） start=%s, endExclude=%s, step=%s ====>%n", start, endExclude, step);
            RangeUtils.rangeStream(numList, start, endExclude, step, RangeEnum.LC_RO, 4, (subNumList) -> {
                System.out.printf("%s%n", subNumList);
            });
            System.out.println();
        });

        //设置step=2, 3, 4
        IntStream.range(2, 5).forEach(step -> {
            System.out.printf("<==== 测试rangeStream（同时获取拆分索引和获取拆分后列表） start=%s, endExclude=%s, step=%s ====>%n", start, endExclude, step);
            RangeUtils.rangeStream(numList, start, endExclude, step, RangeEnum.LC_RO, 3, (rangeIndex, subNumList) -> {
                System.out.printf("%s=%s%n", rangeIndex, subNumList);
            });
            System.out.println();
        });
    }

    public enum RangeEnum {
        // 左开右闭
        LO_RC,
        // 左闭右开
        LC_RO,
        // 闭区间
        CLOSED,
        // 开区间
        OPEN;

        public static final List<RangeEnum> LEFT_OPEN = ListUtil.of(LO_RC, OPEN);
        public static final List<RangeEnum> RIGHT_OPEN = ListUtil.of(LC_RO, OPEN);
    }

    /**
     * range索引
     */
    public static class RangeIndex implements Comparable<RangeIndex> {
        private final Integer stepIndex;
        /**
         * 开闭区间枚举
         */
        private final RangeEnum rangeEnum;
        /**
         * 当前range的起始索引
         */
        private final Integer startIndex;
        /**
         * 当前range的结束索引
         */
        private final Integer stopIndexExclude;
        /**
         * 是否合法的，使用时过滤不合法的区间段
         */
        private final Boolean legal;

        public RangeIndex(Integer stepIndex, Integer startIndex, Integer stopIndexExclude) {
            this.stepIndex = stepIndex;
            this.startIndex = startIndex;
            this.stopIndexExclude = stopIndexExclude;
            this.rangeEnum = null;
            this.legal = Boolean.TRUE;
        }

        public RangeIndex(RangeEnum rangeEnum, Integer stepIndex, Integer startIndex, Integer stopIndexExclude, Boolean legal) {
            this.rangeEnum = rangeEnum;
            this.stepIndex = stepIndex;
            this.startIndex = startIndex;
            this.stopIndexExclude = stopIndexExclude;
            this.legal = legal;
        }

        public Integer getStepIndex() {
            return stepIndex;
        }

        public RangeEnum getRangeEnum() {
            return rangeEnum;
        }

        public Integer getStartIndex() {
            return startIndex;
        }

        public Integer getStopIndexExclude() {
            return stopIndexExclude;
        }

        public Boolean getLegal() {
            return legal;
        }

        @Override
        public String toString() {
            if (!legal) {
                return null;
            }
            String left = RangeEnum.LEFT_OPEN.contains(this.rangeEnum) ? "(" : "[";
            String right = RangeEnum.RIGHT_OPEN.contains(this.rangeEnum) ? ")" : "]";
            return left + startIndex + ", " + stopIndexExclude + right;
        }

        @Override
        public int compareTo(RangeIndex o) {
            return this.getStepIndex().compareTo(o.getStepIndex());
        }
    }
}


