import react, { JSXElementConstructor, ReactElement, ReactNode } from 'react';
import useDimensions from 'react-cool-dimensions';

import { Point, MapScope } from './types';
import Marker from './Marker';

import LayerStack from './LayerStack';
import { tileProviders } from './tile-providers';
import { lon2tile, lat2tile } from '../../lib/geo';

export interface MapProperties {
  scope: MapScope;
  numberOfTiledLayers?: number;
  /** Slippy graphics are non scalable SVG snippets defined by geo locations */
  slippyGraphics?: ReactElement<any, string | JSXElementConstructor<any>>[];
}

/**
 *
 * @returns A `<Map>` component
 *
 * `<Map>` components display the map specified by their {@link MapProperties}'s scope.
 *
 * They can be driven by {@link components/map/LiveMap!LiveMap} component to react to user's event.
 *
 */
export const Map: react.FC<MapProperties> = (props: MapProperties) => {
  console.log(
    `Map rendering, scope: ${JSON.stringify(props.scope)}, slippyGraphics: ${
      props.slippyGraphics
    }`
  );

  const { observe, width, height } = useDimensions<HTMLDivElement>();

  const tileProvider = tileProviders[props.scope.tileProvider];

  const tilesZoom = Math.min(
    Math.max(Math.round(props.scope.zoom), tileProvider.minZoom),
    tileProvider.maxZoom
  );
  const tilesCenter: Point = {
    x: lon2tile(props.scope.center.lon, tilesZoom),
    y: lat2tile(props.scope.center.lat, tilesZoom),
  };
  const softZoom = props.scope.zoom - tilesZoom;
  const relativeScale = 2 ** softZoom;
  const visibleTileSize = tileProvider.tileSize * relativeScale;
  const nbTilesLeft = width / 2 / visibleTileSize;
  const nbTilesTop = height / 2 / visibleTileSize;
  const firstTileLeft = Math.floor(tilesCenter.x - nbTilesLeft);
  const firstTileTop = Math.floor(tilesCenter.y - nbTilesTop);

  return (
    <div style={{ width: '100%', height: '100%' }} ref={observe}>
      <LayerStack
        numberOfTiledLayers={props.numberOfTiledLayers}
        keyObject={{
          provider: props.scope.tileProvider,
          zoomLevel: tilesZoom,
          x: firstTileLeft,
          y: firstTileTop,
        }}
        coordinateSystem={{
          shift: {
            x: -((tilesCenter.x - nbTilesLeft) % 1) * visibleTileSize,
            y: -((tilesCenter.y - nbTilesTop) % 1) * visibleTileSize,
          },
          zoom: relativeScale,
        }}
        slippyGraphics={props.slippyGraphics}
      />
    </div>
  );
};

export default Map;
