

import { world, system, Entity, ItemUseOnAfterEvent } from "@minecraft/server";

class PortalManager {
    constructor() {
        this._initEvents();
    }

    _initEvents() {
        world.afterEvents.itemUseOn.subscribe((e) => { this._onPortalBlockInteract(e) });

        system.afterEvents.scriptEventReceive.subscribe((e) => {
            if (e.id !== 'eternal:checkPortal') return; this._checkIfPortalBrokenScriptEvent(e.sourceEntity);
        })
    }


    /**
     * 
     * @param {Entity} e 
     */
    _checkIfPortalBrokenScriptEvent(e) {
        let location = e.location;
        location = { x: e.location.x - 0.5, y: e.location.y - 1, z: e.location.z - 0.5 };

        try {
            if (!this._detectPortalShape(location, e.dimension)) e.remove();
        } catch { }

    }

    /**
     *  
     * @param {ItemUseOnAfterEvent} e
     */
    _onPortalBlockInteract(e) {
        if (e.itemStack.typeId !== 'minecraft:flint_and_steel') return;
        if (e.block.dimension.id !== 'minecraft:overworld') return;

        if (e.block.typeId !== 'eternal:theia_portal_block' && e.block.typeId !== 'eternal:primarine_gold_block') return;

        const portalCorners = this._detectPortalShape(e.block.location, e.block.dimension);
        if (!portalCorners) return;

        // Ignite portal

        const findLowestCoordinate = (coords) => {
            return coords.reduce((lowest, current) => {
                return {
                    x: Math.min(lowest.x, current.x),
                    y: Math.min(lowest.y, current.y),
                    z: Math.min(lowest.z, current.z)
                };
            });
        }

        const determinePrimaryAxis = (start, end) => {
            const dx = end.x - start.x;
            const dz = end.z - start.z;

            if (Math.abs(dx) > Math.abs(dz)) return 'x';
            else return 'z';
        };


        const bottom_left = findLowestCoordinate(Array.from(Object.values(portalCorners)));
        const primaryAxis = determinePrimaryAxis(portalCorners.corner_1, portalCorners.corner_2);

        if (primaryAxis === 'z') {
            const fillArea = `${bottom_left.x} ${bottom_left.y + 1} ${bottom_left.z + 1} ${bottom_left.x} ${bottom_left.y + 3} ${bottom_left.z + 2}`;
            const entityLocation = { x: bottom_left.x + 0.5, y: bottom_left.y + 1, z: bottom_left.z + 1.5 };
            const coordString = `${entityLocation.x} ${entityLocation.y} ${entityLocation.z}`;
            e.source.runCommandAsync(`execute positioned ${coordString} unless entity @e[r=1,type=eternal:portal] run summon eternal:portal ~~~ 90`);
            e.source.runCommandAsync(`fill ${fillArea} eternal:theia_portal_north`);
        }
        else {
            const fillArea = `${bottom_left.x + 1} ${bottom_left.y + 1} ${bottom_left.z} ${bottom_left.x + 2} ${bottom_left.y + 3} ${bottom_left.z}`;
            const entityLocation = { x: bottom_left.x + 1.5, y: bottom_left.y + 1, z: bottom_left.z + 0.5 };
            const coordString = `${entityLocation.x} ${entityLocation.y} ${entityLocation.z}`;

            e.source.runCommandAsync(`execute positioned ${coordString} unless entity @e[r=1,type=eternal:portal] run summon eternal:portal ~~~ `);
            e.source.runCommandAsync(`fill ${fillArea} eternal:theia_portal_north`);
        }

        //console.warn(JSON.stringify(corner_1), JSON.stringify(corner_2), JSON.stringify(corner_3), JSON.stringify(corner_4))
    }


    /**
     * 
     * @param {location} location 
     * @returns {{corner_1: location, corner_2: location, corner_3: location, corner_4: location}} - the 4 corner points of the portal
     */
    _detectPortalShape(location, dimension) {
        const isPortalCorner = (block) => block.typeId === 'eternal:theia_portal_block';
        const isPortalEdge = (block) => block.typeId === 'eternal:primarine_gold_block';

        const directions3D = [
            { dx: 1, dy: 0, dz: 0 },
            { dx: 0, dy: 1, dz: 0 },
            { dx: 0, dy: 0, dz: 1 },
            { dx: -1, dy: 0, dz: 0 },
            { dx: 0, dy: -1, dz: 0 },
            { dx: 0, dy: 0, dz: -1 }
        ];

        const directionsXZ = [
            { dx: 0, dz: 3 },
            { dx: 0, dz: -3 },
            { dx: 3, dz: 0 },
            { dx: -3, dz: 0 }
        ];

        const directionsY = [
            { dy: 4 },
            { dy: -4 }
        ];

        const findCorner = (location, directions, updateLocation) => {
            for (const direction of directions) {
                let currentLocation = { ...location };
                while (true) {
                    updateLocation(currentLocation, direction);
                    const block = dimension.getBlock(currentLocation);
                    if (isPortalCorner(block)) return currentLocation;
                    if (!isPortalEdge(block)) break;
                }
            }
        }

        const findCornerXYZ = (location) => {
            return findCorner(location, directions3D, (loc, direction) => {
                loc.x += direction.dx;
                loc.y += direction.dy;
                loc.z += direction.dz;
            });
        }

        const findCornerX = (corner) => {
            return findCorner(corner, directionsXZ, (loc, direction) => {
                loc.x += direction.dx;
                loc.z += direction.dz;
            });
        }

        const findCornerY = (corner) => {
            return findCorner(corner, directionsY, (loc, direction) => {
                loc.y += direction.dy;
            });
        }


        const generateIntermediatePoints = (start, end, numPoints) => {
            const points = [];
            for (let i = 1; i < numPoints; i++) { // Start from 1 and end before numPoints to exclude endpoints
                const t = i / numPoints;
                const x = Math.round(start.x + t * (end.x - start.x));
                const y = Math.round(start.y + t * (end.y - start.y));
                const z = Math.round(start.z + t * (end.z - start.z));
                points.push({ x, y, z });
            }
            return points;
        };

        // Detect portal corners
        const corner_1 = findCornerXYZ(location);
        const corner_2 = findCornerX(corner_1);
        const corner_3 = findCornerY(corner_1);
        const corner_4 = findCornerY(corner_2);


        const intermediatePoints = [
            ...generateIntermediatePoints(corner_1, corner_2, 3),
            ...generateIntermediatePoints(corner_1, corner_3, 4),
            ...generateIntermediatePoints(corner_3, corner_4, 3),
            ...generateIntermediatePoints(corner_2, corner_4, 4)
        ];



        for (const point of intermediatePoints) {

            if (!isPortalEdge(dimension.getBlock(point))) return;
        }

        return { corner_1, corner_2, corner_3, corner_4 };
    }
}

const portalManager = new PortalManager();



/**
 * @typedef location
 * @property {number} x
 * @property {number} y
 * @property {number} z
 */