package com.prolog.eis.service.stationOutbound.impl;

import com.prolog.eis.dto.eis4.*;
import com.prolog.eis.service.stationOutbound.StationObTaskDispatchService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

@Service
@RequiredArgsConstructor
public class StationObTaskDispatchServiceImpl implements StationObTaskDispatchService {
    @Override
    public List<StationOBCarryTaskDto> dispatch(StationOBDispatchDto stationOBDispatch) {
        List<StationOBCarryTaskDto> obCarryTaskList = new ArrayList<>();
        HashSet<String> noTaskAreaHs = new HashSet<>();
        HashSet<String> finishTaskContainerNoHs = this.findFinishTaskContainerNos(stationOBDispatch);
        while (true) {
            StationOBAreaDto bestArea = this.findBestTargetArea(stationOBDispatch, noTaskAreaHs);
            if (bestArea == null)//找不到最优区域则跳出
                return obCarryTaskList;

            StationOBContainerDto bestContainer = this.findBestContainer(stationOBDispatch, bestArea, finishTaskContainerNoHs, stationOBDispatch.getDistancePolicy());
            if (bestContainer == null) {
                noTaskAreaHs.add(bestArea.getAreaNo());
                continue;
            }

            bestArea.increaseUseLocatorCount();
            stationOBDispatch.removeContainer(bestContainer.getContainerNo());

            StationOBCarryTaskDto obCarryTask = new StationOBCarryTaskDto();
            obCarryTask.setContainerNo(bestContainer.getContainerNo());
            obCarryTask.setSourceAreaNo(bestContainer.getAreaNo());
            obCarryTask.setTargetAreaNo(bestArea.getAreaNo());

            obCarryTaskList.add(obCarryTask);
        }
    }

    private HashSet<String> findFinishTaskContainerNos(StationOBDispatchDto stationOBDispatch) {
        HashSet<String> finishContainerHs = new HashSet<>();
        for (StationOBAreaDto area : stationOBDispatch.getAreaList()) {
            for (StationOBAreaContainerDto container : area.getContainerList()) {
                if (container.isFinishTask()) {
                    finishContainerHs.add(container.getContainerNo());
                }
            }
        }

        return finishContainerHs;
    }

    private int compareArea(StationOBAreaDto area1, StationOBAreaDto area2) {
        if (area1.getPriority() < area2.getPriority()) {
            return -1;
        } else if (area1.getPriority() > area2.getPriority()) {
            return 1;
        }

        if (area1.getUseLocatorCount() < area2.getUseLocatorCount()) {
            return -1;
        } else if (area1.getUseLocatorCount() > area2.getUseLocatorCount()) {
            return 1;
        }

        //拆零总拣优先从站台号最大的站台索取托盘
        if (area1.getAreaNo() != null && area1.getAreaNo().startsWith("ZJ001-")
                && area2.getAreaNo() != null && area2.getAreaNo().startsWith("ZJ001-")) {
            int areaNoCompareResult = area1.getAreaNo().compareTo(area2.getAreaNo());
            if (areaNoCompareResult < 0) {
                return 1;
            } else if (areaNoCompareResult > 0) {
                return -1;
            } else {
                return 0;
            }
        }

        return 0;
    }

    /**
     * 找出有空货位，区域优先级最高，空位数最多的1个最优区域，所有区域已满则跳出；
     *
     * @param stationOBDispatchDto
     * @return
     */
    private StationOBAreaDto findBestTargetArea(StationOBDispatchDto stationOBDispatchDto, HashSet<String> noTaskAreaHs) {
        StationOBAreaDto bestArea = null;

        for (StationOBAreaDto area : stationOBDispatchDto.getAreaList()) {
            if (area.getFreeLocatorCount() <= 0)
                continue;

            if (noTaskAreaHs.contains(area.getAreaNo()))
                continue;

            if (bestArea == null) {
                bestArea = area;
                continue;
            }

            if (this.compareArea(area, bestArea) < 0) {
                bestArea = area;
                continue;
            }
        }

        return bestArea;
    }

    private boolean checkContainerTask(StationOBContainerDto container, String targetAreaNo) {
        for (StationOBContainerTaskDto containerTask : container.getContainerTaskList()) {
            if (containerTask.getTaskTargetAreaNoList().contains(targetAreaNo)) {
                return true;
            }
        }

        return false;
    }

    private int compareContainerYDistance(StationOBContainerDto container1, StationOBContainerDto container2, StationOBAreaDto area, int distancePolicy) {
        int distanceY1 = Math.abs(container1.getY() - area.getY());
        int distanceY2 = Math.abs(container2.getY() - area.getY());

        return distanceY1 - distanceY2;
    }

    private int compareContainerDistance(StationOBContainerDto container1, StationOBContainerDto container2, StationOBAreaDto area, int distancePolicy) {
        int distanceX1 = Math.abs(container1.getX() - area.getX());
        int distanceX2 = Math.abs(container2.getX() - area.getX());
        int distanceY1 = Math.abs(container1.getY() - area.getY());
        int distanceY2 = Math.abs(container2.getY() - area.getY());

        if (distancePolicy == 1) {
            if (distanceX1 != distanceX2) {
                return distanceX1 - distanceX2;
            } else {
                return distanceY1 - distanceY1;
            }
        } else if (distancePolicy == 2) {
            if (distanceY1 != distanceY2) {
                return distanceY1 - distanceY2;
            } else {
                return distanceX1 - distanceX1;
            }
        } else {
            int distance1 = distanceX1 + distanceY1;
            int distance2 = distanceX2 + distanceY2;
            return distance1 - distance2;
        }
    }

    private int compareContainer(int priority1, int priority2, StationOBPriorityTypeEnum priorityType) {
        if (priorityType == StationOBPriorityTypeEnum.ASC) {
            return priority1 - priority2;
        } else if (priorityType == StationOBPriorityTypeEnum.DESC) {
            return priority2 - priority1;
        } else {
            return 0;
        }
    }

    private int findIndex(StationOBAreaDto area, StationOBContainerDto container) {
        int index = 1000;
        try {
            index = area.getTaskSourceAreaNoList().indexOf(container.getAreaNo());
        } catch (Exception ex) {
        }

        return index;
    }

    private boolean checkInRange(int x, int y, StationOBRangeDto range) {
        if (range == null) {
            return true;
        }

        return x >= range.getMinX() && x <= range.getMaxX()
                && y >= range.getMinY() && y <= range.getMaxY();
    }

    private int compareInRange(int x1, int y1, int x2, int y2, StationOBRangeDto range) {
        if (range == null) {
            return 0;
        }

        boolean isInRange1 = this.checkInRange(x1, y1, range);
        boolean isInRange2 = this.checkInRange(x2, y2, range);
        if (isInRange1 && !isInRange2) {
            return -1;
        } else if (!isInRange1 && isInRange2) {
            return 1;
        } else {
            return 0;
        }
    }

    private int compareBoCi(String boCi1, String boCi2) {
        if (boCi1 != null && boCi2 == null) {
            return -1;
        } else if (boCi1 == null && boCi2 != null) {
            return 1;
        } else if (boCi1 != null && boCi2 != null) {
            return boCi1.compareTo(boCi2);
        } else {
            return 0;
        }
    }

    private int compareContainer(StationOBContainerDto container1, StationOBContainerDto container2, StationOBDispatchDto dispatch, StationOBAreaDto area, int distancePolicy) {
        int boCiCompareResult = this.compareBoCi(container1.getBoCi(), container2.getBoCi());
        if (boCiCompareResult != 0) {
            return boCiCompareResult;
        }

        int index1 = this.findIndex(area, container1);
        int index2 = this.findIndex(area, container2);
        if (index1 < index2) {
            return -1;
        } else if (index1 > index2) {
            return 1;
        }

        int inRangeCompareResult = this.compareInRange(container1.getX(), container1.getY(), container2.getX(), container2.getY(), area.getRange());
        if (inRangeCompareResult != 0) {
            return inRangeCompareResult;
        }

        int yDistanceCompareResult = this.compareContainerYDistance(container1, container2, area, distancePolicy);
        if (yDistanceCompareResult != 0) {
            return yDistanceCompareResult;
        }

        int priorityResult1 = this.compareContainer(container1.getPriority1(), container2.getPriority1(), dispatch.getPriorityType1());
        if (priorityResult1 != 0) {
            return priorityResult1;
        }

        int priorityResult2 = this.compareContainer(container1.getPriority2(), container2.getPriority2(), dispatch.getPriorityType2());
        if (priorityResult2 != 0) {
            return priorityResult2;
        }

        int priorityResult3 = this.compareContainer(container1.getPriority3(), container2.getPriority3(), dispatch.getPriorityType3());
        if (priorityResult3 != 0) {
            return priorityResult3;
        }

        int priorityResult4 = this.compareContainer(container1.getPriority4(), container2.getPriority4(), dispatch.getPriorityType4());
        if (priorityResult4 != 0) {
            return priorityResult4;
        }

        int priorityResult5 = this.compareContainer(container1.getPriority5(), container2.getPriority5(), dispatch.getPriorityType5());
        if (priorityResult5 != 0) {
            return priorityResult5;
        }

        int distanceCompareResult = this.compareContainerDistance(container1, container2, area, distancePolicy);
        return distanceCompareResult;
    }

    /**
     * 找出有该区域任务的，其他区域到位状态的，
     * 在该区域其他区域优先级最高、距离当前区域最近的托盘；找不到该区域的任务托盘，则排除该区域；
     *
     * @return
     */
    private StationOBContainerDto findBestContainer(StationOBDispatchDto dispatch, StationOBAreaDto area, HashSet<String> finishTaskContainerNoHs, int distancePolicy) {
        StationOBContainerDto bestContainer = null;
        for (StationOBAreaDto sourceArea : area.getTaskSourceAreaList()) {
            for (StationOBContainerDto container : sourceArea.getTaskContainerList()) {
                if (!finishTaskContainerNoHs.contains(container.getContainerNo()))
                    continue;

                boolean hasTask = this.checkContainerTask(container, area.getAreaNo());
                if (!hasTask) {
                    continue;
                }

                if (bestContainer == null) {
                    bestContainer = container;
                    continue;
                }

                if (this.compareContainer(container, bestContainer, dispatch, area, distancePolicy) < 0) {
                    bestContainer = container;
                    continue;
                }
            }
        }

        return bestContainer;
    }
}
