import { defineComponent, getCurrentInstance, ref, reactive, watch, onUnmounted, h, createCommentVNode, computed } from 'vue';
import { $, getVcParentInstance } from '../utils/private/vm';
import { useCommon } from '../composables';
import { hSlot } from '../utils/private/render';
import { position, pixelOffset, show, rectangle } from '../utils/cesium-props';
import { makeCartesian3, makeCartesian2, makeColor, makeRectangle } from '../utils/cesium-helpers';
import usePortal from '../composables/private/use-portal';
import h337 from 'heatmap.js';
import VcEntity from '../vc-entity';
import VcLayerImagery from '../vc-imagery-layer';
import { VcPrimitiveGround } from '../vc-primitives';

/*! *****************************************************************************
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());
    });
}

var VcOverlayHtml = defineComponent({
    name: 'VcOverlayHtml',
    props: Object.assign(Object.assign(Object.assign({}, position), pixelOffset), { autoHidden: {
            type: Boolean,
            default: true
        }, customClass: String, teleport: Object }),
    emits: ['beforeLoad', 'ready', 'destroyed', 'mouseenter', 'mouseleave', 'click'],
    setup(props, ctx) {
        const instance = getCurrentInstance();
        instance.cesiumClass = 'VcOverlayHtml';
        instance.cesiumEvents = [];
        const commonState = useCommon(props, ctx, instance);
        if (commonState === void 0) {
            return;
        }
        const { $services } = commonState;
        const canRender = ref(false);
        const rootRef = ref(null);
        const rootStyle = reactive({});
        const offset = ref(null);
        const position = ref(null);
        const lastCanvasPosition = ref(null);
        let unwatchFns = [];
        unwatchFns.push(watch(() => props.position, val => {
            position.value = makeCartesian3(val, $services.viewer.scene.globe.ellipsoid);
        }));
        unwatchFns.push(watch(() => props.pixelOffset, val => {
            offset.value = makeCartesian2(val);
        }));
        instance.createCesiumObject = () => __awaiter(this, void 0, void 0, function* () {
            return $(rootRef);
        });
        instance.mount = () => __awaiter(this, void 0, void 0, function* () {
            const { viewer } = $services;
            canRender.value = true;
            showPortal();
            offset.value = makeCartesian2(props.pixelOffset);
            position.value = makeCartesian3(props.position, viewer.scene.globe.ellipsoid);
            viewer.scene.preRender.addEventListener(onPreRender);
            return true;
        });
        instance.unmount = () => __awaiter(this, void 0, void 0, function* () {
            const { viewer } = $services;
            viewer.scene.preRender.removeEventListener(onPreRender);
            canRender.value = false;
            hidePortal();
            return true;
        });
        const onPreRender = () => {
            var _a, _b;
            const { viewer } = $services;
            const canvasPosition = viewer.scene.cartesianToCanvasCoordinates(position.value, {});
            if (Cesium.defined(canvasPosition) && !Cesium.Cartesian2.equals(lastCanvasPosition.value, canvasPosition)) {
                rootStyle.left = canvasPosition.x + ((_a = offset.value) === null || _a === void 0 ? void 0 : _a.x) + 'px';
                rootStyle.top = canvasPosition.y + ((_b = offset.value) === null || _b === void 0 ? void 0 : _b.y) + 'px';
                if (props.autoHidden) {
                    const cameraPosition = viewer.camera.position;
                    const cartographicPosition = viewer.scene.globe.ellipsoid.cartesianToCartographic(cameraPosition);
                    if (Cesium.defined(cartographicPosition)) {
                        let cameraHeight = cartographicPosition.height;
                        cameraHeight += 1 * viewer.scene.globe.ellipsoid.maximumRadius;
                        if (Cesium.Cartesian3.distance(cameraPosition, position.value) > cameraHeight) {
                            rootStyle.display = 'none';
                        }
                        else {
                            rootStyle.display = 'block';
                        }
                    }
                }
                else {
                    rootStyle.display = 'block';
                }
            }
            lastCanvasPosition.value = canvasPosition;
        };
        onUnmounted(() => {
            unwatchFns.forEach(item => item());
            unwatchFns = [];
        });
        const renderContent = () => {
            if (canRender.value) {
                return h('div', {
                    ref: rootRef,
                    class: `vc-html-container${props.customClass ? ' ' + props.customClass : ''}`,
                    style: rootStyle,
                    onMouseenter: onMouseenter,
                    onMouseleave: onMouseleave,
                    onClick: onClick
                }, hSlot(ctx.slots.default));
            }
            else {
                return createCommentVNode('v-if');
            }
        };
        const onClick = evt => {
            ctx.emit('click', evt);
        };
        const onMouseenter = evt => {
            ctx.emit('mouseenter', evt);
        };
        const onMouseleave = evt => {
            ctx.emit('mouseleave', evt);
        };
        const renderPortalContent = () => {
            return renderContent();
        };
        const { showPortal, hidePortal, renderPortal } = usePortal(instance, rootRef, renderPortalContent);
        if (props.teleport && props.teleport.to && !props.teleport.disabled) {
            return renderPortal;
        }
        else {
            return () => renderContent();
        }
    }
});

var VcOverlayHeatmap = defineComponent({
    name: 'VcOverlayHeatmap',
    props: Object.assign(Object.assign(Object.assign({}, show), rectangle), { min: Number, max: Number, data: [Array, String], options: Object, type: {
            type: String,
            default: 'primitive'
        }, segments: {
            type: Array,
            default: () => []
        }, projection: {
            type: String,
            default: '3857'
        } }),
    emits: ['beforeLoad', 'ready', 'destroyed'],
    setup(props, ctx) {
        const instance = getCurrentInstance();
        instance.cesiumClass = 'VcOverlayHeatmap';
        instance.cesiumEvents = [];
        const commonState = useCommon(props, ctx, instance);
        if (commonState === void 0) {
            return;
        }
        const rootRef = ref(null);
        const project = ref(null);
        const defaultOptions = {
            minCanvasSize: 700,
            maxCanvasSize: 2000,
            radiusFactor: 60,
            spacingFactor: 1.5,
            maxOpacity: 0.8,
            minOpacity: 0.1,
            blur: 0.85,
            gradient: {
                '.3': 'blue',
                '.65': 'yellow',
                '.8': 'orange',
                '.95': 'red'
            },
            xField: 'x',
            yField: 'y',
            valueField: 'value',
            container: undefined
        };
        const coordinates = ref(null);
        const material = ref(null);
        const image = ref(null);
        const childRef = ref(null);
        const appearance = ref(null);
        const canRender = ref(false);
        const config = ref(null);
        const vcParent = getVcParentInstance(instance);
        vcParent.proxy.createPromise.then(() => {
            canRender.value = true;
        });
        const options = computed(() => {
            return Object.assign({}, defaultOptions, props.options);
        });
        let unwatchFns = [];
        unwatchFns.push(watch(() => image, val => {
            material.value.image = val.value;
            appearance.value.material.uniforms.image = val.value;
        }, {
            deep: true
        }));
        unwatchFns.push(watch(() => props.data, (newVal, oldVal) => {
            if (!instance.mounted) {
                return;
            }
            const heatmapInstance = instance.cesiumObject;
            if (Array.isArray(newVal) && Array.isArray(oldVal)) {
                setData(newVal, heatmapInstance);
                image.value = heatmapInstance.getDataURL();
            }
            else {
                commonState.reload();
            }
        }, {
            deep: true
        }));
        unwatchFns.push(watch(() => [props.max, props.min], vals => {
            const heatmapInstance = instance.cesiumObject;
            heatmapInstance.setDataMax(vals[0]);
            heatmapInstance.setDataMin(vals[1]);
            image.value = heatmapInstance.getDataURL();
        }));
        unwatchFns.push(watch(() => [props.type, props.projection, props.rectangle], vals => {
            commonState.reload();
        }));
        unwatchFns.push(watch(() => props.options, val => {
            const heatmapInstance = instance.cesiumObject;
            heatmapInstance.configure(val);
            image.value = heatmapInstance.getDataURL();
        }, {
            deep: true
        }));
        instance.createCesiumObject = () => __awaiter(this, void 0, void 0, function* () {
            const { WebMercatorProjection, GeographicProjection } = Cesium;
            project.value = props.projection === '3857' ? new WebMercatorProjection() : new GeographicProjection();
            const id = getID();
            config.value = getConfig(props.rectangle);
            const container = document.createElement('div');
            if (Cesium.defined(id)) {
                container.setAttribute('id', id);
            }
            container.setAttribute('style', 'width: ' + config.value.width + 'px; height: ' + config.value.height + 'px; margin: 0px; display: none;');
            document.body.appendChild(container);
            options.value.container = container;
            if (props.segments.length) {
                options.value.gradient = {};
                const Δ = props.max - props.min;
                for (let i = 0; i < props.segments.length; i++) {
                    options.value.gradient[`${(props.segments[i][0] - props.min) / Δ}`] = makeColor(props.segments[i][1]).toCssColorString();
                }
            }
            const heatmapInstance = h337.create(options.value);
            container.children[0].setAttribute('id', id + '-hm');
            if (Array.isArray(props.data)) {
                setData(props.data, heatmapInstance);
                material.value = new Cesium.ImageMaterialProperty({
                    image: image.value,
                    transparent: true
                });
                appearance.value = new Cesium.MaterialAppearance({
                    material: new Cesium.Material({
                        fabric: {
                            type: 'Image',
                            uniforms: {
                                image: image.value
                            }
                        }
                    })
                });
            }
            return heatmapInstance;
        });
        instance.unmount = () => __awaiter(this, void 0, void 0, function* () {
            document.body.removeChild(instance.cesiumObject._config.container);
            return true;
        });
        const getID = (len) => {
            let id = '';
            const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
            for (let i = 0; i < (len || 8); i++) {
                id += possible.charAt(Math.floor(Math.random() * possible.length));
            }
            return id;
        };
        const getConfig = bounds => {
            const rectangle = makeRectangle(bounds);
            const swmb = project.value.project(new Cesium.Cartographic(rectangle.west, rectangle.south));
            const nemb = project.value.project(new Cesium.Cartographic(rectangle.east, rectangle.north));
            const mbb = {
                north: nemb.y,
                east: nemb.x,
                south: swmb.y,
                west: swmb.x
            };
            let width = mbb.east > 0 && mbb.west < 0 ? mbb.east + Math.abs(mbb.west) : Math.abs(mbb.east - mbb.west);
            let height = mbb.north > 0 && mbb.south < 0 ? mbb.north + Math.abs(mbb.south) : Math.abs(mbb.north - mbb.south);
            let factor = 1;
            if (width > height && width > options.value.maxCanvasSize) {
                factor = width / options.value.maxCanvasSize;
                if (height / factor < options.value.minCanvasSize) {
                    factor = height / options.value.minCanvasSize;
                }
            }
            else if (height > width && height > options.value.maxCanvasSize) {
                factor = height / options.value.maxCanvasSize;
                if (height / factor < options.value.minCanvasSize) {
                    factor = width / options.value.minCanvasSize;
                }
            }
            else if (width < height && width < options.value.minCanvasSize) {
                factor = width / options.value.minCanvasSize;
                if (height / factor > options.value.maxCanvasSize) {
                    factor = height / options.value.maxCanvasSize;
                }
            }
            else if (height < width && height < options.value.minCanvasSize) {
                factor = height / options.value.minCanvasSize;
                if (width / factor > options.value.maxCanvasSize) {
                    factor = width / options.value.maxCanvasSize;
                }
            }
            width = width / factor;
            height = height / factor;
            if (!Cesium.defined(options.value.radius)) {
                options.value.radius = width > height ? width / options.value.radiusFactor : height / options.value.radiusFactor;
            }
            const spacing = options.value.radius * options.value.spacingFactor;
            const xoffset = mbb.west;
            const yoffset = mbb.south;
            width = Math.round(width + spacing * 2);
            height = Math.round(height + spacing * 2);
            mbb.west -= spacing * factor;
            mbb.east += spacing * factor;
            mbb.south -= spacing * factor;
            mbb.north += spacing * factor;
            const swmw = project.value.unproject(new Cesium.Cartesian3(mbb.west, mbb.south));
            const nemw = project.value.unproject(new Cesium.Cartesian3(mbb.east, mbb.north));
            const mwb = {
                north: Cesium.Math.toDegrees(nemw.latitude),
                east: Cesium.Math.toDegrees(nemw.longitude),
                south: Cesium.Math.toDegrees(swmw.latitude),
                west: Cesium.Math.toDegrees(swmw.longitude)
            };
            coordinates.value = mwb;
            return {
                height,
                width,
                factor,
                xoffset,
                yoffset,
                spacing
            };
        };
        const setData = (data, heatmapInstance) => {
            if (data) {
                const { height, xoffset, yoffset, factor, spacing } = config.value;
                const xField = options.value.xField;
                const yField = options.value.yField;
                const datas = [];
                for (let i = 0; i < data.length; i++) {
                    const gp = data[i];
                    if (!Cesium.defined(gp.id)) {
                        gp.id = i;
                    }
                    const mp = project.value.project(Cesium.Cartographic.fromDegrees(gp[xField], gp[yField]));
                    const hp = {
                        x: Math.round((mp.x - xoffset) / factor + spacing),
                        y: Math.round((mp.y - yoffset) / factor + spacing)
                    };
                    hp.y = height - hp.y;
                    if (gp[options.value.valueField] || gp[options.value.valueField] === 0) {
                        hp[options.value.valueField] = gp[options.value.valueField];
                    }
                    if (hp[options.value.valueField] > props.max || hp[options.value.valueField] < props.min) {
                        continue;
                    }
                    datas.push(hp);
                }
                heatmapInstance.setData({
                    min: props.min,
                    max: props.max,
                    data: datas
                });
                image.value = heatmapInstance.getDataURL();
            }
        };
        onUnmounted(() => {
            unwatchFns.forEach(item => item());
            unwatchFns = [];
        });
        Object.assign(instance.proxy, {
            rootRef: rootRef,
            childRef: childRef
        });
        return () => {
            if (canRender.value) {
                const child = [];
                if (props.type === 'entity' && image.value) {
                    child.push(h(VcEntity, {
                        ref: childRef,
                        show: props.show,
                        rectangle: {
                            coordinates: coordinates.value,
                            material: material.value
                        }
                    }));
                }
                else if (props.type === 'primitive') {
                    child.push(h(VcPrimitiveGround, {
                        ref: childRef,
                        show: props.show,
                        appearance: appearance.value,
                        releaseGeometryInstances: false,
                        geometryInstances: new Cesium.GeometryInstance({
                            geometry: new Cesium.RectangleGeometry({
                                rectangle: makeRectangle(coordinates.value)
                            })
                        })
                    }));
                }
                else if (props.type === 'imagery-layer' && image.value) {
                    child.push(h(VcLayerImagery, {
                        ref: childRef,
                        show: props.show,
                        imageryProvider: new Cesium.SingleTileImageryProvider({
                            url: image.value,
                            rectangle: makeRectangle(coordinates.value)
                        })
                    }));
                }
                return h('i', {
                    ref: rootRef,
                    class: 'vc-overlay-heatmap',
                    style: 'display: none !important'
                }, child);
            }
            else {
                return createCommentVNode('v-if');
            }
        };
    }
});

const components = [
    VcOverlayHtml,
    VcOverlayHeatmap
];
const install = (app) => {
    components.forEach(cmp => {
        app.component(cmp.name, cmp);
    });
};
var index = {
    install
};

export default index;
export { VcOverlayHeatmap, VcOverlayHtml };
