import react, { useEffect, 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 or its number of tiles is updated.
 *
 * 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)
 *
 */
export const TiledLayer: react.FC<TiledLayerProperties> = (
  props: TiledLayerProperties
) => {
  console.log(`Rendering TiledLayer: ${JSON.stringify(props)}`);
  const tiles = useRef<any>({});
  const previousKeyObject = useRef<TileKeyObject>(props.keyObject);
  if (!isEqual(props.keyObject, previousKeyObject.current)) {
    previousKeyObject.current = props.keyObject;
    tiles.current = {};
  }

  range(props.viewPort.topLeft.y, props.viewPort.bottomRight.y + 1).map(
    (row) => {
      range(props.viewPort.topLeft.x, props.viewPort.bottomRight.x + 1).map(
        (col) => {
          const keyObject = {
            provider: props.keyObject.provider,
            zoomLevel: props.keyObject.zoomLevel,
            x: props.keyObject.x + col,
            y: props.keyObject.x + row,
          };
          const key = tileUri(keyObject);
          if (!Object.hasOwn(tiles.current, key)) {
            tiles.current[key] = (
              <g key={key} transform={`translate(${col}, ${row})`}>
                {props.tileFactory(keyObject)}
              </g>
            );
          }
        }
      );
    }
  );

  return <>{Object.values(tiles.current)}</>;
};

export default TiledLayer;
