import { useState, useEffect } from 'react';
import { feature } from 'topojson-client';
import { geoPath } from 'd3-geo';

/**
 * Hook to parse the features out of a topojson file and inject each feature's
 * SVG path as the `d` attribute of the feature's `properties` object.
 *
 * @param {string} url
 *        The URL to fetch a TopoJSON file from
 * @param {Object} opts
 * @param {string[]} opts.objects
 *        The name of the layers within the `objects` property of the TopoJSON
 *        that the caller expects. Specifying object names causes the hook to
 *        return an initial value of an empty GeoJSON feature collections for
 *        each name specified. This can be helpful in simplifying the logic of
 *        the code that calls this hook because it doesn't have to deal with an
 *        empty case before the data loads.
 * @returns {Object.<string, Object>}
 */
const useTopojson = function useTopojson(url, opts = {}) {
  const {
    objects
  } = opts;

  // The GeoJSON-ified TopoJSON to return
  const [featurized, setFeaturized] = useState(() => {
    if (!objects) return undefined;
    // Set up an initial value if `objects` were specified
    return objects.reduce((collections, key) => ({
      ...collections,
      [key]: {
        type: 'FeatureCollection',
        features: []
      }
    }), {});
  });

  // Load the TopoJSON and convert each of its layers into GeoJSON
  useEffect(() => {
    const path = geoPath();
    const featurize = (fullTopo, topoObject) => {
      const topo = feature(fullTopo, topoObject);
      return {
        ...topo,
        features: topo.features.map(feat => ({
          ...feat,
          properties: {
            ...feat.properties,
            d: path(feat)
          }
        }))
      };
    };
    const fetchTopo = async () => {
      const rsp = await fetch(url);
      const topo = await rsp.json();
      setFeaturized(Object.entries(topo.objects).reduce((topoObjects, [key, topoObject]) => ({
        ...topoObjects,
        [key]: featurize(topo, topoObject)
      }), {}));
    };
    fetchTopo();
  }, [url]);
  return featurized;
};
export default useTopojson;