package cn.lihongjie.coal.coalBlend.entity;

import cn.lihongjie.coal.base.entity.OrgCommonEntity;
import cn.lihongjie.coal.exception.BizException;

import com.google.ortools.Loader;
import com.google.ortools.sat.*;

import io.vavr.collection.Stream;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.Formula;
import org.springframework.util.StopWatch;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;

@Entity
@Data
@Comment("配煤记录")
@Slf4j
public class CoalBlendEntity extends OrgCommonEntity {

    @ElementCollection
    @NotEmpty(message = "煤参数定义不能为空")
    private List<CoalParameterDefVo> parameterDefs;

    @NotEmpty(message = "煤不能为空")
    @Size(message = "至少需要两种煤", min = 2)
    @OneToMany(mappedBy = "coal", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<CoalBlendCoalInfoEntity> coals;

    @NotNull(message = "约束条件不能为空")
    private CoalBlendConstrainVo constrains;

    @OneToMany(mappedBy = "coal", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<CoalBlendResultInfoEntity> results;

    @Comment("煤类型")
    private String coalType;

    @Formula(
            "(select i.name\n"
                    + "from t_dictionary d,\n"
                    + "     t_dictionary_item i\n"
                    + "where d.id = i.dictionary_id\n"
                    + "  and d.code = 'coalBlend.coalType'\n"
                    + "  and i.code = coal_type)")
    private String coalTypeName;

    @Comment("配煤类型")
    private String blendType;

    @Formula(
            "(select i.name\n"
                    + "from t_dictionary d,\n"
                    + "     t_dictionary_item i\n"
                    + "where d.id = i.dictionary_id\n"
                    + "  and d.code = 'coal.blendType'\n"
                    + "  and i.code = blend_type)")
    private String blendTypeName;

    @Comment("结果个数")
    private Integer count = 20;

    @Comment("最长等待时间")
    private Integer maxTime = 10;

    @Comment("铲车配煤 配比之和 最大值")
    private Integer type2PercentSum = 10;

    @Comment("目标参数, 比如： param1  param2")
    private String targetParam = "";

    @Comment("求目标参数最大值 -1， 最小值 1")
    private Integer targetOrder = -1;

    public void blend() {

        StopWatch stopWatch = new StopWatch();

        this.results = new ArrayList<>();

        stopWatch.start("check 1");
        long count1 = 0;
        count1 = coals.stream().map(x1 -> x1.getParam1()).filter(Objects::nonNull).count();

        AtomicBoolean param1 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param1.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam2()).filter(Objects::nonNull).count();

        AtomicBoolean param2 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param2.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam3()).filter(Objects::nonNull).count();

        AtomicBoolean param3 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param3.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam4()).filter(Objects::nonNull).count();

        AtomicBoolean param4 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param4.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam5()).filter(Objects::nonNull).count();

        AtomicBoolean param5 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param5.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam6()).filter(Objects::nonNull).count();

        AtomicBoolean param6 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param6.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam7()).filter(Objects::nonNull).count();

        AtomicBoolean param7 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param7.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam8()).filter(Objects::nonNull).count();

        AtomicBoolean param8 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param8.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam9()).filter(Objects::nonNull).count();

        AtomicBoolean param9 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param9.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam10()).filter(Objects::nonNull).count();

        AtomicBoolean param10 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param10.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam11()).filter(Objects::nonNull).count();

        AtomicBoolean param11 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param11.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam12()).filter(Objects::nonNull).count();

        AtomicBoolean param12 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param12.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam13()).filter(Objects::nonNull).count();

        AtomicBoolean param13 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param13.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam14()).filter(Objects::nonNull).count();

        AtomicBoolean param14 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param14.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam15()).filter(Objects::nonNull).count();

        AtomicBoolean param15 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param15.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam16()).filter(Objects::nonNull).count();

        AtomicBoolean param16 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param16.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam17()).filter(Objects::nonNull).count();

        AtomicBoolean param17 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param17.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam18()).filter(Objects::nonNull).count();

        AtomicBoolean param18 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param18.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam19()).filter(Objects::nonNull).count();

        AtomicBoolean param19 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param19.set(count1 > 0);
        }
        count1 = coals.stream().map(x1 -> x1.getParam20()).filter(Objects::nonNull).count();

        AtomicBoolean param20 = new AtomicBoolean(false);
        if (count1 > 0 && count1 != coals.size()) {
            throw new BizException("煤的参数不可以留空");
        } else {

            param20.set(count1 > 0);
        }

        // 原煤配煤逻辑 / 人工配煤

        if (StringUtils.equals(coalType, "2") || StringUtils.equals(coalType, "3")) {

            // 参与配比的煤的可能性 1 到 煤的数量之间
            // <editor-fold desc="Description">
            int startIndex = StringUtils.equals(coalType, "2") ? 0 : coals.size() - 1;
            for (int i = startIndex; i < coals.size(); i++) {

                // todo 这个循环可以删除?
                // 针对第j个煤参与配比, 那么他的比例应该在0-煤j的比例之间, 步长为0.01
                //                for (int j = 0; j <= i; j++) {
                int j = i;
                CoalBlendCoalInfoEntity coalJ = coals.get(j);

                int maxP = (int) (coalJ.getYmPercent() * 100);
                int startK = StringUtils.equals(coalType, "2") ? 0 : maxP;
                for (int k = startK; k <= maxP; k++) {
                    // 计算各个参数是否满足条件
                    CoalBlendResultInfoEntity vo = new CoalBlendResultInfoEntity();

                    if (param1.get()) {
                        Double min = constrains.getParam1Min();
                        Double max = constrains.getParam1Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam1() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam1() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam1(sum);
                        }
                    }

                    if (param2.get()) {
                        Double min = constrains.getParam2Min();
                        Double max = constrains.getParam2Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam2() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam2() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam2(sum);
                        }
                    }

                    if (param3.get()) {
                        Double min = constrains.getParam3Min();
                        Double max = constrains.getParam3Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam3() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam3() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam3(sum);
                        }
                    }

                    if (param4.get()) {
                        Double min = constrains.getParam4Min();
                        Double max = constrains.getParam4Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam4() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam4() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam4(sum);
                        }
                    }

                    if (param5.get()) {
                        Double min = constrains.getParam5Min();
                        Double max = constrains.getParam5Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam5() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam5() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam5(sum);
                        }
                    }

                    if (param6.get()) {
                        Double min = constrains.getParam6Min();
                        Double max = constrains.getParam6Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam6() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam6() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam6(sum);
                        }
                    }

                    if (param7.get()) {
                        Double min = constrains.getParam7Min();
                        Double max = constrains.getParam7Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam7() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam7() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam7(sum);
                        }
                    }

                    if (param8.get()) {
                        Double min = constrains.getParam8Min();
                        Double max = constrains.getParam8Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam8() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam8() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam8(sum);
                        }
                    }

                    if (param9.get()) {
                        Double min = constrains.getParam9Min();
                        Double max = constrains.getParam9Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam9() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam9() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam9(sum);
                        }
                    }

                    if (param10.get()) {
                        Double min = constrains.getParam10Min();
                        Double max = constrains.getParam10Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam10() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam10() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam10(sum);
                        }
                    }

                    if (param11.get()) {
                        Double min = constrains.getParam11Min();
                        Double max = constrains.getParam11Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam11() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam11() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam11(sum);
                        }
                    }

                    if (param12.get()) {
                        Double min = constrains.getParam12Min();
                        Double max = constrains.getParam12Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam12() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam12() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam12(sum);
                        }
                    }

                    if (param13.get()) {
                        Double min = constrains.getParam13Min();
                        Double max = constrains.getParam13Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam13() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam13() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam13(sum);
                        }
                    }

                    if (param14.get()) {
                        Double min = constrains.getParam14Min();
                        Double max = constrains.getParam14Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam14() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam14() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam14(sum);
                        }
                    }

                    if (param15.get()) {
                        Double min = constrains.getParam15Min();
                        Double max = constrains.getParam15Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam15() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam15() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam15(sum);
                        }
                    }

                    if (param16.get()) {
                        Double min = constrains.getParam16Min();
                        Double max = constrains.getParam16Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam16() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam16() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam16(sum);
                        }
                    }

                    if (param17.get()) {
                        Double min = constrains.getParam17Min();
                        Double max = constrains.getParam17Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam17() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam17() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam17(sum);
                        }
                    }

                    if (param18.get()) {
                        Double min = constrains.getParam18Min();
                        Double max = constrains.getParam18Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam18() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam18() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam18(sum);
                        }
                    }

                    if (param19.get()) {
                        Double min = constrains.getParam19Min();
                        Double max = constrains.getParam19Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam19() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam19() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam19(sum);
                        }
                    }

                    if (param20.get()) {
                        Double min = constrains.getParam20Min();
                        Double max = constrains.getParam20Max();
                        Double sum = 0.0;
                        for (int l = 0; l < j; l++) {

                            sum += coals.get(l).getParam20() * coals.get(l).getYmPercent() / 100.0;
                        }

                        sum += k * coalJ.getParam20() / 10000.0;

                        sum =
                                BigDecimal.valueOf(sum)
                                        .setScale(2, RoundingMode.HALF_UP)
                                        .doubleValue();
                        if ((min != null && sum < min) || (max != null && sum > max)) {
                            continue;
                        } else {
                            vo.setParam20(sum);
                        }
                    }

                    // 生成配煤结果

                    ArrayList<CoalPercentVo> percentInfos = new ArrayList<>();
                    vo.setPercentInfos(percentInfos);
                    for (int l = 0; l < j; l++) {

                        percentInfos.add(
                                new CoalPercentVo(
                                        coals.get(l).getName(),
                                        coals.get(l).getYmPercent(),
                                        0.0,
                                        coals.get(l).getYmPercent()));
                    }

                    percentInfos.add(
                            new CoalPercentVo(coalJ.getName(), k / 100.0, null, k / 100.0));

                    if (results.isEmpty()) {
                        results = new ArrayList<>();
                    }
                    vo.setPercentSum(
                            percentInfos.stream().mapToDouble(CoalPercentVo::getYmPercent).sum());
                    results.add(vo);

                    if (results.size() > count) {
                        break;
                    }
                }
            }
            //            }
            // </editor-fold>

            return;
        }

        stopWatch.stop();
        stopWatch.start("loadNativeLibraries");

        Loader.loadNativeLibraries();

        stopWatch.stop();
        stopWatch.start("CpModel");
        CpModel model = new CpModel();

        Stream<IntVar> percentVal =
                Stream.ofAll(coals)
                        .zipWithIndex(
                                (c, i) -> {
                                    return model.newIntVar(
                                            c.getMinPercent(), c.getMaxPercent(), "x" + i);
                                });

        IntVar[] percentValArr = percentVal.toJavaArray(IntVar[]::new);
        // 各个煤累加和 100
        model.addEquality(LinearExpr.sum(percentValArr), 100);
        addBaseConstrain(model, percentValArr);

        IntVar gcd = model.newIntVar(2, 100, "gcd");
        Stream<IntVar> gcdVal = null;

        if (Objects.equals(blendType, "2")) {

            gcdVal =
                    Stream.ofAll(coals)
                            .zipWithIndex(
                                    (c, i) ->
                                            model.newIntVar(
                                                    c.getMinPercent(),
                                                    c.getMaxPercent(),
                                                    "gcdX" + i));
            percentVal.zip(gcdVal).forEach(t -> model.addDivisionEquality(t._2, t._1, gcd));
            percentVal
                    .zip(gcdVal)
                    .forEach(t -> model.addModuloEquality(LinearExpr.constant(0), t._1, gcd));

            model.addLessOrEqual(
                    LinearExpr.sum(gcdVal.toJavaArray(IntVar[]::new)), type2PercentSum);
        }

        if (StringUtils.isNotEmpty(targetParam)) {
            if (targetOrder == -1) {

                model.maximize(
                        LinearExpr.weightedSum(
                                percentValArr,
                                coals.stream()
                                        .mapToLong(x -> (long) (x.getParam(targetParam) * 100))
                                        .toArray()));

            } else {

                model.minimize(
                        LinearExpr.weightedSum(
                                percentValArr,
                                coals.stream()
                                        .mapToLong(x -> (long) (x.getParam(targetParam) * 100))
                                        .toArray()));
            }
        }
        stopWatch.stop();
        stopWatch.start("CpSolver");

        // 初始化求解器
        CpSolver cpSolver = new CpSolver();
        SatParameters.Builder parameters = cpSolver.getParameters();
        parameters.setEnumerateAllSolutions(true);
        parameters.setMaxTimeInSeconds(this.getMaxTime());
        parameters.setLogSearchProgress(true);

        cpSolver.setLogCallback((x) -> {});
        parameters.setFillAdditionalSolutionsInResponse(false);
        HashSet<Object> seen = new HashSet<>();
        Stream<IntVar> finalGcdVal = gcdVal;
        try {

            cpSolver.solve(
                    model,
                    new CpSolverSolutionCallback() {

                        @Override
                        public void onSolutionCallback() {

                            CoalBlendResultInfoEntity vo = new CoalBlendResultInfoEntity();

                            if (finalGcdVal != null) {

                                vo.setPercentInfos(
                                        Stream.ofAll(coals)
                                                .zip(percentVal)
                                                .zip(finalGcdVal)
                                                .map(
                                                        t ->
                                                                new CoalPercentVo(
                                                                        t._1._1.getName(),
                                                                        ((double) (value(t._1._2))),
                                                                        (double) value(t._2)))
                                                .toJavaList());
                            } else {
                                vo.setPercentInfos(
                                        Stream.ofAll(coals)
                                                .zip(percentVal)
                                                .zip(percentVal)
                                                .map(
                                                        t ->
                                                                new CoalPercentVo(
                                                                        t._1._1.getName(),
                                                                        ((double) (value(t._1._2))),
                                                                        (double) value(t._2)))
                                                .toJavaList());
                            }
                            if (!seen.add(vo.getHashKey())) {
                                return;
                            }

                            if (param1.get())
                                vo.setParam1(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam1()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param2.get())
                                vo.setParam2(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam2()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param3.get())
                                vo.setParam3(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam3()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param4.get())
                                vo.setParam4(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam4()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param5.get())
                                vo.setParam5(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam5()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param6.get())
                                vo.setParam6(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam6()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param7.get())
                                vo.setParam7(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam7()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param8.get())
                                vo.setParam8(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam8()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param9.get())
                                vo.setParam9(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam9()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param10.get())
                                vo.setParam10(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam10()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param11.get())
                                vo.setParam11(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam11()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param12.get())
                                vo.setParam12(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam12()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param13.get())
                                vo.setParam13(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam13()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param14.get())
                                vo.setParam14(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam14()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param15.get())
                                vo.setParam15(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam15()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param16.get())
                                vo.setParam16(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam16()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param17.get())
                                vo.setParam17(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam17()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param18.get())
                                vo.setParam18(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam18()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param19.get())
                                vo.setParam19(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam19()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);
                            if (param20.get())
                                vo.setParam20(
                                        value(
                                                        LinearExpr.weightedSum(
                                                                percentValArr,
                                                                coals.stream()
                                                                        .mapToLong(
                                                                                x ->
                                                                                        (long)
                                                                                                (x
                                                                                                                .getParam20()
                                                                                                        * 100))
                                                                        .toArray()))
                                                / 10000.0);

                            CoalBlendEntity.this.results.add(vo);

                            if (CoalBlendEntity.this.results.size() >= CoalBlendEntity.this.count) {
                                stopSearch();
                            }
                        }
                    });

        } catch (Throwable e) {
            log.error("{}", e);

        } finally {

            if (stopWatch.isRunning()) {

                stopWatch.stop();
            }
            log.info(stopWatch.prettyPrint());
        }
    }

    /**
     * 添加参数依赖
     *
     * @param model
     * @param percentValArr
     */
    private void addBaseConstrain(CpModel model, IntVar[] percentValArr) {
        Double val = null;

        val = constrains.getParam1Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam1() * 100)).toArray()),
                    (long) (val * 10000));
        }

        val = constrains.getParam2Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam2() * 100)).toArray()),
                    (long) (val * 10000));
        }

        val = constrains.getParam3Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam3() * 100)).toArray()),
                    (long) (val * 10000));
        }

        val = constrains.getParam4Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam4() * 100)).toArray()),
                    (long) (val * 10000));
        }

        val = constrains.getParam5Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam5() * 100)).toArray()),
                    (long) (val * 10000));
        }

        val = constrains.getParam6Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam6() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam7Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam7() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam8Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam8() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam9Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam9() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam10Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam10() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam11Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam11() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam12Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam12() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam13Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam13() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam14Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam14() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam15Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam15() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam16Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam16() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam17Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam17() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam18Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam18() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam19Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam19() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam20Min();

        if (val != null) {
            model.addGreaterOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam20() * 100)).toArray()),
                    (long) (val * 10000));
        }

        val = constrains.getParam1Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam1() * 100)).toArray()),
                    (long) (val * 10000));
        }

        val = constrains.getParam2Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam2() * 100)).toArray()),
                    (long) (val * 10000));
        }

        val = constrains.getParam3Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam3() * 100)).toArray()),
                    (long) (val * 10000));
        }

        val = constrains.getParam4Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam4() * 100)).toArray()),
                    (long) (val * 10000));
        }

        val = constrains.getParam5Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam5() * 100)).toArray()),
                    (long) (val * 10000));
        }

        val = constrains.getParam6Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam6() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam7Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam7() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam8Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam8() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam9Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam9() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam10Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam10() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam11Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam11() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam12Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam12() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam13Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam13() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam14Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam14() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam15Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam15() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam16Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam16() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam17Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam17() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam18Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam18() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam19Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam19() * 100)).toArray()),
                    (long) (val * 10000));
        }
        val = constrains.getParam20Max();

        if (val != null) {
            model.addLessOrEqual(
                    LinearExpr.weightedSum(
                            percentValArr,
                            coals.stream().mapToLong(x -> (long) (x.getParam20() * 100)).toArray()),
                    (long) (val * 10000));
        }
    }

    public static class BlendGroup {}
}
