// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.npc.navigation;

import it.unimi.dsi.fastutil.objects.ObjectIterator;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
import javax.annotation.Nullable;
import java.util.List;
import com.hypixel.hytale.server.npc.movement.controllers.MotionController;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import com.hypixel.hytale.logger.HytaleLogger;

public class AStarDebugBase
{
    public static final char CENTER = ' ';
    public static final char CROSS = '\u253c';
    public static final char HLINE = '\u2500';
    public static final char VLINE = '\u2502';
    public static final char OPEN_NODE = '\u25c6';
    public static final char CLOSED_NODE = '\u25c7';
    public static final char CLOSED_PATH_NODE = '\u25ef';
    public static final char OPEN_PATH_NODE = '\u25c9';
    public static final char BLOCKED_NODE = '\u00d7';
    public static final char START_POSITION = '@';
    public static final char END_POSITION = '\u03a9';
    public static final String BORDER_PATTERN;
    public static final String CENTER_PATTERN;
    protected AStarBase aStarBase;
    protected HytaleLogger logger;
    protected HytaleLogger.Api loggerInfo;
    
    public AStarDebugBase(final AStarBase base, @Nonnull final HytaleLogger logger) {
        this.aStarBase = base;
        this.logger = logger;
        this.loggerInfo = logger.at(Level.INFO);
    }
    
    public void dumpOpens(final MotionController controller) {
        final int openCount = this.aStarBase.getOpenCount();
        final List<AStarNode> openNodes = this.aStarBase.getOpenNodes();
        int maxLength = -1;
        for (int i = 0; i < openCount; ++i) {
            final int length = openNodes.get(i).getLength();
            if (length > maxLength) {
                maxLength = length;
            }
        }
        this.loggerInfo.log("== A* iter=%s opens=%s total=%s maxLength=%s %s", this.aStarBase.getIterations(), openCount, this.aStarBase.getVisitedBlocks().size(), maxLength, this.getExtraLogString(controller));
        for (int i = 0; i < openCount; ++i) {
            this.loggerInfo.log("%2d %s", i, openNodes.get(i).toString());
        }
    }
    
    public void dumpPath() {
        AStarNode node = this.aStarBase.getPath();
        this.loggerInfo.log("== A* Path iter=%s opens=%s total=%s", this.aStarBase.getIterations(), this.aStarBase.getOpenCount(), this.aStarBase.getVisitedBlocks().size());
        while (node != null) {
            this.loggerInfo.log("%s", node.toString());
            node = node.getNextPathNode();
        }
    }
    
    public void dumpMap(final boolean drawPath, final MotionController controller) {
        final int openCount = this.aStarBase.getOpenCount();
        final List<AStarNode> openNodes = this.aStarBase.getOpenNodes();
        AStarNode start = null;
        boolean finalPath = false;
        if (drawPath) {
            final AStarNode path = this.aStarBase.getPath();
            if (path != null) {
                start = path;
                finalPath = true;
            }
            else if (openCount > 0) {
                start = openNodes.get(openCount - 1);
            }
        }
        this.dumpMap(start, finalPath, controller);
    }
    
    public void dumpMap(@Nullable AStarNode pathNode, final boolean isFinalPath, final MotionController controller) {
        final long startPositionIndex = this.aStarBase.getStartPositionIndex();
        final Long2ObjectMap<AStarNode> visitedBlocks = this.aStarBase.getVisitedBlocks();
        int s = AStarBase.xFromIndex(startPositionIndex);
        int e = this.getDumpMapRegionX(s);
        int minX;
        int maxX;
        if (s < e) {
            minX = s;
            maxX = e;
        }
        else {
            minX = e;
            maxX = s;
        }
        s = AStarBase.zFromIndex(startPositionIndex);
        e = this.getDumpMapRegionZ(s);
        int minZ;
        int maxZ;
        if (s < e) {
            minZ = s;
            maxZ = e;
        }
        else {
            minZ = e;
            maxZ = s;
        }
        ObjectIterator<Long2ObjectMap.Entry<AStarNode>> fastIterator = Long2ObjectMaps.fastIterator(visitedBlocks);
        while (fastIterator.hasNext()) {
            final AStarNode node = (AStarNode)fastIterator.next().getValue();
            final int x = AStarBase.xFromIndex(node.getPositionIndex());
            final int z = AStarBase.zFromIndex(node.getPositionIndex());
            if (x < minX) {
                minX = x;
            }
            if (x > maxX) {
                maxX = x;
            }
            if (z < minZ) {
                minZ = z;
            }
            if (z > maxZ) {
                maxZ = z;
            }
        }
        final int rows = maxZ - minZ + 1;
        final int columns = maxX - minX + 1;
        final int offset = minX & 0x1;
        final boolean evenStart = (minZ & 0x1) == 0x0;
        final String first = "'" + (evenStart ? AStarDebugBase.CENTER_PATTERN : AStarDebugBase.BORDER_PATTERN).substring(offset, offset + columns);
        final String second = "'" + (evenStart ? AStarDebugBase.BORDER_PATTERN : AStarDebugBase.CENTER_PATTERN).substring(offset, offset + columns);
        final StringBuilder[] map = new StringBuilder[rows];
        for (int i = 0; i < rows; i += 2) {
            map[i] = new StringBuilder(first);
            if (i + 1 < rows) {
                map[i + 1] = new StringBuilder(second);
            }
        }
        fastIterator = Long2ObjectMaps.fastIterator(visitedBlocks);
        while (fastIterator.hasNext()) {
            final AStarNode node2 = (AStarNode)fastIterator.next().getValue();
            this.plot(node2.getPositionIndex(), node2.isInvalid() ? '\u00d7' : (node2.isOpen() ? '\u25c6' : '\u25c7'), map, minX, minZ);
        }
        final int openCount = this.aStarBase.getOpenCount();
        int maxLength;
        if (pathNode != null) {
            maxLength = pathNode.getLength();
            while (pathNode != null) {
                this.plot(pathNode.getPositionIndex(), pathNode.isOpen() ? '\u25c9' : '\u25ef', map, minX, minZ);
                pathNode = (isFinalPath ? pathNode.getNextPathNode() : pathNode.getPredecessor());
            }
        }
        else {
            final List<AStarNode> openNodes = this.aStarBase.getOpenNodes();
            int index = openCount;
            maxLength = 0;
            while (--index >= 0) {
                final int pos = openCount - index;
                if (pos > 51) {
                    break;
                }
                this.plot(openNodes.get(index).getPositionIndex(), (char)((pos >= 26) ? (pos - 26 + 97) : (pos + 65)), map, minX, minZ);
            }
        }
        this.plot(startPositionIndex, '@', map, minX, minZ);
        this.drawMapFinish(map, minX, minZ);
        this.loggerInfo.log("== A* iter=%s, opens=%s total=%s maxLength=%s %s", this.aStarBase.getIterations(), openCount, visitedBlocks.size(), maxLength, this.getExtraLogString(controller));
        for (final StringBuilder stringBuilder : map) {
            this.loggerInfo.log(stringBuilder.toString());
        }
    }
    
    protected void plot(final long positionIndex, final char character, @Nonnull final StringBuilder[] map, final int minX, final int minZ) {
        final int row = AStarBase.zFromIndex(positionIndex) - minZ;
        final int column = AStarBase.xFromIndex(positionIndex) - minX + 1;
        map[row].setCharAt();
    }
    
    protected void drawMapFinish(final StringBuilder[] map, final int minX, final int minZ) {
    }
    
    protected int getDumpMapRegionZ(final int def) {
        return def;
    }
    
    protected int getDumpMapRegionX(final int def) {
        return def;
    }
    
    @Nonnull
    protected String getExtraLogString(final MotionController controller) {
        return "";
    }
    
    static {
        BORDER_PATTERN = "\u2500\u253c".repeat(1025);
        CENTER_PATTERN = " \u2502".repeat(1025);
    }
}
