import { useEffect, useRef, useState } from "react"
import { PanoramaPosition, PluginConstructor, Point, Position, SphericalPosition, Viewer } from "@photo-sphere-viewer/core"
import { LensflarePlugin, ReactPhotoSphereViewer } from "react-photo-sphere-viewer"

import { RenderedScene } from "@/types"

import { MouseEventHandler } from "./types"
import { useImageDimension } from "@/lib/useImageDimension"
import { lightSourceNames } from "@/lib/lightSourceNames"

type PhotoSpherePlugin = (PluginConstructor | [PluginConstructor, any])

export function SphericalImage({
  rendered,
  onEvent,
  className,
  debug,
}: {
  rendered: RenderedScene
  onEvent: MouseEventHandler
  className?: string
  debug?: boolean
}) {


  const imageDimension = useImageDimension(rendered.assetUrl)
  const maskDimension = useImageDimension(rendered.maskUrl)

  const sceneConfig = JSON.stringify({ rendered, debug, imageDimension, maskDimension })
  const [lastSceneConfig, setLastSceneConfig] = useState<string>(sceneConfig)
  const rootContainerRef = useRef<HTMLDivElement>(null)
  const viewerContainerRef = useRef<HTMLElement>()
  const viewerRef = useRef<Viewer>()
  const [mouseMoved, setMouseMoved] = useState<boolean>(false)

  const defaultZoomLvl = 1 // 0 = 180 fov, 100 = 1 fov

  const options = {
    defaultZoomLvl,
    fisheye: false, // ..no!
    overlay: rendered.maskUrl || undefined,
    overlayOpacity: debug ? 0.5 : 0,
    /*
    panoData: {
      fullWidth: 2000,
      fullHeight: 1200,
      croppedWidth: 1024,
      croppedHeight: 512,
      croppedX: 0,
      croppedY: 200,
      // poseHeading: 0, // 0 to 360
      posePitch: 0, // -90 to 90
      // poseRoll: 0, // -180 to 180
    }
    */
  }


  const cacheRef = useRef("")
  useEffect(() => {
    const listener = (e: DragEvent) => {
      if (!rootContainerRef.current) { return }

      // TODO: check if we are currently dragging an object
      // if yes, then we should check if clientX and clientY are matching the 
      const boundingRect = rootContainerRef.current.getBoundingClientRect()

      // abort if we are not currently dragging over our display area
      if (e.clientX < boundingRect.left) { return }
      if (e.clientX > (boundingRect.left + boundingRect.width)) { return }
      if (e.clientY < boundingRect.top) { return }
      if (e.clientY > (boundingRect.top + boundingRect.height)) { return }

      const containerX = e.clientX - boundingRect.left
      const containerY = e.clientY - boundingRect.top
    
      const relativeX = containerX / boundingRect.width
      const relativeY = containerY / boundingRect.height

      const key = `${relativeX},${relativeY}`

      // to avoid use
      if (cacheRef.current === key) {
        return
      }
      // console.log(`DRAG: calling onEvent("hover", ${relativeX}, ${relativeY})`)

      cacheRef.current = key
      onEvent("hover", relativeX, relativeY)
    }

    document.addEventListener('drag', listener)

    return () => {
      document.removeEventListener('drag', listener)
    }
  }, [onEvent])

  useEffect(() => {
    const task = async () => {
      // console.log("SphericalImage: useEffect")
      if (sceneConfig !== lastSceneConfig) {
        // console.log("SphericalImage: scene config changed!")
    
        if (!viewerRef.current) {
          // console.log("SphericalImage: no ref!")
          setLastSceneConfig(sceneConfig)
          return
        }
        const viewer = viewerRef.current

        const newOptions = {
          ...options,
        }

        const lensflares: { id: string; position: SphericalPosition; type: number }[] = []
          
        if (maskDimension.width && imageDimension.width) {

          // console.log("rendered.segments:", rendered.segments)
   
          rendered.segments
            .filter(segment => lightSourceNames.includes(segment.label))
            .forEach(light => {
            // console.log("light detected", light)
            const [x1, y1, x2, y2] = light.box
            const [centerX, centerY] = [(x1 + x2) / 2, (y1 + y2) / 2]
            // console.log("center:", { centerX, centerY })
            const [relativeX, relativeY] = [centerX / maskDimension.width, centerY/ maskDimension.height]
            // console.log("relative:", { relativeX, relativeY})

            const panoramaPosition: PanoramaPosition = {
              textureX: relativeX * imageDimension.width,
              textureY: relativeY * imageDimension.height
            }
            // console.log("panoramaPosition:", panoramaPosition)

            const position = viewer.dataHelper.textureCoordsToSphericalCoords(panoramaPosition)
            // console.log("sphericalPosition:", position)
            if ( // make sure coordinates are valid
              !isNaN(position.pitch) && isFinite(position.pitch) &&
              !isNaN(position.yaw) && isFinite(position.yaw)) {
              lensflares.push({
                id: `flare_${lensflares.length}`,
                position,
                type: 0,
              })
            }
          })
        }

        // console.log("lensflares:", lensflares)
        const lensFlarePlugin = viewer.getPlugin<LensflarePlugin>("lensflare")
        lensFlarePlugin.setLensflares(lensflares)

        // console.log("SphericalImage: calling setOptions")
        // console.log("SphericalImage: changing the panorama to: " + rendered.assetUrl.slice(0, 120))
      
        await viewer.setPanorama(rendered.assetUrl, {
          ...newOptions,
          showLoader: false,
        })

        // TODO we should separate all those updates, probaby
        viewer.setOptions(newOptions)
        // viewer.setOverlay(rendered.maskUrl || undefined)

        // console.log("SphericalImage: asking to re-render")
        viewerRef.current.needsUpdate()

        setLastSceneConfig(sceneConfig)
      }
    }
    task()
  }, [sceneConfig, rendered.assetUrl, viewerRef.current, maskDimension.width, imageDimension])

  const handleEvent = async (event: React.MouseEvent<HTMLDivElement, MouseEvent>, isClick: boolean) => {
    const rootContainer = rootContainerRef.current
    const viewer = viewerRef.current
    const viewerContainer = viewerContainerRef.current

    /*
    if (isClick) console.log(`handleEvent(${isClick})`, {
      " imageDimension.width": imageDimension.width,
      "rendered.maskUrl": rendered.maskUrl
    })
    */
  
    if (!viewer || !rootContainer || !viewerContainer || !imageDimension.width || !rendered.maskUrl) {
      return
    }

    const containerRect = viewerContainer.getBoundingClientRect()
    // if (isClick) console.log("containerRect:", containerRect)

    const containerY = event.clientY - containerRect.top
    // console.log("containerY:", containerY)
    
    const position: Position = viewer.getPosition()

    const viewerPosition: Point = viewer.dataHelper.sphericalCoordsToViewerCoords(position)
    // if (isClick) console.log("viewerPosition:", viewerPosition)

    // we want to ignore events that are happening in the toolbar
    // note that we will probably hide this toolbar at some point,
    // to implement our own UI
    if (isClick && containerY > (containerRect.height - 40)) {
      // console.log("we are in the toolbar.. ignoring the click")
      return
    }

    const panoramaPosition: PanoramaPosition = viewer.dataHelper.sphericalCoordsToTextureCoords(position)

    if (typeof panoramaPosition.textureX !== "number" || typeof panoramaPosition.textureY !== "number") {
      return
    }
    
    const relativeX = panoramaPosition.textureX / imageDimension.width
    const relativeY = panoramaPosition.textureY / imageDimension.height

    onEvent(isClick ? "click" : "hover", relativeX, relativeY)
  }

  if (!rendered.assetUrl) {
    return null
  }

  return (
    <div
      ref={rootContainerRef}
      onMouseMove={(event) => {
        handleEvent(event, false)
        setMouseMoved(true)
      }}
      onMouseUp={(event) => {
        if (!mouseMoved) {
          handleEvent(event, true)
        }
        setMouseMoved(false)
      }}
      onMouseDown={() => {
        setMouseMoved(false)
      }}
      >
      <ReactPhotoSphereViewer
        src={rendered.assetUrl}
        container=""
        containerClass={className}
        
        height="100vh"
        width="100%"
        
        // to access a plugin we must use viewer.getPlugin()
        plugins={[[LensflarePlugin, { lensflares: [] }]]}

        {...options}

        // note: photo sphere viewer performs an aggressive caching of our callbacks,
        // so we aggressively disable it by using a ref
        onClick={() => {
          // nothing to do here
        }}
    
        onReady={(instance) => {
          viewerRef.current = instance
          viewerContainerRef.current = instance.container

          /*
          const markersPlugs = instance.getPlugin(MarkersPlugin);
          if (!markersPlugs)
            return;
          markersPlugs.addMarker({
            id: "imageLayer2",
            imageLayer: "drone.png",
            size: { width: 220, height: 220 },
            position: { yaw: '130.5deg', pitch: '-0.1deg' },
            tooltip: "Image embedded in the scene"
          });
          markersPlugs.addEventListener("select-marker", () => {
            console.log("asd");
          });
          */
        }}

      />
    </div>
  )
}