import { Texture, SpriteMaterial, Sprite, Vector2, Object3D, Vector3 } from 'three';
import { Element } from '@ktmap/map';

/******************************************************************************
Copyright (c) Microsoft Corporation.

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */

function __awaiter(thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
}

class PoiMarker extends Element {
    constructor(options) {
        super();
        Object.defineProperty(this, "options", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: options
        });
        Object.defineProperty(this, "_scale", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "sprite", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
    }
    complete() { }
    // 重写父类 _inject
    __inject(map, _scale) {
        super.__inject(map);
        this._scale = _scale;
        return this;
    }
    __init() { }
    _create() {
        return __awaiter(this, void 0, void 0, function* () {
            const canvas = yield this.generateCanvas(this.options);
            this.sprite = this.makeCanvasSprite(canvas, this.options);
            return this.sprite;
        });
    }
    setPosition(position) {
        this.sprite && this.sprite.position.copy(position);
        this.map.reDraw();
    }
    generateCanvas({ icon, scale = 1 }) {
        const canvas = document.createElement('canvas');
        const context = canvas.getContext('2d');
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => {
                const w = img.width * scale;
                const h = img.height * scale;
                // 根据文字内容获取宽度
                canvas.width = w * this._scale;
                canvas.height = h * this._scale;
                // canvas.width = w;
                // canvas.height = h;
                context.drawImage(img, 0, 0, canvas.width, canvas.height);
                resolve(canvas);
            };
            img.src = icon;
        });
    }
    makeCanvasSprite(canvas, { name, renderOrder, position }) {
        const texture = new Texture(canvas);
        texture.needsUpdate = true;
        const spriteMaterial = new SpriteMaterial({
            map: texture,
            transparent: true,
            depthTest: false,
            // depthTest: this.depthTest,
            depthWrite: false
        });
        const sprite = new Sprite(spriteMaterial);
        // sprite.name = OBJECT_NAME.POILabel
        sprite.name = name;
        // sprite.userData.name = name
        sprite.renderOrder = renderOrder;
        sprite.center = new Vector2(0.5, 0);
        sprite.position.copy(position);
        // sprite.position.z += 10
        // this.group.add(sprite)
        // reDraw()
        return sprite;
    }
}
Object.defineProperty(PoiMarker, "NAME", {
    enumerable: true,
    configurable: true,
    writable: true,
    value: 'PoiMarker'
});

class PoisLayer extends Element {
    constructor({ name, collision }) {
        super();
        Object.defineProperty(this, "_scale", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 4
        });
        Object.defineProperty(this, "options", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "_group", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        this.options = { name, collision };
        this._group = new Object3D();
        this._group.name = name;
    }
    complete() { }
    __init() {
        this.map.scene.add(this._group);
        this.map.updateSizeMap.set(this, () => this._updateSize());
    }
    add(...poiMarker) {
        return __awaiter(this, void 0, void 0, function* () {
            for (const item of poiMarker) {
                item.__inject(this.map, this._scale);
                const marker = yield item._create();
                this._group.add(marker);
            }
            this.map.reDraw();
            return this;
        });
    }
    remove(...poiMarker) {
        var _a, _b, _c, _d, _e, _f;
        for (const item of poiMarker) {
            (_c = (_b = (_a = item === null || item === void 0 ? void 0 : item.sprite) === null || _a === void 0 ? void 0 : _a.geometry) === null || _b === void 0 ? void 0 : _b.dispose) === null || _c === void 0 ? void 0 : _c.call(_b);
            (_f = (_e = (_d = item === null || item === void 0 ? void 0 : item.sprite) === null || _d === void 0 ? void 0 : _d.material) === null || _e === void 0 ? void 0 : _e.dispose) === null || _f === void 0 ? void 0 : _f.call(_e);
            (item === null || item === void 0 ? void 0 : item.sprite) && this._group.remove(item === null || item === void 0 ? void 0 : item.sprite);
        }
        this.map.reDraw();
        return this;
    }
    /**
     * @desc 暴露出更新size方法，使其可以在 cameraControls 控制时候更新
     */
    _updateSize() {
        const sprites = this._group ? this._group.children : [];
        if (sprites.length === 0)
            return;
        //  是否需要碰撞检测
        if (this.options.collision) {
            for (let i = 0; i < sprites.length; i++) {
                const sprite = sprites[i];
                sprite.visible = true;
            }
        }
        for (let i = 0; i < sprites.length; i++) {
            const compareSprite = sprites[i];
            // @ts-ignore
            const canvas = compareSprite.material.map.image;
            if (canvas) {
                const position = compareSprite.position;
                const scale = this.getPoiScale(position, {
                    w: canvas.width,
                    h: canvas.height
                });
                // 缩小 4 倍在 retina 屏幕会比较高清
                compareSprite.scale.set(scale[0] / this._scale, scale[1] / this._scale, 1.0);
                // 是否需要碰撞检测
                if (this.options.collision && compareSprite.visible) {
                    for (let j = i + 1; j < sprites.length; j++) {
                        const sprite = sprites[j];
                        if (this.isMarkerCollision(compareSprite, sprite)) {
                            sprite.visible = false;
                        }
                    }
                }
            }
        }
    }
    getPoiScale(position, poiRect) {
        const distance = this.map.camera.position.distanceTo(position); // 相机和标注点的距离
        const top = Math.tan(((this.map.camera.fov / 2) * Math.PI) / 180) * distance; // camera.fov 相机的拍摄距离
        // var meterPerPixel = (2 * top) / window.innerHeight;
        const meterPerPixel = (2 * top) / this.map.$map.clientHeight;
        const scaleX = poiRect.w * meterPerPixel;
        const scaleY = poiRect.h * meterPerPixel;
        return [scaleX, scaleY, 1.0];
    }
    /**
     * @desc 检测两个标注sprite是否碰撞
     */
    isMarkerCollision(compareSprite, sprite) {
        const comparePosition = this.toScreenPos([
            compareSprite.position.x,
            compareSprite.position.y,
            compareSprite.position.z
        ], this.map.camera);
        const spritePosition = this.toScreenPos([sprite.position.x, sprite.position.y, sprite.position.z], this.map.camera);
        const image1 = compareSprite.material.map.image;
        const image2 = sprite.material.map.image;
        const w1 = image1.width / this._scale;
        const h1 = image1.height / this._scale;
        const x1 = comparePosition.x - w1 / 2;
        const y1 = comparePosition.y - h1 / 2;
        const w2 = image2 ? image2.width / this._scale : 0;
        const h2 = image2 ? image2.height / this._scale : 0;
        const x2 = spritePosition.x - w2 / 2; // 点2左下角的xy点
        const y2 = spritePosition.y - h2 / 2;
        if (x1 >= x2 && x1 >= x2 + w2) {
            return false;
        }
        else if (x1 <= x2 && x1 + w1 <= x2) {
            return false;
        }
        else if (y1 >= y2 && y1 >= y2 + h2) {
            return false;
        }
        else if (y1 <= y2 && y1 + h1 <= y2) {
            return false;
        }
        else {
            return true;
        }
    }
    /**
     * @desc three世界坐标转为屏幕坐标
     */
    toScreenPos(position, camera) {
        const worldVector = new Vector3(position[0], position[1], position[2]);
        const standardVector = worldVector.project(camera); // 世界坐标转标准设备坐标
        const a = this.map.$map.clientWidth / 2;
        const b = this.map.$map.clientHeight / 2;
        const x = Math.round(standardVector.x * a + a); // 标准设备坐标转屏幕坐标
        const y = Math.round(-standardVector.y * b + b); // 标准设备坐标转屏幕坐标
        return {
            x,
            y
        };
    }
}
Object.defineProperty(PoisLayer, "NAME", {
    enumerable: true,
    configurable: true,
    writable: true,
    value: 'PoisLayer'
});

export { PoiMarker, PoisLayer };
