"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module LocatingElements
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.TentativePoint = void 0;
const core_geometry_1 = require("@itwin/core-geometry");
const AccuSnap_1 = require("./AccuSnap");
const HitDetail_1 = require("./HitDetail");
const IModelApp_1 = require("./IModelApp");
const Tool_1 = require("./tools/Tool");
const ViewTool_1 = require("./tools/ViewTool");
/**
 * @public
 * @extensions
 */
class TentativePoint {
    constructor() {
        this.isActive = false;
        this._point = new core_geometry_1.Point3d();
        this._rawPoint = new core_geometry_1.Point3d();
        this._viewPoint = new core_geometry_1.Point3d();
    }
    get _hotDistanceInches() { return 0.21; }
    onInitialized() { }
    setHitList(list) { this.tpHits = list; }
    /** @return true if the tentative point is currently active and snapped to an element. */
    get isSnapped() { return undefined !== this.currSnap; }
    /** @return The current snap path when TentativePoint.isSnapped or undefined. */
    getCurrSnap() { return this.currSnap; }
    getPoint() {
        const snap = this.currSnap;
        return !snap ? this._point : snap.adjustedPoint;
    }
    setPoint(point) {
        this.setCurrSnap(undefined);
        this.tpHits = undefined;
        this._point.setFrom(point);
    }
    clear(doErase) {
        if (doErase) {
            this.removeTentative();
            IModelApp_1.IModelApp.accuSnap.synchSnapMode();
        }
        IModelApp_1.IModelApp.accuSnap.destroy();
        this.isActive = false;
        this.setCurrSnap(undefined);
        this.tpHits = undefined;
    }
    removeTentative() {
        this._tentativePromise = undefined;
        if (!this.isActive)
            return;
        IModelApp_1.IModelApp.accuSnap.erase();
        if (this.getCurrSnap())
            IModelApp_1.IModelApp.viewManager.invalidateDecorationsAllViews();
        else
            this.viewport.invalidateDecorations();
        this.isActive = false;
    }
    setCurrSnap(newSnap) {
        if (newSnap)
            newSnap.setSnapPoint(newSnap.snapPoint, HitDetail_1.SnapHeat.InRange); // Reset adjustedPoint from pre-located snap and set SnapHeat...
        this.currSnap = newSnap;
    }
    showTentative() {
        if (this.isSnapped) {
            IModelApp_1.IModelApp.viewManager.invalidateDecorationsAllViews();
            IModelApp_1.IModelApp.accuSnap.displayToolTip(this._viewPoint, this.viewport, undefined); // eslint-disable-line @typescript-eslint/no-floating-promises
        }
        else {
            this.viewport.invalidateDecorations();
        }
        this.isActive = true;
    }
    getHitAndList(holder) {
        const hit = this.currSnap;
        if (hit) {
            holder.setHitList(this.tpHits);
            this.tpHits = undefined;
        }
        return hit;
    }
    onButtonEvent(ev) {
        switch (ev.button) {
            case Tool_1.BeButton.Data:
                if (!ev.isDown)
                    return; // cleared on down...
                break;
            case Tool_1.BeButton.Reset:
                if (ev.isDown)
                    return; // cleared on up...
                break;
            case Tool_1.BeButton.Middle:
                return;
        }
        this.removeTentative();
        IModelApp_1.IModelApp.accuSnap.synchSnapMode();
        this.setCurrSnap(undefined);
        this.tpHits = undefined;
    }
    decorate(context) {
        const viewport = context.viewport;
        if (!this.isActive || !viewport)
            return;
        const tpSize = Math.floor(viewport.pixelsPerInch * 0.4) + 0.5;
        const toSizeOutline = tpSize + 1;
        const position = context.viewport.worldToView(this._point);
        position.x = Math.floor(position.x) + 0.5;
        position.y = Math.floor(position.y) + 0.5;
        const drawDecoration = (ctx) => {
            ctx.beginPath();
            ctx.strokeStyle = "rgba(0,0,0,.5)";
            ctx.lineWidth = 3;
            ctx.moveTo(-toSizeOutline, 0);
            ctx.lineTo(toSizeOutline, 0);
            ctx.moveTo(0, -toSizeOutline);
            ctx.lineTo(0, toSizeOutline);
            ctx.stroke();
            ctx.beginPath();
            ctx.strokeStyle = "white";
            ctx.lineWidth = 1;
            if (!this.isSnapped)
                ctx.setLineDash([4, 1]);
            ctx.shadowColor = "black";
            ctx.shadowBlur = 5;
            ctx.moveTo(-tpSize, 0);
            ctx.lineTo(tpSize, 0);
            ctx.moveTo(0, -tpSize);
            ctx.lineTo(0, tpSize);
            ctx.stroke();
        };
        context.addCanvasDecoration({ position, drawDecoration });
    }
    async getSnap(newSearch) {
        // Use next hit from previous search when using tentative to cycle through hits...
        let thisHit = (!newSearch && undefined !== this.tpHits ? this.tpHits.getNextHit() : undefined);
        // Use existing AccuSnap hit list if one exists...
        if (undefined === thisHit) {
            this.tpHits = undefined;
            thisHit = IModelApp_1.IModelApp.accuSnap.getHitAndList(this);
        }
        if (undefined === thisHit) {
            // search for elements around the current raw point (search should not be affected by locks!)
            const aperture = (2.0 * this.viewport.pixelsFromInches(IModelApp_1.IModelApp.locateManager.apertureInches) / 2.0) + 1.5;
            const options = IModelApp_1.IModelApp.locateManager.options.clone(); // Copy to avoid changing out from under active Tool...
            const picker = IModelApp_1.IModelApp.locateManager.picker;
            options.hitSource = HitDetail_1.HitSource.TentativeSnap;
            if (0 === picker.doPick(this.viewport, this._rawPoint, aperture, options))
                return undefined;
            this.tpHits = picker.getHitList(true);
            thisHit = (undefined !== this.tpHits ? this.tpHits.getNextHit() : undefined);
        }
        else if (thisHit instanceof HitDetail_1.SnapDetail) {
            // Make the current AccuSnap the TentativePoint snap...
            return thisHit;
        }
        if (undefined === thisHit)
            return undefined;
        const snapModes = IModelApp_1.IModelApp.accuSnap.getActiveSnapModes(); // Get the list of point snap modes to consider
        if (1 === snapModes.length && HitDetail_1.SnapMode.Intersection === snapModes[0])
            snapModes.push(HitDetail_1.SnapMode.Nearest); // Add nearest when doing intersection by itself to support finding extended intersections...
        const thisSnap = await AccuSnap_1.AccuSnap.requestSnap(thisHit, snapModes, this._hotDistanceInches, IModelApp_1.IModelApp.accuSnap.keypointDivisor, this.tpHits);
        if (undefined !== thisSnap)
            IModelApp_1.IModelApp.accuDraw.onSnap(thisSnap); // AccuDraw can adjust nearest snap to intersection of circle (polar distance lock) or line (axis lock) with snapped to curve...
        return thisSnap;
    }
    static arePointsCloseEnough(pt1, pt2, pixelDistance) { return pt1.distance(pt2) < (pixelDistance + 1.5); }
    process(ev) {
        if (undefined !== this._tentativePromise)
            return;
        const currTool = IModelApp_1.IModelApp.toolAdmin.viewTool;
        if (currTool && currTool.inDynamicUpdate)
            return; // trying to tentative snap while view is changing isn't useful...
        const wasActive = this.isActive;
        this.removeTentative(); // remove the TP cross if it is already on the screen
        const lastPtView = this._viewPoint.clone();
        this.viewport = ev.viewport;
        this._point.setFrom(ev.point);
        this._rawPoint.setFrom(ev.rawPoint);
        this._viewPoint.setFrom(ev.viewPoint);
        const newSearch = (!this.isSnapped || !TentativePoint.arePointsCloseEnough(lastPtView, this._viewPoint, this.viewport.pixelsFromInches(IModelApp_1.IModelApp.locateManager.apertureInches)));
        const promise = this.getSnap(newSearch);
        this._tentativePromise = promise;
        promise.then((newSnap) => {
            // Ignore response if we're no longer interested in this tentative.
            if (this._tentativePromise === promise) {
                this._tentativePromise = undefined;
                this.setCurrSnap(newSnap); // Adopt the snap as current
                IModelApp_1.IModelApp.accuSnap.clear(); // make sure there's no AccuSnap active after a tentative point (otherwise we continually snap to it).
                if (this.isSnapped)
                    this._point.setFrom(this.currSnap.snapPoint);
                else if (wasActive && newSearch)
                    this._point.setFrom(ev.rawPoint);
                this.showTentative(); // show the TP cross
                if (this.isSnapped) {
                    IModelApp_1.IModelApp.toolAdmin.adjustSnapPoint();
                }
                else if (IModelApp_1.IModelApp.accuDraw.isActive) {
                    const point = this.getPoint().clone();
                    const vp = ev.viewport;
                    if (vp.isSnapAdjustmentRequired) {
                        IModelApp_1.IModelApp.toolAdmin.adjustPointToACS(point, vp, false);
                        const hit = new HitDetail_1.HitDetail({
                            testPoint: point,
                            viewport: vp,
                            hitSource: HitDetail_1.HitSource.TentativeSnap,
                            hitPoint: point,
                            sourceId: "",
                            priority: HitDetail_1.HitPriority.Unknown,
                            distXY: 0,
                            distFraction: 0,
                        });
                        const snap = new HitDetail_1.SnapDetail(hit);
                        this.setCurrSnap(snap);
                        IModelApp_1.IModelApp.toolAdmin.adjustSnapPoint();
                        this.setPoint(this.getPoint());
                    }
                    else {
                        IModelApp_1.IModelApp.accuDraw.adjustPoint(point, vp, false);
                        const savePoint = point.clone();
                        IModelApp_1.IModelApp.toolAdmin.adjustPointToGrid(point, vp);
                        if (!point.isExactEqual(savePoint))
                            IModelApp_1.IModelApp.accuDraw.adjustPoint(point, vp, false);
                        this.setPoint(point);
                    }
                }
                else {
                    IModelApp_1.IModelApp.toolAdmin.adjustPoint(this.getPoint(), ev.viewport);
                }
                IModelApp_1.IModelApp.accuDraw.onTentative();
                if (currTool && currTool instanceof ViewTool_1.ViewManip && currTool.viewHandles.hasHandle(ViewTool_1.ViewHandleType.TargetCenter))
                    currTool.updateTargetCenter(); // Change target center to tentative location...
                else
                    IModelApp_1.IModelApp.toolAdmin.updateDynamics(undefined, undefined, true); // Don't wait for motion to update dynamics...
            }
        });
    }
}
exports.TentativePoint = TentativePoint;
//# sourceMappingURL=TentativePoint.js.map