import { Vector3 } from "three/src/math/Vector3";
import { Vector2 } from "three/src/math/Vector2";
import { PlaneBufferGeometry } from "three/src/geometries/PlaneGeometry";
import { Matrix4 } from "three/src/math/Matrix4";
import { Box2 } from "three/src/math/Box2";
import mapboxgl from "mapbox-gl";
import { CoreObject } from "@polygonjs/polygonjs/dist/src/core/geometry/Object";
import { ObjectType } from "@polygonjs/polygonjs/dist/src/core/geometry/Constant";
import { CoreGeometry } from "@polygonjs/polygonjs/dist/src/core/geometry/Geometry";
import { CoreMath } from "@polygonjs/polygonjs/dist/src/core/math/_Module";
import { MapboxListenerParamConfig, MapboxListenerSopNode } from "./utils/MapboxListener";
import { CoreMapboxTransform } from "../../../core/mapbox/Transform";
const SCALE_ATTRIB_NAME = "scale";
const NORMAL_ATTRIB_NAME = "normal";
const R_MAT_MAPBOX = new Matrix4().makeRotationAxis(new Vector3(1, 0, 0), -Math.PI * 0.5);
const R_MAT_WORLD = new Matrix4().makeRotationAxis(new Vector3(1, 0, 0), Math.PI * 0.5);
var MapboxPlaneType = /* @__PURE__ */ ((MapboxPlaneType2) => {
  MapboxPlaneType2["PLANE"] = "plane";
  MapboxPlaneType2["HEXAGONS"] = "hexagon";
  return MapboxPlaneType2;
})(MapboxPlaneType || {});
const MAPBOX_PLANE_TYPES = ["plane" /* PLANE */, "hexagon" /* HEXAGONS */];
import { NodeParamsConfig, ParamConfig } from "@polygonjs/polygonjs/dist/src/engine/nodes/utils/params/ParamsConfig";
import { MapboxPlaneHexagonsController } from "./utils/mapbox_plane/HexagonsController";
import { isBooleanTrue } from "@polygonjs/polygonjs/dist/src/core/Type";
class MapboxPlaneSopParamsConfig extends MapboxListenerParamConfig(NodeParamsConfig) {
  constructor() {
    super(...arguments);
    this.type = ParamConfig.INTEGER(0, {
      menu: {
        entries: MAPBOX_PLANE_TYPES.map((name, i) => {
          return { name, value: i };
        })
      }
    });
    this.resolution = ParamConfig.INTEGER(10, {
      range: [1, 20],
      rangeLocked: [true, false]
    });
    this.sizeMult = ParamConfig.FLOAT(1, {
      range: [0, 1],
      rangeLocked: [true, false]
    });
    this.fullView = ParamConfig.BOOLEAN(1);
    this.asPoints = ParamConfig.BOOLEAN(0, {
      visibleIf: {
        type: MAPBOX_PLANE_TYPES.indexOf("plane" /* PLANE */)
      }
    });
    this.mapboxTransform = ParamConfig.BOOLEAN(1);
  }
}
const ParamsConfig = new MapboxPlaneSopParamsConfig();
export class MapboxPlaneSopNode extends MapboxListenerSopNode {
  constructor() {
    super(...arguments);
    this.paramsConfig = ParamsConfig;
    this._hexagonsController = new MapboxPlaneHexagonsController(this);
  }
  static type() {
    return "mapboxPlane";
  }
  cook() {
    this._mapboxListener.cook();
  }
  _postInitController() {
    const geometry = this._buildPlane();
    if (geometry) {
      let type = ObjectType.MESH;
      if (isBooleanTrue(this.pv.asPoints) || this._asHexagons()) {
        type = ObjectType.POINTS;
      }
      const object = this.createObject(geometry, type);
      const core_object = new CoreObject(object, 0);
      core_object.addAttribute("mapbox_sw", this.pv.southWest);
      core_object.addAttribute("mapbox_ne", this.pv.northEast);
      this.setObject(object);
    }
  }
  _buildPlane() {
    if (!this._cameraNode) {
      return;
    }
    const map_center = this._cameraNode.center();
    if (!map_center) {
      this.states.error.set("map is not yet loaded");
      return;
    }
    const transformer = new CoreMapboxTransform(this._cameraNode);
    const mapbox_center_3d = new Vector3(map_center.lng, 0, map_center.lat);
    transformer.transform_position_FINAL(mapbox_center_3d);
    const mapbox_center = new Vector2(mapbox_center_3d.x, mapbox_center_3d.z);
    const vertical_far_lng_lat_points = this._cameraNode.verticalFarLngLatPoints();
    const vertical_near_lng_lat_points = this._cameraNode.verticalNearLngLatPoints();
    const lng_lat_points = this.pv.fullView ? vertical_far_lng_lat_points : vertical_near_lng_lat_points;
    if (!lng_lat_points) {
      return;
    }
    const mirrored_near_lng_lat_points = lng_lat_points.map((p) => this._mirrorLngLat(p, map_center));
    lng_lat_points.push(map_center);
    for (let p of mirrored_near_lng_lat_points) {
      lng_lat_points.push(p);
    }
    const box = new Box2();
    for (let p of lng_lat_points) {
      box.expandByPoint(new Vector2(p.lng, p.lat));
    }
    const mapbox_box = new Box2();
    for (let p of lng_lat_points) {
      const pt3d = new Vector3(p.lng, 0, p.lat);
      transformer.transform_position_FINAL(pt3d);
      mapbox_box.expandByPoint(new Vector2(pt3d.x, pt3d.z));
    }
    const mapbox_dimensions = new Vector2();
    mapbox_box.getSize(mapbox_dimensions);
    const horizontal_lng_lat_points = this._cameraNode.horizontal_lng_lat_points();
    if (!horizontal_lng_lat_points) {
      return;
    }
    const mapbox_horizontal_lng_lat_points = horizontal_lng_lat_points.map((p) => {
      const pt3d = new Vector3(p.lng, 0, p.lat);
      transformer.transform_position_FINAL(pt3d);
      return { lng: pt3d.x, lat: pt3d.z };
    });
    const mapbox_horizontal_distances = {
      lng: Math.abs(mapbox_horizontal_lng_lat_points[0].lng - mapbox_horizontal_lng_lat_points[1].lng),
      lat: Math.abs(mapbox_horizontal_lng_lat_points[0].lat - mapbox_horizontal_lng_lat_points[1].lat)
    };
    const mapbox_horizontal_distance = Math.sqrt(mapbox_horizontal_distances.lng * mapbox_horizontal_distances.lng + mapbox_horizontal_distances.lat * mapbox_horizontal_distances.lat);
    const mapbox_segment_size = mapbox_horizontal_distance / this.pv.resolution;
    const segments_counts = {
      x: CoreMath.highest_even(this.pv.sizeMult * Math.ceil(mapbox_dimensions.x / mapbox_segment_size)),
      y: CoreMath.highest_even(this.pv.sizeMult * Math.ceil(mapbox_dimensions.y / mapbox_segment_size))
    };
    mapbox_dimensions.x = segments_counts.x * mapbox_segment_size;
    mapbox_dimensions.y = segments_counts.y * mapbox_segment_size;
    const mapbox_box_untransformed = new Box2();
    const mapbox_corners = [
      mapbox_center.clone().sub(mapbox_dimensions.clone().multiplyScalar(0.5)),
      mapbox_center.clone().sub(mapbox_dimensions.clone().multiplyScalar(-0.5)),
      mapbox_center.clone().add(mapbox_dimensions.clone().multiplyScalar(0.5)),
      mapbox_center.clone().add(mapbox_dimensions.clone().multiplyScalar(-0.5))
    ];
    for (let p of mapbox_corners) {
      const untransformed_3d = transformer.untransform_position_FINAL(new Vector3(p.x, 0, p.y));
      const untransformed = new Vector2(untransformed_3d.x, untransformed_3d.z);
      mapbox_box_untransformed.expandByPoint(untransformed);
    }
    const world_dimensions = new Vector2();
    mapbox_box_untransformed.getSize(world_dimensions);
    const world_plane_center = new Vector2(map_center.lng, map_center.lat);
    const horizontal_scale = mapbox_dimensions.x / segments_counts.x;
    let core_geo;
    const plane_dimensions = this.pv.mapboxTransform ? mapbox_dimensions : world_dimensions;
    const rotation_matrix = this.pv.mapboxTransform ? R_MAT_MAPBOX : R_MAT_WORLD;
    const geometry_center = this.pv.mapboxTransform ? mapbox_center : world_plane_center;
    let geometry;
    if (this._asHexagons()) {
      geometry = this._hexagonsController.geometry(plane_dimensions, segments_counts);
    } else {
      geometry = new PlaneBufferGeometry(plane_dimensions.x, plane_dimensions.y, segments_counts.x, segments_counts.y);
    }
    geometry.applyMatrix4(rotation_matrix);
    geometry.translate(geometry_center.x, 0, geometry_center.y);
    core_geo = new CoreGeometry(geometry);
    const z_scale = [horizontal_scale, 1][0];
    const scale = [horizontal_scale, horizontal_scale, z_scale];
    core_geo.addNumericAttrib(SCALE_ATTRIB_NAME, 3, scale);
    core_geo.addNumericAttrib(NORMAL_ATTRIB_NAME, 3, [0, 1, 0]);
    return geometry;
  }
  _mirrorLngLat(p, map_center) {
    const delta = {
      lng: map_center.lng - p.lng,
      lat: map_center.lat - p.lat
    };
    return new mapboxgl.LngLat(map_center.lng + delta.lng, map_center.lat + delta.lat);
  }
  _asHexagons() {
    return this.pv.type == MAPBOX_PLANE_TYPES.indexOf("hexagon" /* HEXAGONS */);
  }
}
