import Rectangle from "../../../core/geom/2d/Rectangle.js";
import { SurfacePoint3 } from "../../../core/geom/3d/SurfacePoint3.js";
import Vector3 from "../../../core/geom/Vector3.js";
import { ResourceAccessKind } from "../../../core/model/ResourceAccessKind.js";
import { ResourceAccessSpecification } from "../../../core/model/ResourceAccessSpecification.js";
import { VisualTip } from "../../../view/tooltip/VisualTip.js";
import Mesh from "../../graphics/ecs/mesh/Mesh.js";
import { MeshSystem } from "../../graphics/ecs/mesh/MeshSystem.js";
import { modelHitTest } from "../../graphics/util/modelHitTest.js";
import { System } from "../System.js";
import { TooltipComponent } from "./TooltipComponent.js";

const ray_source = new Vector3();
const ray_direction = new Vector3();

const hit = new SurfacePoint3();

export class TooltipComponentSystem extends System {
    /**
     *
     * @param {GraphicsEngine} graphics
     * @param {TooltipManager} tooltips
     * @param {PointerDevice} pointer
     * @param {Localization} localization
     */
    constructor({ graphics, tooltips, pointer, localization }) {
        super();

        this.dependencies = [TooltipComponent];

        this.components_used = [
            ResourceAccessSpecification.from(Mesh, ResourceAccessKind.Read)
        ];

        /**
         *
         * @type {GraphicsEngine}
         */
        this.graphics = graphics;

        /**
         *
         * @type {TooltipManager}
         */
        this.tooltips = tooltips;

        /**
         *
         * @type {PointerDevice}
         */
        this.pointer = pointer;

        /**
         *
         * @type {Localization}
         */
        this.localization = localization;

        /**
         *
         * @type {number}
         */
        this.time_since_position_update = 0;

        /**
         * Delay before tooltip appears, in seconds
         * @type {number}
         */
        this.display_delay = 0.2;

        /**
         * Screen-space rectangle, used by the tooltip system as the anchor to point to
         * @type {Rectangle}
         */
        this.target = new Rectangle();

        /**
         *
         * @type {VisualTip}
         */
        this.tip = null;


        /**
         *
         * @type {number}
         * @private
         */
        this.__current_tip_entity = -1;
    }


    __clearTip() {
        if (this.tip !== null) {
            this.tooltips.remove(this.tip);

            this.tip = null;
        }

        this.__current_tip_entity = -1;
    }

    __updateActiveBounds() {
        this.time_since_position_update = 0;

        if (this.tip !== null) {
            const tip_target = this.__computeTooltipTarget();

            if (tip_target !== this.__current_tip_entity) {
                this.__clearTip();
            } else {
                this.__updateTipTargetBounds();
            }
        }
    }

    __updateTipTargetBounds() {

        const pointer_position = this.pointer.position;

        this.target.set(pointer_position.x - 10, pointer_position.y - 10, 20, 20);
    }

    /**
     *
     * @return {boolean}
     * @private
     */
    __isOnRenderCanvas() {
        return this.graphics.domElement === this.pointer.getTargetElement();
    }

    __computeTooltipTarget() {

        //check what's under pointer
        if (!this.__isOnRenderCanvas()) {
            //only do work when tapping on game field
            return -1;
        }

        const ecd = this.entityManager.dataset;

        if (ecd === null) {
            return -1;
        }

        const pointer_position = this.pointer.position;


        this.graphics.viewportProjectionRay(pointer_position.x, pointer_position.y, ray_source, ray_direction);

        // test against meshes
        const meshSystem = this.entityManager.getSystem(MeshSystem);

        const entities = [];

        meshSystem.traverseVisible((mesh, entity) => {

            if (ecd.getComponent(entity, TooltipComponent) !== undefined) {

                entities.push(entity);

            }

        });

        const hit_entity = modelHitTest(hit, entities, ecd, this.graphics, pointer_position.x, pointer_position.y);


        return hit_entity;
    }

    async startup(entityManager) {

        this.pointer.position.onChanged.add(this.__updateActiveBounds, this);

    }

    async shutdown(entityManager) {

        this.pointer.position.onChanged.remove(this.__updateActiveBounds, this);

    }

    update(timeDelta) {
        this.time_since_position_update += timeDelta;

        if (this.time_since_position_update < this.display_delay) {
            return;
        }

        const ecd = this.entityManager.dataset;

        if (ecd === null) {
            return;
        }

        if (this.tip !== null) {
            // already presenting a tip
            this.__updateActiveBounds();

            return;
        }


        const hit_entity = this.__computeTooltipTarget();

        if (hit_entity === -1) {

            return;

        }

        const pointer_position = this.pointer.position;

        this.target.set(pointer_position.x - 10, pointer_position.y - 10, 20, 20);

        const key = ecd.getComponent(hit_entity, TooltipComponent).key;

        const localization = this.localization;

        const localized_string = localization.getString(key);


        const tip = new VisualTip(this.target, () => {

            const view = this.tooltips.getGML().compile(localized_string);

            return view;

        });


        this.tip = tip;
        this.tooltips.add(tip);

        this.__current_tip_entity = hit_entity;
    }
}
