import Vector3 from "../../../core/geom/Vector3.js";
import { ResourceAccessKind } from "../../../core/model/ResourceAccessKind.js";
import { ResourceAccessSpecification } from "../../../core/model/ResourceAccessSpecification.js";
import { System } from '../../ecs/System.js';
import { obtainTerrain } from "../../ecs/terrain/util/obtainTerrain.js";
import { Transform } from '../../ecs/transform/Transform.js';
import GridPosition from '../position/GridPosition.js';
import { GridPosition2Transform } from './GridPosition2Transform.js';
import { GridPosition2TransformFlags } from "./GridPosition2TransformFlags.js";


export class GridPosition2TransformSystem extends System {
    constructor() {
        super();

        this.dependencies = [GridPosition2Transform, GridPosition, Transform];

        this.components_used = [
            ResourceAccessSpecification.from(Transform, ResourceAccessKind.Write)
        ];


        this.mapPoint = (x, y, v3) => {
            const terrain = obtainTerrain(this.entityManager.dataset);
            if (terrain !== null) {
                terrain.mapPointGrid2World(x, y, v3);
            }
        };

        this.data = [];
    }

    /**
     *
     * @param {GridPosition2Transform} component
     * @param {GridPosition} gp
     * @param {Transform} transform
     * @param {number} entity
     */
    link(component, gp, transform, entity) {
        const mapPoint = this.mapPoint;

        function sync() {
            const transformOffset = component.offset;

            const x = gp.x;
            const y = gp.y;

            const centerX = x + transformOffset.x;
            const centerY = y + transformOffset.y;

            setTransformFromGrid(mapPoint, centerX, centerY, transform);
        }

        sync.clear = function () {
            gp.onChanged.remove(sync);
            component.offset.onChanged.remove(sync);
        };

        gp.onChanged.add(sync);
        component.offset.onChanged.add(sync);

        if (component.getFlag(GridPosition2TransformFlags.Immediate)) {
            sync();
        }

        this.data[entity] = {
            sync
        };
    }

    /**
     *
     * @param {GridPosition2Transform} component
     * @param {GridPosition} gp
     * @param {Transform} transform
     * @param {number} entity
     */
    unlink(component, gp, transform, entity) {
        const { sync } = this.data[entity];

        sync.clear();

        delete this.data[entity];
    }

    reset() {
        const data = this.data;

        for (let entity in data) {

            if (!data.hasOwnProperty(entity)) {
                continue;
            }

            const { sync } = data[entity];

            sync.clear();
        }

        this.data = [];
    }
}

const v3 = new Vector3();

/**
 *
 * @param {function} mapPointGrid2world
 * @param {number} x
 * @param {number} y
 * @param {Transform} transform
 */
function setTransformFromGrid(mapPointGrid2world, x, y, transform) {
    mapPointGrid2world(x, y, v3);
    const transformPosition = transform.position;
    //check if positions are different
    if (v3.x !== transformPosition.x || v3.z !== transformPosition.z) {
        transformPosition.setXZ(v3.x, v3.z);
    }
}
