// Source code is decompiled from a .class file using FernFlower decompiler (from Intellij IDEA).
package com.aps.service.Algorithm;

import com.aps.common.util.FileHelper;
import com.aps.common.util.GlobalCacheUtil;
import com.aps.common.util.ProductionDeepCopyUtil;
import com.aps.entity.Algorithm.Chromosome;
import com.aps.entity.Algorithm.GAScheduleResult;
import com.aps.entity.Algorithm.IDAndChildID.GroupResult;
import com.aps.entity.basic.Entry;
import com.aps.entity.basic.Machine;
import com.aps.entity.basic.Material;
import com.aps.entity.basic.Order;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class SimulatedAnnealing {
    private final Random rnd = new Random();
    private static final double SIGNIFICANT_IMPROVEMENT_THRESHOLD = 1.0E-4;
    private List<Entry> allOperations;
    private List<Order> orders;
    private TreeMap<String, Material> materials;
    private List<GroupResult> _entryRel;
    private FitnessCalculator fitnessCalculator;
    private Map<String, Entry> entrys;
    private Map<Integer, Entry> entrybyids;
    private List<Machine> cachedMachines;
    private List<Order> cachedOrders;
    private List<GroupResult> cachedEntryRel;
    private TreeMap<String, Material> cachedMaterials;
    private List<Entry> cachedAllOperations;
    private final ExecutorService decodeExecutor;

    private void log(String message) {
        this.log(message, false);
    }

    private void log(String message, boolean enableLogging) {
        if (enableLogging) {
            FileHelper.writeLogFile(message);
        }

    }

    public SimulatedAnnealing(List<Entry> allOperations, List<Order> orders, TreeMap<String, Material> materials, List<GroupResult> entryRel, FitnessCalculator _fitnessCalculator) {
        this.decodeExecutor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors() - 1, Runtime.getRuntime().availableProcessors() - 1, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue(200), new ThreadPoolExecutor.CallerRunsPolicy());
        this.allOperations = allOperations;
        this.orders = orders;
        this.materials = materials;
        this._entryRel = entryRel;
        Map<Integer, Object> mp = this.buildEntryKey();
        this.fitnessCalculator = _fitnessCalculator;
        this.entrys = (Map)mp.get(1);
        this.entrybyids = (Map)mp.get(2);
        this.cachedAllOperations = ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList(allOperations), Entry.class);
        this.cachedOrders = ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList(orders), Order.class);
        this.cachedEntryRel = ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList(entryRel), GroupResult.class);
        this.cachedMaterials = ProductionDeepCopyUtil.deepCopyTreeMap(materials, String.class, Material.class);
    }

    public List<Chromosome> batchSearch(List<Chromosome> chromosomes, VariableNeighborhoodSearch vns, GeneticDecoder decoder, List<Machine> machines) {
        List<Chromosome> saHcOptimized = new ArrayList();

        for(Chromosome chromosome : chromosomes) {
            Chromosome optimized = this.searchWithHillClimbing(chromosome, vns, decoder, machines);
            saHcOptimized.add(optimized);
        }

        return saHcOptimized;
    }

    public Chromosome batchSearchGetMax(List<Chromosome> chromosomes, VariableNeighborhoodSearch vns, GeneticDecoder decoder, List<Machine> machines) {
        List<Chromosome> saHcOptimized = this.batchSearch(chromosomes, vns, decoder, machines);
        int bestidx = this.Getbest(saHcOptimized, (Chromosome)null);
        return bestidx > -1 ? (Chromosome)saHcOptimized.get(bestidx) : null;
    }

    public Chromosome searchWithHillClimbing(Chromosome chromosome, VariableNeighborhoodSearch vns, GeneticDecoder decoder, List<Machine> machines) {
        this.log("模拟退火+爬山法 - 开始执行", true);
        Chromosome current = vns.copyChromosome(chromosome);
        this.decode(decoder, current, machines);
        Chromosome best = this.lightCopy(current);
        this.writeKpi(best);
        double[] initialFitnessLevel = (double[])best.getFitnessLevel().clone();
        double initialFitness = best.getFitness();
        double temperature = (double)100.0F;
        double coolingRate = 0.9;
        double temperatureThreshold = (double)5.0F;
        int maxIterations = 100;
        int noImproveCount = 0;
        int maxNoImprove = 15;
        int stagnantWindow = 15;
        int[] recentImprovements = new int[stagnantWindow];
        double improvementRateThreshold = 0.05;
        this.log(String.format("模拟退火+爬山法 - 参数配置：温度=%.1f, 降温率=%.2f, 阈值=%.1f, 最大迭代=%d, 最大无改进=%d", temperature, coolingRate, temperatureThreshold, maxIterations, maxNoImprove));
        int acceptCount = 0;
        int improveCount = 0;
        int significantImproveCount = 0;
        int totalIterations = 0;

        for(int i = 0; i < maxIterations; ++i) {
            totalIterations = i + 1;
            boolean improved = false;
            decoder.DelOrder(current);
            Chromosome neighbor = vns.generateNeighbor(current);
            this.decode(decoder, neighbor, machines);
            double energyDifference = this.calculateEnergyDifference(neighbor, current);
            boolean accepted = false;
            if (energyDifference > (double)0.0F || this.rnd.nextDouble() < Math.exp(energyDifference / temperature)) {
                current = neighbor;
                accepted = true;
                ++acceptCount;
                if (this.isBetter(neighbor, best)) {
                    best = this.lightCopy(neighbor);
                    this.writeKpi(best);
                    improved = true;
                    ++improveCount;
                    boolean isSignificant = this.isSignificantImprovement(neighbor, best);
                    if (isSignificant) {
                        noImproveCount = 0;
                        ++significantImproveCount;
                        this.logImprovementDetails(best, initialFitnessLevel, initialFitness, totalIterations);
                        this.log(String.format("模拟退火+爬山法 - 迭代%d：找到更优解(显著)，fitness=%.4f", totalIterations, best.getFitness()), true);
                    } else {
                        this.log(String.format("模拟退火+爬山法 - 迭代%d：找到更优解(微小)，fitness=%.4f", totalIterations, best.getFitness()), true);
                    }
                }
            }

            if (!improved) {
                ++noImproveCount;
            }

            if (totalIterations <= stagnantWindow) {
                recentImprovements[totalIterations - 1] = improved ? 1 : 0;
            }

            temperature *= coolingRate;
            if (totalIterations % 10 == 0) {
                System.gc();
            }

            if (totalIterations % 30 == 0) {
                this.log(String.format("模拟退火+爬山法 - 迭代%d/%d：温度=%.4f, 接受数=%d, 改进数=%d, 无改进连续=%d, 总改进率=%.2f%%", totalIterations, maxIterations, temperature, acceptCount, improveCount, noImproveCount, totalIterations > 0 ? (double)improveCount / (double)totalIterations * (double)100.0F : (double)0.0F));
            }

            boolean shouldStop = false;
            String stopReason = "";
            if (temperature < temperatureThreshold) {
                shouldStop = true;
                stopReason = "温度低于阈值";
            } else if (noImproveCount >= maxNoImprove) {
                shouldStop = true;
                stopReason = String.format("连续无改进达到上限(%d次)", maxNoImprove);
            } else if (totalIterations >= stagnantWindow) {
                double recentImproveRate = this.calculateRecentImprovementRate(recentImprovements, stagnantWindow);
                if (recentImproveRate < improvementRateThreshold) {
                    shouldStop = true;
                    stopReason = String.format("最近%d次迭代改进率过低(%.2f%%)", stagnantWindow, recentImproveRate * (double)100.0F);
                }
            }

            if (shouldStop) {
                this.log(String.format("模拟退火+爬山法 - 提前停止：%s，迭代%d次，最终温度=%.4f", stopReason, totalIterations, temperature));
                this.logFinalSummary(best, initialFitnessLevel, initialFitness, improveCount, significantImproveCount, totalIterations);
                this.log("模拟退火+爬山法 - 切换到爬山法求精");
                HillClimbing hillClimbing = new HillClimbing(this.allOperations, this.orders, this.materials, this._entryRel, this.fitnessCalculator);
                Chromosome refined = hillClimbing.search(best, decoder, machines);
                this.log("模拟退火+爬山法 - 爬山法求精完成");
                return refined;
            }
        }

        this.log(String.format("模拟退火+爬山法 - 完成所有%d次迭代，最终fitness=%.4f", maxIterations, best.getFitness()), true);
        this.logFinalSummary(best, initialFitnessLevel, initialFitness, improveCount, significantImproveCount, totalIterations);
        return best;
    }

    public Chromosome search(Chromosome chromosome, TabuSearch tabusearch, VariableNeighborhoodSearch vns, GeneticDecoder decoder, List<Machine> machines) {
        this.log("模拟退火 - 开始执行", true);
        Chromosome current = vns.copyChromosome(chromosome);
        this.decode(decoder, current, machines);
        Chromosome best = this.lightCopy(current);
        this.writeKpi(best);
        double[] initialFitnessLevel = (double[])best.getFitnessLevel().clone();
        double initialFitness = best.getFitness();
        double temperature = (double)100.0F;
        double coolingRate = 0.9;
        double temperatureThreshold = (double)5.0F;
        int maxIterations = 80;
        int noImproveCount = 0;
        int maxNoImprove = 10;
        int stagnantWindow = 10;
        int[] recentImprovements = new int[stagnantWindow];
        double improvementRateThreshold = 0.001;
        this.log(String.format("模拟退火 - 参数配置：温度=%.1f, 降温率=%.2f, 阈值=%.1f, 最大迭代=%d, 最大无改进=%d", temperature, coolingRate, temperatureThreshold, maxIterations, maxNoImprove));
        int acceptCount = 0;
        int improveCount = 0;
        int significantImproveCount = 0;
        int totalIterations = 0;

        for(int i = 0; i < maxIterations; ++i) {
            totalIterations = i + 1;
            boolean improved = false;
            this.log(String.format("模拟退火 - 迭代%d：", totalIterations));
            decoder.DelOrder(current);
            Chromosome neighbor = vns.generateNeighbor(current);
            this.decode(decoder, neighbor, machines);
            if (tabusearch.isTabu(neighbor.getGeneStr()) && !this.isBetter(current, best)) {
                temperature *= coolingRate;
                ++noImproveCount;
                if (totalIterations <= stagnantWindow) {
                    recentImprovements[totalIterations - 1] = 0;
                }
            } else {
                double energyDifference = this.calculateEnergyDifference(neighbor, current);
                boolean accepted = false;
                if (energyDifference > (double)0.0F || this.rnd.nextDouble() < Math.exp(energyDifference / temperature)) {
                    current = neighbor;
                    ++acceptCount;
                    if (this.isBetter(neighbor, best)) {
                        best = this.lightCopy(neighbor);
                        tabusearch.addToTabuList(best.getGeneStr());
                        this.writeKpi(best);
                        improved = true;
                        ++improveCount;
                        boolean isSignificant = this.isSignificantImprovement(neighbor, best);
                        if (isSignificant) {
                            noImproveCount = 0;
                            ++significantImproveCount;
                            this.logImprovementDetails(best, initialFitnessLevel, initialFitness, totalIterations);
                            this.log(String.format("模拟退火 - 迭代%d：找到更优解(显著)，fitness=%.4f", totalIterations, best.getFitness()));
                        } else {
                            this.log(String.format("模拟退火 - 迭代%d：找到更优解(微小)，fitness=%.4f", totalIterations, best.getFitness()));
                        }
                    }
                }

                if (!improved) {
                    ++noImproveCount;
                }

                if (totalIterations <= stagnantWindow) {
                    recentImprovements[totalIterations - 1] = improved ? 1 : 0;
                }

                temperature *= coolingRate;
                if (totalIterations % 10 == 0) {
                    System.gc();
                }

                this.log(String.format("模拟退火 - 迭代%d/%d：温度=%.4f, 接受数=%d, 改进数=%d, 无改进连续=%d, 总改进率=%.2f%%", totalIterations, maxIterations, temperature, acceptCount, improveCount, noImproveCount, totalIterations > 0 ? (double)improveCount / (double)totalIterations * (double)100.0F : (double)0.0F));
                boolean shouldStop = false;
                String stopReason = "";
                if (temperature < temperatureThreshold) {
                    shouldStop = true;
                    stopReason = "温度低于阈值";
                } else if (noImproveCount >= maxNoImprove) {
                    shouldStop = true;
                    stopReason = String.format("连续无改进达到上限(%d次)", maxNoImprove);
                } else if (totalIterations >= stagnantWindow) {
                    double recentImproveRate = this.calculateRecentImprovementRate(recentImprovements, stagnantWindow);
                    if (recentImproveRate < improvementRateThreshold) {
                        shouldStop = true;
                        stopReason = String.format("最近%d次迭代改进率过低(%.2f%%)", stagnantWindow, recentImproveRate * (double)100.0F);
                    }
                }

                if (shouldStop) {
                    this.log(String.format("模拟退火 - 提前停止：%s，迭代%d次，最终温度=%.4f", stopReason, totalIterations, temperature));
                    this.logFinalSummary(best, initialFitnessLevel, initialFitness, improveCount, significantImproveCount, totalIterations);
                    return best;
                }
            }
        }

        this.log(String.format("模拟退火 - 完成所有%d次迭代，最终fitness=%.4f", maxIterations, best.getFitness()), true);
        this.logFinalSummary(best, initialFitnessLevel, initialFitness, improveCount, significantImproveCount, totalIterations);
        return best;
    }

    private void logImprovementDetails(Chromosome best, double[] initialFitnessLevel, double initialFitness, int iteration) {
        StringBuilder sb = new StringBuilder("模拟退火 - 改进详情: 迭代" + iteration + ", ");
        double[] currentFitness = best.getFitnessLevel();
        if (currentFitness != null && currentFitness.length > 0 && initialFitnessLevel != null && initialFitnessLevel.length > 0) {
            int minLength = Math.min(currentFitness.length, initialFitnessLevel.length);

            for(int i = 0; i < minLength; ++i) {
                double improvement = currentFitness[i] - initialFitnessLevel[i];
                sb.append(String.format("KPI%d: %.4f→%.4f(+%.4f) ", i + 1, initialFitnessLevel[i], currentFitness[i], improvement));
            }
        } else {
            sb.append("(KPI数据不可用) ");
        }

        double totalImprovement = best.getFitness() - initialFitness;
        sb.append(String.format("总Fitness: %.4f→%.4f(+%.4f)", initialFitness, best.getFitness(), totalImprovement));
        this.log(sb.toString());
    }

    private double calculateRecentImprovementRate(int[] recentImprovements, int windowSize) {
        int improveCount = 0;

        for(int i = 0; i < windowSize; ++i) {
            improveCount += recentImprovements[i];
        }

        return (double)improveCount / (double)windowSize;
    }

    private void logFinalSummary(Chromosome best, double[] initialFitnessLevel, double initialFitness, int improveCount, int significantImproveCount, int totalIterations) {
        StringBuilder sb = new StringBuilder("模拟退火 - 最终总结: ");
        double[] currentFitness = best.getFitnessLevel();
        sb.append(String.format("总迭代%d次, 成功改进%d次(显著%d次), 改进率%.2f%%. ", totalIterations, improveCount, significantImproveCount, totalIterations > 0 ? (double)improveCount / (double)totalIterations * (double)100.0F : (double)0.0F));
        if (currentFitness != null && currentFitness.length > 0 && initialFitnessLevel != null && initialFitnessLevel.length > 0) {
            int minLength = Math.min(currentFitness.length, initialFitnessLevel.length);

            for(int i = 0; i < minLength; ++i) {
                double improvement = currentFitness[i] - initialFitnessLevel[i];
                sb.append(String.format("KPI%d: %.4f→%.4f(%.2f%%) ", i + 1, initialFitnessLevel[i], currentFitness[i], initialFitnessLevel[i] > (double)0.0F ? improvement / initialFitnessLevel[i] * (double)100.0F : (double)0.0F));
            }
        } else {
            sb.append("(KPI数据不可用) ");
        }

        double totalImprovement = best.getFitness() - initialFitness;
        sb.append(String.format("总Fitness: %.4f→%.4f(%.2f%%)", initialFitness, best.getFitness(), initialFitness > (double)0.0F ? totalImprovement / initialFitness * (double)100.0F : (double)0.0F));
        this.log(sb.toString());
    }

    private void writeKpi(Chromosome chromosome) {
        String fitness = "";
        double[] fitness1 = chromosome.getFitnessLevel();
        if (fitness1 != null) {
            for(int i = 0; i < fitness1.length; ++i) {
                fitness = fitness + fitness1[i] + ",";
            }
        } else {
            fitness = "null (未计算)";
        }

        this.log(String.format("模拟退火 - kpi：%s", fitness), true);
        if (chromosome.getMakespan() != (double)0.0F) {
            FileHelper.writeLogFile(String.format("模拟退火 - kpi-Makespan: %f", chromosome.getMakespan()));
        }

        if (chromosome.getDelayTime() != (double)0.0F) {
            FileHelper.writeLogFile(String.format("模拟退火 - kpi-DelayTime: %f", chromosome.getDelayTime()));
        }

        if (chromosome.getTotalChangeoverTime() != (double)0.0F) {
            FileHelper.writeLogFile(String.format("模拟退火 - kpi-ChangeoverTime: %f", chromosome.getTotalChangeoverTime()));
        }

        if (chromosome.getMachineLoadStd() != (double)0.0F) {
            FileHelper.writeLogFile(String.format("模拟退火 - kpi-MachineLoad: %f", chromosome.getMachineLoadStd()));
        }

        if (chromosome.getTotalFlowTime() != (double)0.0F) {
            FileHelper.writeLogFile(String.format("模拟退火 - kpi-FlowTime: %f", chromosome.getTotalFlowTime()));
        }

    }

    private double calculateEnergyDifference(Chromosome neighbor, Chromosome current) {
        double[] neighborFitness = neighbor.getFitnessLevel();
        double[] currentFitness = current.getFitnessLevel();
        double diff = (double)0.0F;

        for(int i = 0; i < neighborFitness.length; ++i) {
            diff += neighborFitness[i] - currentFitness[i];
        }

        return diff;
    }

    private Map<Double, List<Entry>> groupOperationsByPriority() {
        Map<Double, List<Entry>> groups = new HashMap();

        for(Entry op : this.allOperations) {
            double priority = op.getPriority();
            ((List)groups.computeIfAbsent(priority, (k) -> new ArrayList())).add(op);
        }

        Map<Double, List<Entry>> filteredGroups = new HashMap();

        for(Map.Entry<Double, List<Entry>> entry : groups.entrySet()) {
            List<Entry> ops = (List)entry.getValue();
            boolean allSingleMachine = ops.stream().allMatch((opx) -> opx.getMachineOptions().size() <= 1);
            Set<Integer> groupIds = (Set)ops.stream().map(Entry::getGroupId).collect(Collectors.toSet());
            if (!allSingleMachine || groupIds.size() > 1) {
                filteredGroups.put((Double)entry.getKey(), ops);
            }
        }

        return filteredGroups;
    }

    private Map<Integer, Object> buildEntryKey() {
        Map<Integer, Object> index0 = new HashMap();
        Map<String, Entry> index = new HashMap();
        Map<Integer, Entry> index2 = new HashMap();

        for(Entry op : this.allOperations) {
            String key = op.getGroupId() + "_" + op.getSequence();
            index.put(key, op);
            index2.put(op.getId(), op);
        }

        index0.put(1, index);
        index0.put(2, index2);
        return index0;
    }

    private void decode(GeneticDecoder decoder, Chromosome chromosome, List<Machine> machines) {
        chromosome.setResult(new CopyOnWriteArrayList());
        if (this.cachedMachines == null) {
            this.cachedMachines = ProductionDeepCopyUtil.deepCopyList(machines, Machine.class);
        }

        chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(this.cachedMachines, Machine.class));
        chromosome.setOrders(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList(this.cachedOrders), Order.class));
        chromosome.setOperatRel(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList(this.cachedEntryRel), GroupResult.class));
        chromosome.setMaterials(ProductionDeepCopyUtil.deepCopyTreeMap(this.cachedMaterials, String.class, Material.class));
        chromosome.setAllOperations(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList(this.cachedAllOperations), Entry.class));
        List<GAScheduleResult> lockedOrders = (List)GlobalCacheUtil.get("locked_orders_" + chromosome.getScenarioID());
        if (lockedOrders != null && !lockedOrders.isEmpty()) {
            chromosome.setResultOld(ProductionDeepCopyUtil.deepCopyList(lockedOrders, GAScheduleResult.class));
            this.log("将 " + lockedOrders.size() + " 个锁定工单加载到初始种群中");
        } else {
            chromosome.setResultOld(new CopyOnWriteArrayList());
        }

        decoder.decodeChromosomeWithCache(chromosome, false);
    }

    private Chromosome lightCopy(Chromosome source) {
        Chromosome copy = new Chromosome();
        copy.setOperationSequencing(new CopyOnWriteArrayList(source.getOperationSequencing()));
        copy.setMachineSelection(new CopyOnWriteArrayList(source.getMachineSelection()));
        copy.setGlobalOpList(new CopyOnWriteArrayList(source.getGlobalOpList()));
        copy.setOrders(new CopyOnWriteArrayList(source.getOrders()));
        copy.setAllOperations(new CopyOnWriteArrayList(source.getAllOperations()));
        copy.setResult(source.getResult());
        copy.setMachines(source.getMachines());
        copy.setOperatRel(new CopyOnWriteArrayList(source.getOperatRel()));
        copy.setScenarioID(source.getScenarioID());
        copy.setBaseTime(source.getBaseTime());
        copy.setGenerateType(source.getGenerateType());
        copy.setFitnessLevel(source.getFitnessLevel());
        copy.setFitness(source.getFitness());
        return copy;
    }

    private boolean isBetter(Chromosome c1, Chromosome c2) {
        return this.fitnessCalculator.isBetter(c1, c2);
    }

    private boolean isSignificantImprovement(Chromosome newChromo, Chromosome oldChromo) {
        if (!this.isBetter(newChromo, oldChromo)) {
            return false;
        } else {
            double newFitness = newChromo.getFitness();
            double oldFitness = oldChromo.getFitness();
            return newFitness - oldFitness > 1.0E-4;
        }
    }

    private int Getbest(List<Chromosome> candidates, Chromosome best) {
        int bestidx = -1;
        if (best == null) {
            best = (Chromosome)candidates.get(0);
            bestidx = 0;
        }

        for(int i = 0; i < candidates.size(); ++i) {
            Chromosome candidate = (Chromosome)candidates.get(i);
            if (this.isBetter(candidate, best)) {
                bestidx = i;
            }
        }

        return bestidx;
    }
}
