package com.jeeplus.modules.sg.balancedlibrary.liKuResourcePool.utils;

import com.jeeplus.modules.sg.balancedlibrary.liKuResourcePool.entity.LiKuResourcePool;
import com.jeeplus.modules.sg.balancedlibrary.materialReportDetails.entity.MaterialReportDetails;
import com.jeeplus.modules.sys.utils.DictUtils;

import java.math.BigDecimal;
import java.util.*;

public class OptimalUtil {
    //是否有“元素组合的和”与“待比对值”相同的情况，true为有
    private boolean getEqual = false;
    //所有可能的元素组合列表
    private List<List<MaterialReportDetails>> result = new ArrayList<>();
    //由物资上报方开始匹配时所有可能的元素组合列表
    private List<List<LiKuResourcePool>> resultReverse = new ArrayList<>();
    //最大浪费量
    private BigDecimal maxiNun = new BigDecimal(DictUtils.getDictValue("maxWaste", "maximum_waste", ""));
    //由物资上报方开始匹配时最大差额
    private BigDecimal maxiNunReverse = new BigDecimal(DictUtils.getDictValue("maxWasteReverse", "maximum_waste", ""));

    /**
     *
     * @param total 待比对的值
     * @param elements  所有元素集合
     * @return
     */
    public List<MaterialReportDetails> getOptimal(double total, List<MaterialReportDetails> elements){
        //list降序排列
        Collections.sort(elements, Comparator.comparing(MaterialReportDetails::getLengthOfMetre));
        Collections.reverse(elements);
        solve(0,0,new ArrayList<>(),total,elements);

        //找到差值最小组合返回
        List<MaterialReportDetails> returnList = new ArrayList<>();
        BigDecimal currMax = BigDecimal.valueOf(0.0);//当前总和最大
        for (List<MaterialReportDetails> detailsList :result) {
            BigDecimal listSum = BigDecimal.valueOf(0.0);
            for (MaterialReportDetails details :detailsList) {
                listSum = listSum.add(BigDecimal.valueOf(details.getLengthOfMetre()));
            }
            if (listSum.compareTo(currMax) > 0) {
                currMax = listSum;
                returnList = detailsList;
//                returnList.clear();
//                returnList.addAll(detailsList);
            }
        }


        return returnList;
    }

    /**
     *
     * @param total 待比对的值
     * @param elements  利库所有元素集合
     * @return
     */
    public List<LiKuResourcePool> getOptimalReverse(double total, List<LiKuResourcePool> elements){
        //list降序排列
        Collections.sort(elements, Comparator.comparing(LiKuResourcePool::getLengthOfMetre));
        solveReverse(0,0,new ArrayList<>(),total,elements);

        //找到差值最小组合返回
        List<LiKuResourcePool> returnList = new ArrayList<>();
        BigDecimal currMin = BigDecimal.valueOf(total).add(maxiNunReverse);//当前最小值
        for (List<LiKuResourcePool> poolList : resultReverse) {
            BigDecimal listSum = BigDecimal.valueOf(0.0);
            for (LiKuResourcePool pool : poolList) {
                listSum = listSum.add(BigDecimal.valueOf(pool.getLengthOfMetre()));
            }
            if (listSum.compareTo(currMin) < 0) {
                currMin = listSum;
                returnList = poolList;
//                returnList.clear();
//                returnList.addAll(detailsList);
            }
        }


        return returnList;
    }

    /**
     *
     * @param index 当前集合下标
     * @param sum   当前组合元素和
     * @param curr  当前组合元素集合
     * @param total 待比对的值
     * @param elements 所有元素集合
     */
    public void solve(int index, double sum, List<MaterialReportDetails> curr, double total, List<MaterialReportDetails> elements ){
        //如果找到元素组合的和与待比对值一样，则不往下继续查找
        if(getEqual == true){
            return;
        }
        //走到这一步，所有最后一个元素也参考过（可能加入组合也可能不加入），最终还是组合值小于待比对值。则放入到result中
        if(index == elements.size()){
            BigDecimal allSum = BigDecimal.valueOf(sum);
            BigDecimal allTotal = BigDecimal.valueOf(total);
            BigDecimal subtract = allTotal.subtract(allSum).setScale(2, BigDecimal.ROUND_HALF_UP);
            if (subtract.compareTo(maxiNun) <= 0) {
                result.add(curr);
            }
            return;
        }
        MaterialReportDetails next = elements.get(index);
        //处理单位不同,所有都以米作为单位计算
        Double nextNeed = next.getLengthOfMetre();

        index++;
        //如果将当前值加上已有元素组合的值等于待比对值，得到结果
        if((sum+nextNeed)==total){
            curr.add(next);
            result = new ArrayList<>();
            result.add(curr);
            getEqual = true;
        //如果将当前值加上已有元素组合的值大于待比对值，则不考虑这种组合的后续可能性
        }else if((sum+nextNeed) > total){
            solve(index,sum,curr,total,elements);
        }else if((sum+nextNeed) < total){
            List newCurr = new ArrayList(curr);
            newCurr.add(next);
            solve(index,sum+nextNeed,newCurr,total,elements);
//            solve(index,sum,curr,total,elements);
        }
    }

    /**
     *
     * @param index 当前集合下标
     * @param sum   当前组合元素和
     * @param curr  当前组合元素集合
     * @param total 待比对的值
     * @param elements 所有元素集合
     */
    public void solveReverse(int index, double sum, List<LiKuResourcePool> curr, double total, List<LiKuResourcePool> elements ){
        //如果找到元素组合的和与待比对值一样，则不往下继续查找
        if(getEqual == true){
            return;
        }
        //走到这一步，说明总和比total少，不放入
        if(index == elements.size()){
            return;
        }
        LiKuResourcePool next = elements.get(index);
        //处理单位不同,所有都以米作为单位计算
        Double cableLength = next.getLengthOfMetre();

        index++;
        //如果将当前值加上已有元素组合的值等于待比对值，得到结果
        if((sum+cableLength)==total){
            curr.add(next);
            resultReverse = new ArrayList<>();
            resultReverse.add(curr);
            getEqual = true;
            //如果将当前值加上已有元素组合的值大于待比对值，则不考虑这种组合的后续可能性
        }else if((sum+cableLength) > total){
            BigDecimal bigSum = BigDecimal.valueOf(sum);
            BigDecimal bigLength = BigDecimal.valueOf(cableLength);
            BigDecimal bigTotal = BigDecimal.valueOf(total);
            BigDecimal subtract = bigSum.add(bigLength).subtract(bigTotal).setScale(2,BigDecimal.ROUND_HALF_UP);
            if(subtract.compareTo(maxiNunReverse)<=0){
                curr.add(next);
                resultReverse.add(curr);
            }
            //由于资源池是由小到大排序，所以后面的一定不会比这条更接近最优解，则下面的代码不需要
//            solveReverse(index,sum,curr,total,elements);
        }else if((sum+cableLength) < total){
            List newCurr = new ArrayList(curr);
            newCurr.add(next);
            solveReverse(index,sum+cableLength,newCurr,total,elements);
            solveReverse(index,sum,curr,total,elements);
        }
    }
}
