import react, { useCallback, useEffect, useRef, useState } from 'react';
import { atom, useAtom } from 'jotai';

import { TileFactory, TileKeyObject } from './types';

import { coordinateSystemAtom } from './Map';
import TiledLayer from './TiledLayer';
import Tile from './Tile';
import _, { floor, range } from 'lodash';
import tileUri from './uris';

export interface LayerStackProperties {
  /**
   * A key identifying the initial top left tile
   */
  keyObject: TileKeyObject;
  /**
   * Number of {@link components/map/TiledLayer!TiledLayer}.
   */
  numberOfTiledLayers?: number;
}

/**
 *
 * @param props
 * @returns A stack of layers embedded in an SVG element
 *
 * This component does the conversion between the {@link components/map/Map!CoordinateSystem} stored
 * in the {@link components/map/Map!coordinateSystemAtom} atom and the {@link components/map/TiledLayer!TiledLayer}
 * components which units are in tiles.
 *
 */
export const LayerStack: react.FC<LayerStackProperties> = (
  props: LayerStackProperties
) => {
  const [coordinateSystem] = useAtom(coordinateSystemAtom);

  const simpleTileFactory: TileFactory = useCallback(
    (keyObject) => (
      <Tile
        href={`https://tile.openstreetmap.org/${keyObject.zoomLevel}/${keyObject.x}/${keyObject.y}.png`}
      />
    ),
    []
  );

  const numberOfTiledLayers =
    props.numberOfTiledLayers === undefined ? 1 : props.numberOfTiledLayers;

  const [activeTiledLayer, setActiveTiledLayer] = useState(
    Math.floor(numberOfTiledLayers / 2)
  );

  const viewPort = {
    topLeft: {
      x: Math.floor(-coordinateSystem.shift.x / coordinateSystem.zoom / 256),
      y: Math.floor(-coordinateSystem.shift.y / coordinateSystem.zoom / 256),
    },
    bottomRight: {
      x: Math.ceil(
        (-coordinateSystem.shift.x + window.innerWidth) /
          coordinateSystem.zoom /
          256
      ),
      y: Math.ceil(
        (-coordinateSystem.shift.y + window.innerHeight) /
          coordinateSystem.zoom /
          256
      ),
    },
  };

  const getTiledLayer = (i: number) => {
    const relativeZoomLevel = i - activeTiledLayer;
    const zoom = 2 ** relativeZoomLevel;
    const origin = {
      x: props.keyObject.x * zoom,
      y: props.keyObject.y * zoom,
    };
    const keyObject = {
      provider: props.keyObject.provider,
      zoomLevel: props.keyObject.zoomLevel + relativeZoomLevel,
      x: Math.floor(origin.x),
      y: Math.floor(origin.y),
    };
    const shift = {
      x: origin.x - floor(origin.x),
      y: origin.y - floor(origin.y),
    };
    return (
      <g
        transform={`scale(${256 * zoom}) translate(${shift.x}, ${shift.y})`}
        key={tileUri({
          provider: keyObject.provider,
          zoomLevel: keyObject.zoomLevel,
        })}
      >
        <TiledLayer
          keyObject={keyObject}
          viewPort={i === activeTiledLayer ? viewPort : undefined}
          tileFactory={simpleTileFactory}
        />
      </g>
    );
  };

  const tileLayers = range(0, numberOfTiledLayers).map((i) => {});

  //  console.log(`tiledLayers: ${JSON.stringify(tiledLayers)}`);

  return (
    <svg
      width={window.innerWidth}
      height={window.innerHeight}
      data-testid='layer-stack'
    >
      <g
        transform={`translate(${coordinateSystem.shift.x}, ${coordinateSystem.shift.y}) scale(${coordinateSystem.zoom})`}
      >
        {
          // Tiled layers with less detail
          range(0, activeTiledLayer).map(getTiledLayer)
        }
        {
          // Tiled layers with more details
          range(numberOfTiledLayers, activeTiledLayer + 1, -1).map(
            getTiledLayer
          )
        }
        {
          // And the active one
          getTiledLayer(activeTiledLayer)
        }
      </g>
    </svg>
  );
};

export default LayerStack;
