import { _decorator, Component, Vec2 } from 'cc';
const { ccclass, property } = _decorator;

// A*寻路节点
export class PathNode {
    x: number;
    y: number;
    gCost: number = 0;       // 从起点到当前点的实际代价
    hCost: number = 0;       // 从当前点到终点的启发式代价
    fCost: number = 0;       // 总代价 f = g + h
    parent: PathNode | null = null;
    walkable: boolean = true;

    constructor(x: number, y: number, walkable: boolean = true) {
        this.x = x;
        this.y = y;
        this.walkable = walkable;
    }

    calculateFCost() {
        this.fCost = this.gCost + this.hCost;
    }
}

@ccclass('AStarPathfinding')
export class AStarPathfinding extends Component {

    private grid: PathNode[][] = [];
    private gridWidth: number = 0;
    private gridHeight: number = 0;

    /**
     * 初始化寻路网格
     * @param width 网格宽度
     * @param height 网格高度
     * @param walkableData 可行走数据，0表示不可行走，1表示可行走
     */
    initializeGrid(width: number, height: number, walkableData: number[][]) {
        this.gridWidth = width;
        this.gridHeight = height;
        this.grid = [];

        for (let x = 0; x < width; x++) {
            this.grid[x] = [];
            for (let y = 0; y < height; y++) {
                const walkable = walkableData[y] && walkableData[y][x] === 1;
                this.grid[x][y] = new PathNode(x, y, walkable);
            }
        }
    }

    /**
     * 使用A*算法寻找路径
     * @param startX 起点X坐标
     * @param startY 起点Y坐标
     * @param targetX 终点X坐标
     * @param targetY 终点Y坐标
     * @returns 路径点数组，如果找不到路径返回空数组
     */
    findPath(startX: number, startY: number, targetX: number, targetY: number): Vec2[] {
        // 验证起点和终点是否有效
        if (!this.isValidPosition(startX, startY) || !this.isValidPosition(targetX, targetY)) {
            console.warn('起点或终点坐标无效');
            return [];
        }

        if (!this.grid[startX][startY].walkable || !this.grid[targetX][targetY].walkable) {
            console.warn('起点或终点不可行走');
            return [];
        }

        if (startX === targetX && startY === targetY) {
            return [new Vec2(startX, startY)];
        }

        // 重置所有节点
        this.resetNodes();

        const startNode = this.grid[startX][startY];
        const targetNode = this.grid[targetX][targetY];

        const openSet: PathNode[] = [];
        const closedSet: PathNode[] = [];

        openSet.push(startNode);

        while (openSet.length > 0) {
            // 找到f值最小的节点
            let currentNode = openSet[0];
            for (let i = 1; i < openSet.length; i++) {
                if (openSet[i].fCost < currentNode.fCost ||
                    (openSet[i].fCost === currentNode.fCost && openSet[i].hCost < currentNode.hCost)) {
                    currentNode = openSet[i];
                }
            }

            // 从开放列表移除当前节点，添加到关闭列表
            openSet.splice(openSet.indexOf(currentNode), 1);
            closedSet.push(currentNode);

            // 如果到达目标节点，重建路径
            if (currentNode === targetNode) {
                return this.retracePath(startNode, targetNode);
            }

            // 检查相邻节点
            const neighbors = this.getNeighbors(currentNode);
            for (const neighbor of neighbors) {
                if (!neighbor.walkable || closedSet.indexOf(neighbor) !== -1) {
                    continue;
                }

                const newGCost = currentNode.gCost + this.getDistance(currentNode, neighbor);

                if (newGCost < neighbor.gCost || openSet.indexOf(neighbor) === -1) {
                    neighbor.gCost = newGCost;
                    neighbor.hCost = this.getDistance(neighbor, targetNode);
                    neighbor.calculateFCost();
                    neighbor.parent = currentNode;

                    if (openSet.indexOf(neighbor) === -1) {
                        openSet.push(neighbor);
                    }
                }
            }
        }

        // 没有找到路径
        return [];
    }

    /**
     * 获取节点的相邻节点（4方向）
     */
    private getNeighbors(node: PathNode): PathNode[] {
        const neighbors: PathNode[] = [];

        // 四个方向：上、下、左、右
        const directions = [
            { x: 0, y: 1 },   // 上
            { x: 0, y: -1 },  // 下
            { x: -1, y: 0 },  // 左
            { x: 1, y: 0 }    // 右
        ];

        for (const dir of directions) {
            const checkX = node.x + dir.x;
            const checkY = node.y + dir.y;

            if (this.isValidPosition(checkX, checkY)) {
                neighbors.push(this.grid[checkX][checkY]);
            }
        }

        return neighbors;
    }

    /**
     * 获取两个节点之间的距离（曼哈顿距离）
     */
    private getDistance(nodeA: PathNode, nodeB: PathNode): number {
        const distX = Math.abs(nodeA.x - nodeB.x);
        const distY = Math.abs(nodeA.y - nodeB.y);
        return distX + distY;
    }

    /**
     * 重建路径
     */
    private retracePath(startNode: PathNode, endNode: PathNode): Vec2[] {
        const path: Vec2[] = [];
        let currentNode = endNode;

        while (currentNode !== startNode) {
            path.push(new Vec2(currentNode.x, currentNode.y));
            currentNode = currentNode.parent!;
        }

        path.push(new Vec2(startNode.x, startNode.y));
        path.reverse();

        return path;
    }

    /**
     * 重置所有节点的寻路参数
     */
    private resetNodes() {
        for (let x = 0; x < this.gridWidth; x++) {
            for (let y = 0; y < this.gridHeight; y++) {
                const node = this.grid[x][y];
                node.gCost = 0;
                node.hCost = 0;
                node.fCost = 0;
                node.parent = null;
            }
        }
    }

    /**
     * 检查位置是否有效
     */
    private isValidPosition(x: number, y: number): boolean {
        return x >= 0 && x < this.gridWidth && y >= 0 && y < this.gridHeight;
    }

    /**
     * 设置节点的可行走状态
     */
    setWalkable(x: number, y: number, walkable: boolean) {
        if (this.isValidPosition(x, y)) {
            this.grid[x][y].walkable = walkable;
        }
    }

    /**
     * 获取节点是否可行走
     */
    isWalkable(x: number, y: number): boolean {
        if (!this.isValidPosition(x, y)) {
            return false;
        }
        return this.grid[x][y].walkable;
    }
}