import React from "react";
import ReactDOM from "react-dom";
import { BeButtonEvent, DecorateContext, Decorator, Marker, IModelApp } from "@bentley/imodeljs-frontend";
import { Point3d, XAndY, XYAndZ } from "@bentley/geometry-core";

// a weak map would probably be better to prevent leaks
const reactSetHoverStateMap = new Map<PinMarker, (val: boolean) => void>();

function InMarkerComponent(props: {markerInstance: PinMarker}) {
  const [isHovered, setIsHovered] = React.useState(false);
  React.useEffect(() => {
    reactSetHoverStateMap.set(props.markerInstance, setIsHovered);
  }, []);
  React.useEffect(() => {
    // this is needed especially if you start manipulating "isHovered" from another component, like when someone mouse-overs a UI component
    IModelApp.viewManager.invalidateDecorationsAllViews(); // if you have multiple views you can do a more specific invalidation
  }, [isHovered]);
  return <div> {isHovered ? "hovered!" : "nope :("}</div>
}

export class PinMarker extends Marker {
  public constructor(worldLocation: XYAndZ, size: XAndY = {x: 70, y: 20}) {
    super(worldLocation, size);
    this.htmlElement = document.createElement("div");
    ReactDOM.render(<InMarkerComponent markerInstance={this}/>, this.htmlElement);
  }

  public onMouseEnter(ev: BeButtonEvent) {
    reactSetHoverStateMap.get(this)?.(true);
    super.onMouseEnter(ev);
  }

  public onMouseLeave() {
    reactSetHoverStateMap.get(this)?.(false);
    return super.onMouseLeave();
  }
}

export interface PinDecorator extends Decorator {
  markers: Map<Point3d, PinMarker>;
  getPins?: () => Point3d[];
}

export const pinDecorator: PinDecorator = {
  markers: new Map(),
  // when registered, decorate is called by the view manager every requested frame/update
  decorate(ctx: DecorateContext) {
    const currentPins = new Set(this.getPins?.());
    for (const loc of currentPins) {
      if (!this.markers.has(loc)) this.markers.set(loc, new PinMarker(loc));
    }
    for (const [loc, marker] of this.markers) {
      if (!currentPins.has(loc)) this.markers.delete(loc);
      else marker.addDecoration(ctx);
    }
  },
};