package de.miaurizius.jgame2d.aStar;

import de.miaurizius.jgame2d.core.GamePanel;

import java.util.ArrayList;

public class PathFinder {

    GamePanel panel;
    Node[][] node;
    ArrayList<Node> openList = new ArrayList<>();
    public ArrayList<Node> pathList = new ArrayList<>();
    Node startNode, goalNode, currentNode;
    boolean goalReached;
    int step;

    public PathFinder(GamePanel panel) {
        this.panel = panel;
        instantiateNodes();
    }

    private void instantiateNodes() {
        node = new Node[panel.maxWorldCol][panel.maxWorldRow];
        int col = 0;
        int row = 0;
        while(col < panel.maxWorldCol && row < panel.maxWorldRow) {
            node[col][row] = new Node(col, row);
            col++;
            if(col != panel.maxWorldCol) continue;
            col = 0;
            row++;
        }
    }

    private void resetNodes() {
        // RESET OPEN LIST
        int col = 0, row = 0;
        while(col < panel.maxWorldCol && row < panel.maxWorldRow) {
            node[col][row].open = false;
            node[col][row].checked = false;
            node[col][row].solid = false;

            col++;
            if(col != panel.maxWorldCol) continue;
            col = 0;
            row++;
        }

        // RESET OTHER SETTINGS
        openList.clear();
        pathList.clear();
        goalReached = false;
        step = 0;
    }
    public void setNodes(int startCol, int startRow, int goalCol, int goalRow) {
        resetNodes();
        startNode = node[startCol][startRow];
        currentNode = startNode;
        goalNode = node[goalCol][goalRow];
        openList.add(currentNode);

        int col = 0, row = 0;
        while(col < panel.maxWorldCol && row < panel.maxWorldRow) {
            // SOLID NODES
            int tileNum = panel.tileM.mapTileNum[panel.currentMap.getIndex()][col][row];
            if(panel.tileM.tile[tileNum].collision) {
                node[col][row].solid = true;
            }
            for(int i = 0; i < panel.iTile.length; i++) {
                if(panel.iTile[panel.currentMap.getIndex()][i] != null && panel.iTile[panel.currentMap.getIndex()][i].destructible) {
                    int itCol = panel.iTile[panel.currentMap.getIndex()][i].worldX/panel.tileSize;
                    int itRow = panel.iTile[panel.currentMap.getIndex()][i].worldY/panel.tileSize;
                    node[itCol][itRow].solid = true;
                }
            }

            // SET COST
            getCost(node[col][row]);
            col++;
            if(col != panel.maxWorldCol) continue;
            col = 0;
            row++;
        }
    }
    private void getCost(Node node) {
        // G COST
        int xD = Math.abs(node.col - startNode.col);
        int yD = Math.abs(node.row - startNode.row);
        node.gCost = xD + yD;

        // H COST
        xD = Math.abs(node.col - goalNode.col);
        yD = Math.abs(node.row - goalNode.row);
        node.hCost = xD + yD;

        // F COST
        node.fCost = node.gCost + node.hCost;
    }
    public boolean search() {
        while(!goalReached && step < 500) {
            int col = currentNode.col;
            int row = currentNode.row;

            currentNode.checked = true;
            openList.remove(currentNode);

            if(row - 1 >= 0) openNode(node[col][row-1]);
            if(col - 1 >= 0) openNode(node[col-1][row]);

            if(row + 1 < panel.maxWorldRow) openNode(node[col][row+1]);
            if(col + 1 < panel.maxWorldCol) openNode(node[col+1][row]);

            int bestNodeIndex = 0;
            int bestNodeFCost = 999;
            for(int i = 0; i < openList.size(); i++) {
                if(openList.get(i).fCost < bestNodeFCost) {
                    bestNodeIndex = i;
                    bestNodeFCost = openList.get(i).fCost;
                }
                else if(openList.get(i).fCost == bestNodeFCost) if(openList.get(i).gCost < openList.get(bestNodeIndex).gCost) bestNodeIndex = i;
            }
            if(openList.isEmpty()) break;
            currentNode = openList.get(bestNodeIndex);
            if(currentNode == goalNode) {
                goalReached = true;
                trackPath();
            }
            step++;
        }
        return goalReached;
    }
    private void openNode(Node node) {
        if(node.open || node.checked || node.solid) return;
        node.open = true;
        node.parent = currentNode;
        openList.add(node);
    }
    private void trackPath() {
        Node current = goalNode;
        while(current != startNode) {
            pathList.add(0, current);
            current = current.parent;
        }
    }

}
