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

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

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)
 *
 * TODO: test tiles'X and Y boundaries.
 * TODO: housekeeping
 * TODO: remove tileFactory to facilitate memoisation.
 *
 */
export const TiledLayer: react.FC<TiledLayerProperties> = memo(
  (props: TiledLayerProperties) => {
    console.log(`Rendering TiledLayer: ${JSON.stringify(props)}`);
    const tiles = useRef<any>(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.current.has(key)) {
              tiles.current.set(
                key,
                <g key={key} transform={`translate(${col}, ${row})`}>
                  {props.tileFactory(keyObject)}
                </g>
              );
            }
          });
        }
      );
    }

    return <>{Array.from(tiles.current.values())}</>;
  },
  (prevProps, nextProps) =>
    isEqual(
      [prevProps.keyObject, prevProps.viewPort],
      [nextProps.keyObject, nextProps.viewPort]
    )
);

export default TiledLayer;
