package com.xisc.pm.app.service.autoboard.core;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.TreeMap;

import com.xisc.pm.api.dto.PmProContYieldResposeDTO;
import com.xisc.pm.app.service.autoboard.config.CalcLog;
import com.xisc.pm.app.service.autoboard.dto.PmAutoBoardExecParam;
import com.xisc.pm.app.service.autoboard.dto.PmAutoBoardExecResult;
import com.xisc.pm.app.service.autoboard.dto.PmAutoBoardPolicyGroupDTO;
import com.xisc.pm.app.service.autoboard.dto.PmAutoPdLog;
import com.xisc.pm.app.service.autoboard.dto.PmContBoardParam;
import com.xisc.pm.app.service.autoboard.dto.PmPdResultDTO;
import com.xisc.pm.infra.enums.FormPlateMode;

/**
 * 
 * 组板程序v1.0（S增强版）支持所有S型组板（S1，S2，S3，S7）
 * S型组板，采用贪心思想，已组合版宽度最近为最优原则匹配子板
 *  1. 长度接近极限长
 *  2. 优先最长宽度与最小宽度
 *  3. 不能组板 剩余最小宽度当匹配下一队首宽度时重新入队尾
 * @author jun.zeng
 *
 */
public class PmPdProgramSPlus extends DefaultPmPdProgram {

    private Deque<PmContBoardParam> priorityQueue;
    private PmAutoBoardExecResult execResult;
    private PmAutoBoardExecParam current;
    private PmAutoBoardPolicyGroupDTO policyGroup;
    private FormPlateMode formPlateMode;
    
    private Map<String , PmContBoardParam> orign = new HashMap<>();
    private boolean initOrginFlag = false ;
    
    public PmPdProgramSPlus(Queue<PmContBoardParam> priorityQueue, PmAutoBoardExecResult execResult,
            PmAutoBoardExecParam current, PmAutoBoardPolicyGroupDTO policyGroup, FormPlateMode formPlateMode) {
        this.current = current;
        // S型组板需要使用双端队列
        this.priorityQueue = (Deque<PmContBoardParam>) priorityQueue;
        this.execResult = execResult;
        this.policyGroup = policyGroup;
        this.formPlateMode = formPlateMode;
    }
    
    private Map<String , LinkedHashMap<String, LinkedList<PmContBoardParam>>> init(){
        // 优先按宽度分组，然后按长度分组
        Map<String , LinkedHashMap<String, LinkedList<PmContBoardParam>>> execQueues = new TreeMap<String, LinkedHashMap<String,LinkedList<PmContBoardParam>>>();
        for (PmContBoardParam pmContBoardParam : priorityQueue) {
            if(!initOrginFlag) {
                orign.put(pmContBoardParam.getOrderNo(), pmContBoardParam);
            }
            BigDecimal qty = execResult.calcRemainingPdQty(pmContBoardParam);
            if (BigDecimal.ZERO.compareTo(qty) >= 0) {
                continue;
            }
            if(!execQueues.containsKey(pmContBoardParam.getOrderWid().toPlainString())) {
                execQueues.put(pmContBoardParam.getOrderWid().toPlainString(), new LinkedHashMap<>());
            } 
            LinkedHashMap<String, LinkedList<PmContBoardParam>> widthMap = execQueues.get(pmContBoardParam.getOrderWid().toPlainString());
            if(!widthMap.containsKey(pmContBoardParam.getSmallPlateLen().toPlainString())) {
                widthMap.put(pmContBoardParam.getSmallPlateLen().toPlainString(), new LinkedList<PmContBoardParam>());
            }
            LinkedList<PmContBoardParam> linkedList = widthMap.get(pmContBoardParam.getSmallPlateLen().toPlainString());
            linkedList.add(pmContBoardParam);
        }
        initOrginFlag = true ;
        return execQueues;
    }
    
    private boolean execute(LinkedList<String> first , LinkedList<String> second) {
        if(first == null || first.isEmpty()) {
            return true ;
        }
        int head = 0;
        int tail = 0;
        PmContBoardParam axisPmContBoardParam = null;
        BigDecimal szWid = BigDecimal.ZERO;
        BigDecimal szLen = BigDecimal.ZERO;
        BigDecimal allWt = BigDecimal.ZERO;
        BigDecimal spCount = BigDecimal.ZERO;
        List<BigDecimal> lens = new ArrayList<>();
        List<String> block1 = new ArrayList<>();
        List<String> block2 = new ArrayList<>();
        
        boolean doPdFlag = false;
        boolean execFlag = false;
        int i = 0 ;
        while(i < first.size()) {
            PmContBoardParam firstPmContBoardParam = orign.get(first.get(i));
            PmContBoardParam secondPmContBoardParam = orign.get(second.get(i));
            if(!doPdFlag) {
                axisPmContBoardParam = firstPmContBoardParam;
                szWid = axisPmContBoardParam.getOrderWid().add(secondPmContBoardParam.getOrderWid());
                szLen = axisPmContBoardParam.getSmallPlateLen();
                allWt = axisPmContBoardParam.getOrderUnitAim().add(secondPmContBoardParam.getOrderUnitAim());
                spCount = BigDecimal.valueOf(2L);
                lens.clear();
                block1.clear();
                block2.clear();
                lens.add(axisPmContBoardParam.getSmallPlateLen());
                block1.add(first.get(i));
                block2.add(second.get(i));
                if(policyGroup.getMaxCombinePlateLen().compareTo(szLen.add(axisPmContBoardParam.getSmallPlateLen())) < 0) {
                    execFlag = true ;
                } else if(policyGroup.getMaxCombinePlateLen().compareTo(szLen.add(axisPmContBoardParam.getSmallPlateLen())) == 0) {
                    execFlag = true ;
                }
                tail = i ;
                head = i ;
                i++;
                doPdFlag = true ;
            } else {
                if(policyGroup.getMaxCombinePlateLen().compareTo(szLen.add(axisPmContBoardParam.getSmallPlateLen())) < 0) {
                    execFlag = true ;
                } else if(policyGroup.getMaxCombinePlateLen().compareTo(szLen.add(axisPmContBoardParam.getSmallPlateLen())) == 0) {
                    execFlag = true ;
                    tail = i ;
                    block1.add(first.get(i));
                    block2.add(second.get(i));
                    szLen = szLen.add(firstPmContBoardParam.getSmallPlateLen());
                    allWt = allWt.add(firstPmContBoardParam.getOrderUnitAim().add(secondPmContBoardParam.getOrderUnitAim()));
                    lens.add(firstPmContBoardParam.getSmallPlateLen());
                    spCount = spCount.add(BigDecimal.valueOf(2L));
                    i++;
                } else {
                    block1.add(first.get(i));
                    block2.add(second.get(i));
                    lens.add(firstPmContBoardParam.getSmallPlateLen());
                    szLen = szLen.add(firstPmContBoardParam.getSmallPlateLen());
                    spCount = spCount.add(BigDecimal.valueOf(2L));
                    allWt = allWt.add(firstPmContBoardParam.getOrderUnitAim().add(secondPmContBoardParam.getOrderUnitAim()));
                    tail = i;
                    i++;
                }
            }
            
            if(execFlag || i == first.size()) {
                boolean pdFlag = false;
                p: while(tail >= head) {
                    // 组板中的集合
                    List<PmContBoardParam> orderList = new ArrayList<>();
                    Set<String> boardOrderNoSet = new HashSet<>();
                    boolean smaeLen = true ;
                    BigDecimal smallPlateLen = null;
                    BigDecimal wid1 = null;
                    BigDecimal wid2 = null;
                    for (String orderNo : block1) {
                        PmContBoardParam block1PmContBoardParam = orign.get(orderNo) ;
                        wid1 = block1PmContBoardParam.getOrderWid();
                        if(!boardOrderNoSet.contains(orderNo)) {
                            if(smallPlateLen == null) {
                                smallPlateLen = block1PmContBoardParam.getSmallPlateLen();
                            } else {
                                if(smaeLen) {
                                    smaeLen = smallPlateLen.compareTo(block1PmContBoardParam.getSmallPlateLen()) == 0;
                                }
                            }
                            orderList.add(block1PmContBoardParam);
                            boardOrderNoSet.add(orderNo);
                        } 
                    }
                    for (String orderNo : block2) {
                        PmContBoardParam block2PmContBoardParam = orign.get(orderNo) ;
                        wid2 = block2PmContBoardParam.getOrderWid();
                        if(!boardOrderNoSet.contains(orderNo)) {
                            orderList.add(block2PmContBoardParam);
                            boardOrderNoSet.add(orderNo);
                        } 
                    }
                    FormPlateMode formPlateModeCurrent = null;
                    if(wid1.compareTo(wid2) == 0) {
                        if(smaeLen) {
                            // a)   若组板所有合同同宽同长，组板方式form_plate_mode=S1;
                            formPlateModeCurrent = FormPlateMode.S1; 
                        } else {
                            // b)  若组板上下两层小板同宽，合同不同长，组板方式form_plate_mode=S3;
                            formPlateModeCurrent = FormPlateMode.S3; 
                        }
                    } else if(wid1.compareTo(wid2) < 0) {
                        // c)  若组板BLOCK2宽度大于BLOCK1宽度，组板方式form_plate_mode=S2;
                        formPlateModeCurrent = FormPlateMode.S2;
                    } else if(wid1.compareTo(wid2) > 0) {
                        // d)   若组板BLOLK2宽度小于BLOCK1宽度，组板方式form_plate_mode=S7;
                        formPlateModeCurrent = FormPlateMode.S7;
                    }
                    
                    PmProContYieldResposeDTO respose = checkResult(formPlateModeCurrent, orderList, policyGroup, current,execResult, szWid, szLen,
                            axisPmContBoardParam, lens, allWt , block1 , block2);
                    if (respose != null) {
                        // 如果能组板。记录组板结果；
                        PmPdResultDTO pmPdResult = new PmPdResultDTO(szLen, szWid, spCount, allWt, lens, orderList
                                , formPlateModeCurrent.getCode(), block1, block2 , axisPmContBoardParam);
                        pmPdResult.setRespose(respose);
                        pmPdResult.setCurrent(current);
                        if(respose.getTransFlag() != null) {
                            pmPdResult.setTransFlag(respose.getTransFlag());
                        }
                        try {
                            boolean canExecFlag = execResult.addVirtualSlab(pmPdResult);
                            if(!canExecFlag) {
                                return false;
                            }
                            pdFlag = true ;
                        } catch (Exception e) {
                            CalcLog.log(e.getMessage());
                            CalcLog.log(new PmAutoPdLog(current.getRequestId(), execResult.getVersion() , current.getMoldId()).setAxisOrderNo(axisPmContBoardParam.getOrderNo()).setBlock1(block1).setBlock2(null)
                                    .setFormPlateMode(formPlateModeCurrent.getCode()).setLens(lens).setMessage("组板失败："+e.getMessage()));
                        }
                        doPdFlag = false;
                        break p;
                    } 
                    PmContBoardParam lastPmContBoardParamFirst = orign.get(first.get(tail));
                    PmContBoardParam lastPmContBoardParamSecond = orign.get(second.get(tail));
                    spCount = spCount.subtract(BigDecimal.valueOf(2L));
                    int count = tail - head;
                    if(count >= 1) {
                        block1.remove(count);
                        block2.remove(count);
                        lens.remove(count);
                        szLen = szLen.subtract(lastPmContBoardParamFirst.getSmallPlateLen());
                        allWt = allWt.add(lastPmContBoardParamFirst.getOrderUnitAim());
                        allWt = allWt.add(lastPmContBoardParamSecond.getOrderUnitAim());
                    }
                    tail -- ;
                }
                doPdFlag = false;
                if(pdFlag) {
                    i = tail + 1;
                } else {
                    i = head + 1;
                }
            }
            
        }
        return true ;
    }
    
    private ListIterator<String> getSameLenAndWidthOrderList(LinkedList<PmContBoardParam> block2Order){
        List<String> orderNoList = new ArrayList<>();
        if(block2Order == null || block2Order.isEmpty()) {
            return orderNoList.listIterator();
        }
        for (PmContBoardParam pmContBoardParam : block2Order) {
            BigDecimal qty = execResult.calcRemainingPdQty(pmContBoardParam);
            for (BigDecimal k = BigDecimal.ZERO ; k.compareTo(qty) < 0; k = k.add(BigDecimal.ONE) ) {
                orderNoList.add(pmContBoardParam.getOrderNo());
            }
        }
        return orderNoList.listIterator();
    }

    @Override
    public Boolean call() throws Exception {
        Map<String , LinkedHashMap<String, LinkedList<PmContBoardParam>>> execQueues = init();
        int length = execQueues.size();
        Object[] keys = execQueues.keySet().toArray();
        for(int i = 0; i < length ; i ++ ) {
            LinkedHashMap<String, LinkedList<PmContBoardParam>> first = execQueues.get(keys[i]);
            if(!first.isEmpty()) {
                LinkedList<String> block1 = new LinkedList<>();
                LinkedList<String> block2 = new LinkedList<>();
                for (Entry<String, LinkedList<PmContBoardParam>> entry : first.entrySet()) {
                    LinkedList<PmContBoardParam> block1Order = first.get(entry.getKey());
                    ListIterator<String> iterator = getSameLenAndWidthOrderList(block1Order);
                    p: while(iterator.hasNext()) {
                        String orderNo1 = iterator.next();
                        if(iterator.hasNext()) {
                            block1.add(orderNo1);
                            block2.add(iterator.next());
                        } else {
                            break p;
                        }
                    }
                }
                if(!execute(block1, block2)) {
                    return false ;
                }
            }
        }
        for(int i = length - 1 ; i >= 0 ; i -- ) {
            for (int j = 0 ; j < length ; j++ ) {
                if(i != j) {
                    LinkedHashMap<String, LinkedList<PmContBoardParam>> first = execQueues.get(keys[i]);
                    LinkedHashMap<String, LinkedList<PmContBoardParam>> second = execQueues.get(keys[j]);
                    if(!first.isEmpty() && !second.isEmpty()) {
                        LinkedList<String> block1 = new LinkedList<>();
                        LinkedList<String> block2 = new LinkedList<>();
                        for (Entry<String, LinkedList<PmContBoardParam>> entry : first.entrySet()) {
                            
                            LinkedList<PmContBoardParam> block2Order = second.get(entry.getKey());
                            ListIterator<String> iterator = getSameLenAndWidthOrderList(block2Order);
                            for (PmContBoardParam pmContBoardParam : entry.getValue()) {
                               if(iterator.hasNext()) {
                                   BigDecimal qty = execResult.calcRemainingPdQty(pmContBoardParam);
                                   for (BigDecimal k = BigDecimal.ZERO ; k.compareTo(qty) < 0; k = k.add(BigDecimal.ONE) ) {
                                       if(iterator.hasNext()) {
                                           block1.add(pmContBoardParam.getOrderNo());
                                           block2.add(iterator.next());
                                       }
                                   }
                               }
                            }
                        }
                        if(!execute(block1, block2)) {
                            return false ;
                        }
                    }
                }
            }
            
        }
        return true;
    }

}
