/*
 * Copyright (C) 2020 HERE Europe B.V.
 * Licensed under MIT, see full license in LICENSE
 * SPDX-License-Identifier: MIT
 */

import React, { useRef, useEffect, useState } from "react"
import { Box, Text, Stack, Flex, IconButton, Icon } from "@chakra-ui/core"
import { extent } from "d3-array"
import { keyframes } from "@emotion/core"

import scene from "../../utils/scene"
import useStore, { useMapFocus, useDataDate, useDataTypeStore } from "../../utils/store"
import { formatThousand } from "../../utils/format"

const rotate = keyframes`
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(359.999deg);
  }
`

const Map = ({ points }) => {

  const currentDataType = useDataTypeStore(state => state.currentDataType)

  const updateCenter = useStore(state => state.updateCenter)
  const mapFocus = useMapFocus(state => state.mapFocus)
  const mapZoom = useMapFocus(state => state.mapZoom)
  const updateMapFocus = useMapFocus(state => state.updateMapFocus)
  const currentDate = useDataDate(state => state.currentDate)
  const [loadedMap, setLoadedMap] = useState(false)

  const mapRef = useRef()
  const Leaflet = useRef()
  const sceneRef = useRef()
  const leafletMap = useRef()

  useEffect(() => {
    if (typeof window === undefined || !points.length) return
    const L = require("leaflet")
    const Tangram = require("tangram")
    Leaflet.current = L

    if (leafletMap.current) {
      leafletMap.current.remove()
      leafletMap.current = null
      sceneRef.current = null
    }

    const popup = L.popup({ autoPan: false })

    const map = L.map(mapRef.current, {
      zoomControl: false,
      minZoom: 3,
      maxZoom: 8,
      scrollWheelZoom: true,
    })

    const layer = Tangram.leafletLayer({
      scene,
      attribution: "Loading map",
      webGLContextOptions: {
        antialias: false,
      },
      events: {
        click: selection => {
          if (!selection.feature || (!selection.feature && !selection.feature.properties)) {
            return
          }
          const { lat, long } = selection.feature.properties
          updateMapFocus([lat, long], selection.feature.properties)
        },
        hover: selection => {
          if (!selection.feature || (!selection.feature && !selection.feature.properties)) {
            mapRef.current.style.cursor = "default"
            map.closePopup()
            return
          }

          mapRef.current.style.cursor = "pointer"

          const coords = selection.leaflet_event.latlng
          const sceneDate = sceneRef.current.config.global.currentDate
          const sceneDataType = sceneRef.current.config.global.currentDataType
          const { prefix } = sceneRef.current.config.global

          const num = formatThousand(selection.feature.properties[prefix + sceneDate])

          const isChina = ["Mainland China", "Macau", "Hong Kong", "Taiwan"].includes(selection.feature.properties.countryregion)

          const label = sceneDataType === 0
            ? "Confirmed cases" : sceneDataType === 1 ? "Deaths" : "Recoveries"

          const value = num
            ? `
              <div>
                <div class="tt-address">
                  <p class="tt-zip-code-value">
                    ${selection.feature.properties.provincestate || selection.feature.properties.countryregion}
                  </p>
                  ${
                    selection.feature.properties.provincestate ? `
                      <p class="tt-zip-code-title">
                        ${isChina ? "China" : selection.feature.properties.countryregion}
                      </p>` : ""
                  }
                </div>
                <p class="tt-median-rent-title">${label}</p>
                <p class="tt-median-rent-value">
                  <span>${num}</span>
                </p>
              </div>
            `
            : ""

          popup
            .setLatLng([coords.lat, coords.lng])
            .setContent(`${value}`)
            .openOn(map)

        }
      },
    })

    map.on("move", () => {
      const { lat, lng } = map.getCenter()
      updateCenter([-lng, -lat])
    })

    layer.addTo(map)
    sceneRef.current = layer.scene    
    map.setView([30, 110], 4)
    leafletMap.current = map

    const geojsonData = {
      type: "FeatureCollection",
      features: points,
    }

    const ext = extent(points, d => {
      const headers = d.properties.headers.split(";;")
      const lastDate = headers[headers.length - 1]
      return parseInt(d.properties[lastDate])
    })

    layer.scene.subscribe({
      load: () => {
        layer.scene.config.global.max = ext[1]
        layer.scene.updateConfig()
        layer.scene.setDataSource("dynamic_data", {
          type: "GeoJSON",
          data: geojsonData,
        })
        setLoadedMap(true)
      },
    })

  }, [points])

  const cf = sceneRef.current && sceneRef.current.config

  useEffect(() => {
    if (!mapFocus) return
    leafletMap.current.flyTo(mapFocus, mapZoom || 5, { easeLinearity: 0.01, duration: 1.5 })
  }, [mapFocus, mapZoom])

  useEffect(() => {
    if (!currentDate || !sceneRef.current || !sceneRef.current.config) return
    if (currentDate === sceneRef.current.config.global.currentDate) return
    sceneRef.current.config.global.currentDate = currentDate
    sceneRef.current.updateConfig()
  }, [currentDate, cf])

  useEffect(() => {
    if (!sceneRef.current || !sceneRef.current.config) return
    sceneRef.current.config.global.currentDataType = currentDataType || 0
    sceneRef.current.config.global.prefix = currentDataType === 0
      ? ""
      : currentDataType === 1
        ? "deaths_"
        : "recoveries_"
    sceneRef.current.updateConfig()
  }, [currentDataType, cf])

  const handleZoomIn = () => {
    leafletMap.current.zoomIn()
  }

  const handleZoomOut = () => {
    leafletMap.current.zoomOut()
  }

  return (
    <>
      <Box
        ref={mapRef}
        top="0"
        left={[0, null, "25rem", "30rem"]}
        right="0"
        bottom="0"
        style={{ position: "fixed", transition: "opacity 500ms", opacity: loadedMap ? 1 : 0 }}
      />

      {
        !loadedMap ? (
          <Box
            position="fixed"
            top="50%"
            left={["50%", null, "calc((100% + 25rem) / 2)", "calc((100% + 30rem) / 2)"]}
            zIndex={9999}
            transform="translateX(-50%)"
          >
            <Icon
              name="spinner"
              size="2.5rem"
              color="gray.500"
              animation={`${rotate} 1s linear infinite`}
              mx="auto"
              display="block"
            />
            <Text textAlign="center" color="gray.500" mx="auto" fontSize="sm" mt="0.75rem">
              { "Loading map" }
            </Text>
          </Box>
        ) : null
      }
      
      <Box
        position="fixed"
        bottom={["6rem", "8rem", "5rem"]}
        left={["auto", null, "26.5rem", "32.5rem"]}
        right={["2.75rem", "3.75rem", "auto"]}
        zIndex={2}
        pointerEvents={["none", null, "all"]}
      >

        <Stack spacing="0.5rem" alignItems="center" mb={["6.5rem", null, "1.25rem"]}>
          <Text color="gray.600" width="2.5rem" textAlign="center" fontWeight={700} fontSize="xs" lineHeight="shorter">
            { "more cases" }
          </Text>
          <Box width="2.5rem" height="2.5rem" border="0.125rem solid" borderColor="gray.500" borderRadius="100%" />
          <Box width="1.25rem" height="1.25rem" border="0.125rem solid" borderColor="gray.500" borderRadius="100%" />
          <Box width="0.625rem" height="0.625rem" border="0.125rem solid" borderColor="gray.500" borderRadius="100%" />
          <Text color="gray.600" width="2.5rem" textAlign="center" fontWeight={700} fontSize="xs" lineHeight="shorter">
            { "fewer cases" }
          </Text>
        </Stack>

        <Stack display={["none", null, "block"]} spacing="1.25rem">
          <Flex justifyContent="center">
            <Flex alignItems="center" direction="column" shadow="lg" borderRadius="md">
              <IconButton 
                onClick={handleZoomIn}
                icon="add" bg="white"
                borderRadius="0.25rem 0.25rem 0 0"
                border="0.0625rem solid"
                borderColor="transparent"
                  _hover={{ borderColor: "transparent" }}
                  _focus={{ borderColor: "rgba(236,97,14, 1)", boxShadow: `0 0 0 0.0625rem rgba(236,97,14, 1), 0 0 0 0.25rem rgba(236,97,14, 0.25)` }}
                  _placeholder={{ color: "gray.500" }}
              />
              <Box height="1px" width="100%" bg="gray.100" />
              <IconButton 
                onClick={handleZoomOut}
                icon="minus"
                bg="white"
                borderRadius="0 0 0.25rem 0.25rem"
                border="0.0625rem solid"
                borderColor="transparent"
                  _hover={{ borderColor: "transparent" }}
                  _focus={{ borderColor: "rgba(236,97,14, 1)", boxShadow: `0 0 0 0.0625rem rgba(236,97,14, 1), 0 0 0 0.25rem rgba(236,97,14, 0.25)` }}
                  _placeholder={{ color: "gray.500" }}
              />
            </Flex>
          </Flex>
        </Stack>
      </Box>
    </>
  )
}

export default Map