import react, { memo, useRef } from 'react';
import { range, isEqual } from 'lodash';

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

/**
 * @hidden
 */
export const thisIsAModule = true;

/**
 *
 */

declare global {
  var cacheForTiledLayer: any;
}

//export {};

globalThis.cacheForTiledLayer = new Map();

export interface TiledLayerProperties {
  /** The key of the first (ie top/left) tile */
  keyObject: TileKeyObject;
  /** The current viewport expressed in tiles coordinates */
  viewPort?: Rectangle;
  /** The factory to create tiles */
  tileFactory: TileFactory;
}

/**
 * A lazily loaded layer of tiles.
 *
 * This component is rather dumb and is mainly a sparse array of tiles.
 *
 * New tiles are added to the array when the viewport is updated and they stay in the array until
 * the component is destroyed.
 *
 * This component has no need to know the number nor the size of its tiles: tiles can be added when needed and
 * its unit is the tile size (the parent component needs to transform its enclosing SVG group to adapt its units)
 *
 * The `globalThis.cacheForTiledLayer` global variable is used as a cache to store tiles without being subject
 * to re-initialization when components are unmounted/remounted.
 *
 * This cache is a map of map, the first key identifying the `<TiledLayer` and the second one for `<Tile>`s.
 *
 * Idea stolen [on the web](https://dev.to/tiagof/react-re-mounting-vs-re-rendering-lnh)
 *
 * TODO: cache housekeeping
 * 
 * TODO: test tiles'X and Y boundaries.
 * 
 * TODO: remove tileFactory to facilitate memoisation.
 *
 */
export const TiledLayer: react.FC<TiledLayerProperties> = (
  props: TiledLayerProperties
) => {
  // console.log(`Rendering TiledLayer: ${JSON.stringify(props)}`);

  const key = tileUri({
    provider: props.keyObject.provider,
    zoomLevel: props.keyObject.zoomLevel,
  });

  const tiles: any = globalThis.cacheForTiledLayer.get(key) ?? new Map();
  if (props.viewPort !== undefined) {
    range(props.viewPort.topLeft.y, props.viewPort.bottomRight.y + 1).forEach(
      (row) => {
        range(
          props.viewPort!.topLeft.x,
          props.viewPort!.bottomRight.x + 1
        ).forEach((col) => {
          const keyObject = {
            provider: props.keyObject.provider,
            zoomLevel: props.keyObject.zoomLevel,
            x: props.keyObject.x + col,
            y: props.keyObject.y + row,
          };
          const key = tileUri(keyObject);
          if (!tiles.has(key)) {
            tiles.set(
              key,
              <g key={key} transform={`translate(${col}, ${row})`}>
                {props.tileFactory(keyObject)}
              </g>
            );
          }
        });
      }
    );
    globalThis.cacheForTiledLayer.set(key, tiles);
  }

  return <>{Array.from(tiles.values())}</>;
};

export default TiledLayer;
