import { LayoutZIndex } from "./../../constant/layout";

import { Projection } from "./projection";

import { ComponentTypeEnum } from "../interface/type";

import { BaseComponent } from "../base/base-component";

import { eachSeries } from "../../util/model";

import { mergeSpec } from "@visactor/vutils-extension";

import { ChartEvent } from "../../constant/event";

import { PREFIX } from "../../constant/base";

import { SeriesTypeEnum } from "../../series/interface/type";

import { Zoomable } from "../../interaction/zoom/zoomable";

import { isValid, mixin, isNil, Matrix, isEqual } from "@visactor/vutils";

import { Factory } from "../../core/factory";

import { DEFAULT_MAP_LOOK_UP_KEY } from "../../constant/data";

export function projectionName(key, id) {
    return `${PREFIX}_${id}_${key}`;
}

export class GeoCoordinate extends BaseComponent {
    constructor() {
        super(...arguments), this.type = ComponentTypeEnum.geoCoordinate, this.name = ComponentTypeEnum.geoCoordinate, 
        this.layoutType = "none", this.layoutZIndex = LayoutZIndex.Mark, this._projectionSpec = {
            name: projectionName(this.type, this.id),
            type: "mercator"
        }, this._actualScale = 1, this._initialScale = 1, this.effect = {
            scaleUpdate: () => {
                this.coordinateHelper();
            }
        }, this._handleChartZoom = (params, event) => {
            var _a, _b, _c, _d, _e, _f;
            let scale = params.zoomDelta;
            const _lastActualScale = this._actualScale;
            return this._actualScale *= scale, this._actualScale < (null === (_a = this._spec.zoomLimit) || void 0 === _a ? void 0 : _a.min) ? (this._actualScale = null === (_b = this._spec.zoomLimit) || void 0 === _b ? void 0 : _b.min, 
            scale = (null === (_c = this._spec.zoomLimit) || void 0 === _c ? void 0 : _c.min) / _lastActualScale) : this._actualScale > (null === (_d = this._spec.zoomLimit) || void 0 === _d ? void 0 : _d.max) && (this._actualScale = null === (_e = this._spec.zoomLimit) || void 0 === _e ? void 0 : _e.max, 
            scale = (null === (_f = this._spec.zoomLimit) || void 0 === _f ? void 0 : _f.max) / _lastActualScale), 
            this.zoom(scale, [ params.zoomX, params.zoomY ]), {
                scale: scale,
                totalScale: this._actualScale
            };
        }, this.pan = (delta = [ 0, 0 ]) => {
            var _a, _b, _c;
            const t = null !== (_b = null === (_a = this._projection) || void 0 === _a ? void 0 : _a.translate()) && void 0 !== _b ? _b : [ 0, 0 ];
            let t_x = t[0], t_y = t[1];
            t_x += delta[0], t_y += delta[1], null === (_c = this._projection) || void 0 === _c || _c.translate([ t_x, t_y ]);
        };
    }
    get longitudeField() {
        return this._longitudeField;
    }
    get latitudeField() {
        return this._latitudeField;
    }
    get projectionSpec() {
        return this._projectionSpec;
    }
    setProjection(projectionSpec) {
        this._projectionSpec = Object.assign(Object.assign({}, projectionSpec), {
            name: this._projectionSpec.name
        });
    }
    getZoom() {
        return this._actualScale;
    }
    static getSpecInfo(chartSpec) {
        if (isNil(chartSpec)) return null;
        const specInfos = [];
        return chartSpec.region.forEach(((r, i) => {
            if ("geo" === r.coordinate) {
                const spec = Object.assign(Object.assign({}, r), {
                    padding: 0
                });
                specInfos.push({
                    spec: spec,
                    regionIndex: i,
                    type: ComponentTypeEnum.geoCoordinate,
                    specInfoPath: [ "component", "geoCoordinate", i ]
                });
            }
        })), specInfos;
    }
    setAttrFromSpec() {
        var _a, _b, _c;
        super.setAttrFromSpec(), this._spec.roam && this.initZoomable(this.event, this._option.mode), 
        this._projectionSpec = mergeSpec(this._projectionSpec, this._spec.projection), this._projectionSpec.zoom > (null === (_a = this._spec.zoomLimit) || void 0 === _a ? void 0 : _a.max) && (this._projectionSpec.zoom = this._spec.zoomLimit.max), 
        this._projectionSpec.zoom < (null === (_b = this._spec.zoomLimit) || void 0 === _b ? void 0 : _b.min) && (this._projectionSpec.zoom = this._spec.zoomLimit.min), 
        this._actualScale = null !== (_c = this._projectionSpec.zoom) && void 0 !== _c ? _c : 1, 
        this._initialScale = this._actualScale, this._longitudeField = this._spec.longitudeField, 
        this._latitudeField = this._spec.latitudeField;
    }
    created() {
        super.created(), this._regions = this._option.getRegionsInIndex([ this._option.regionIndex ]), 
        this.initProjection(), this.coordinateHelper(), this.initEvent(), this._initCenterCache();
    }
    dispatchZoom(zoomDelta, center) {
        const scaleCenter = center || {
            x: this.getLayoutStartPoint().x + this.getLayoutRect().width / 2,
            y: this.getLayoutStartPoint().y + this.getLayoutRect().height / 2
        }, {scale: scale, totalScale: totalScale} = this._handleChartZoom({
            zoomDelta: zoomDelta,
            zoomX: scaleCenter.x,
            zoomY: scaleCenter.y
        });
        1 !== scale && this.event.emit("zoom", {
            scale: scale,
            scaleCenter: scaleCenter,
            totalScale: totalScale,
            model: this
        });
    }
    initEvent() {
        var _a, _b, _c;
        this.event.on(ChartEvent.scaleUpdate, {
            filter: ({model: model}) => (null == model ? void 0 : model.id) === this.id
        }, this.effect.scaleUpdate.bind(this));
        const {roam: roam} = this._spec;
        if (roam) {
            let zoom = !0, drag = !0, blank = !1;
            !0 !== roam && (zoom = null === (_a = roam.zoom) || void 0 === _a || _a, drag = null === (_b = roam.drag) || void 0 === _b || _b, 
            blank = null !== (_c = roam.blank) && void 0 !== _c && _c), zoom && this.initZoomEventOfRegions(this._regions, null, this._handleChartZoom), 
            drag && this.initDragEventOfRegions(this._regions, blank ? null : () => !0, this.pan), 
            this._regions.forEach((r => {
                r.getSeries().forEach((s => {
                    s.event.on("zoom", (e => (s.handleZoom(e), !0))), s.event.on("panmove", (e => (s.handlePan(e), 
                    !0)));
                }));
            }));
        }
    }
    initProjection() {
        var _a;
        this._projection = new Projection(this._projectionSpec), null !== this._projection.projection || null === (_a = this._option) || void 0 === _a || _a.onError("unsupported projection type!");
    }
    coordinateHelper() {
        const helper = {
            longitudeField: this._longitudeField,
            latitudeField: this._latitudeField,
            dataToPosition: this.dataToPosition.bind(this),
            dataToLongitude: this.dataToLongitude.bind(this),
            dataToLatitude: this.dataToLatitude.bind(this),
            shape: this.shape.bind(this),
            getCoordinateId: () => this.id
        };
        this._regions.forEach((r => {
            r.getSeries().forEach((s => {
                s.type === SeriesTypeEnum.map || s.type === SeriesTypeEnum.pictogram ? s.setCoordinateHelper(helper) : (s.setXAxisHelper(Object.assign(Object.assign({}, helper), {
                    isContinuous: !0,
                    dataToPosition: (values, option) => {
                        var _a;
                        let value = values[0];
                        if (isNil(value) && (null == option ? void 0 : option.datum)) {
                            const nameFieldValue = option.datum[s.getDimensionField()[0]];
                            value = null === (_a = this._centerCache.get(nameFieldValue)) || void 0 === _a ? void 0 : _a.x;
                        }
                        return this.dataToLongitude(value);
                    },
                    valueToPosition: (value, option) => {
                        var _a;
                        if (isNil(value) && (null == option ? void 0 : option.datum)) {
                            const nameFieldValue = option.datum[s.getDimensionField()[0]];
                            value = null === (_a = this._centerCache.get(nameFieldValue)) || void 0 === _a ? void 0 : _a.x;
                        }
                        return this.dataToLongitude(value);
                    },
                    getFields: () => [ this._longitudeField ],
                    getAxisType: () => this.type,
                    getAxisId: () => this.id,
                    isInverse: () => !1
                })), s.setYAxisHelper(Object.assign(Object.assign({}, helper), {
                    isContinuous: !0,
                    dataToPosition: (values, option) => {
                        var _a;
                        let value = values[0];
                        if (isNil(value) && (null == option ? void 0 : option.datum)) {
                            const nameFieldValue = option.datum[s.getDimensionField()[0]];
                            value = null === (_a = this._centerCache.get(nameFieldValue)) || void 0 === _a ? void 0 : _a.y;
                        }
                        return this.dataToLatitude(value);
                    },
                    valueToPosition: (value, option) => {
                        var _a;
                        if (isNil(value) && (null == option ? void 0 : option.datum)) {
                            const nameFieldValue = option.datum[s.getDimensionField()[0]];
                            value = null === (_a = this._centerCache.get(nameFieldValue)) || void 0 === _a ? void 0 : _a.y;
                        }
                        return this.dataToLatitude(value);
                    },
                    getFields: () => [ this._latitudeField ],
                    getAxisType: () => this.type,
                    getAxisId: () => this.id,
                    isInverse: () => !1
                })));
            }));
        }));
    }
    onLayoutEnd() {
        this.setLayoutRect(this._regions[0].getLayoutRect()), this.setLayoutStartPosition(this._regions[0].getLayoutStartPoint());
        const {width: width, height: height} = this.getLayoutRect(), {translate: translate, scale: scale, center: center} = this.evaluateProjection([ 0, 0 ], [ width, height ]);
        translate && this._projection.translate(translate), scale && this._projection.scale(scale), 
        center && this._projection.center(center), eachSeries(this._regions, (s => {
            var _a;
            if (s.type === SeriesTypeEnum.map || s.type === SeriesTypeEnum.pictogram) {
                null === (_a = s.areaPath) || void 0 === _a || _a.clear();
                const pathGroup = s.getRootMark().getProduct();
                pathGroup && pathGroup.attribute.postMatrix && pathGroup.setAttributes({
                    postMatrix: new Matrix
                });
            }
        })), this._actualScale = this._initialScale, super.onLayoutEnd();
    }
    collectFeatures() {
        const features = [];
        return this._regions.forEach((r => {
            r.getSeries().forEach((s => {
                var _a, _b;
                s.type !== SeriesTypeEnum.map && s.type !== SeriesTypeEnum.pictogram || features.push(...null !== (_b = null === (_a = s.getMapViewData()) || void 0 === _a ? void 0 : _a.latestData) && void 0 !== _b ? _b : []);
            }));
        })), features;
    }
    dataToPosition(values = []) {
        var _a;
        const point = null === (_a = this._projection) || void 0 === _a ? void 0 : _a.project([ values[0], values[1] ]);
        return {
            x: null == point ? void 0 : point[0],
            y: null == point ? void 0 : point[1]
        };
    }
    dataToLatitude(lat) {
        var _a;
        const point = null === (_a = this._projection) || void 0 === _a ? void 0 : _a.project([ 0, lat ]);
        return null == point ? void 0 : point[1];
    }
    dataToLongitude(lon) {
        var _a;
        const point = null === (_a = this._projection) || void 0 === _a ? void 0 : _a.project([ lon, 0 ]);
        return null == point ? void 0 : point[0];
    }
    zoom(p, anchor = [ 0, 0 ]) {
        var _a, _b, _c, _d, _e, _f;
        let s = null !== (_b = null === (_a = this._projection) || void 0 === _a ? void 0 : _a.scale()) && void 0 !== _b ? _b : 0;
        const t = null !== (_d = null === (_c = this._projection) || void 0 === _c ? void 0 : _c.translate()) && void 0 !== _d ? _d : [ 0, 0 ];
        let t_x = t[0], t_y = t[1];
        s *= p, t_x -= (anchor[0] - t_x) * (p - 1), t_y -= (anchor[1] - t_y) * (p - 1), 
        null === (_e = this._projection) || void 0 === _e || _e.scale(s), null === (_f = this._projection) || void 0 === _f || _f.translate([ t_x, t_y ]);
    }
    shape(datum) {
        return this._projection.shape(datum);
    }
    invert(point) {
        return this._projection.invert(point);
    }
    evaluateProjection(start, size) {
        var _a;
        const evaluated = this._projection.evaluate(start, size, this.collectFeatures());
        let translate = evaluated.translate();
        const scale = evaluated.scale() * this._initialScale, center = null !== (_a = this._projectionSpec.center) && void 0 !== _a ? _a : evaluated.invert([ size[0] / 2, size[1] / 2 ]);
        return center && (translate = [ size[0] / 2, size[1] / 2 ]), {
            translate: translate,
            scale: scale,
            center: center
        };
    }
    _initCenterCache() {
        this._centerCache || (this._centerCache = new Map), this._regions.forEach((r => {
            r.getSeries().forEach((s => {
                var _a, _b;
                if ("map" === s.type) {
                    (null !== (_b = null === (_a = s.getMapViewData()) || void 0 === _a ? void 0 : _a.latestData) && void 0 !== _b ? _b : []).forEach(((feature = {}) => {
                        const key = feature[s.getDimensionField()[0]] || feature[DEFAULT_MAP_LOOK_UP_KEY], center = s.getDatumCenter(feature);
                        key && isValid(center) && this._centerCache.set(key, {
                            x: center[0],
                            y: center[1]
                        });
                    }));
                }
            }));
        }));
    }
    _compareSpec(spec, prevSpec) {
        const result = super._compareSpec(spec, prevSpec);
        return result.reMake || (result.reMake = [ "roam", "longitudeField", "latitudeField", "projection", "zoomLimit" ].some((k => !isEqual(null == prevSpec ? void 0 : prevSpec[k], spec[k])))), 
        result;
    }
    release() {
        super.release(), this._centerCache && this._centerCache.clear(), this._centerCache = null;
    }
}

GeoCoordinate.type = ComponentTypeEnum.geoCoordinate, mixin(GeoCoordinate, Zoomable);

export const registerGeoCoordinate = () => {
    Factory.registerComponent(GeoCoordinate.type, GeoCoordinate);
};
//# sourceMappingURL=geo-coordinate.js.map
