import { createSelector } from "redux-bundler";
import olMap from "ol/Map.js";
import View from "ol/View";
import { get, transform, fromLonLat, transformExtent } from "ol/proj";
import ScaleBar from "ol/control/ScaleLine";
import debounce from "lodash/debounce";

import "ol/ol.css";

export default (opts) => {
  const defaults = {
    name: "map",
    center: [-98.579, 39.828],
    zoom: 5,
    rotation: 0,
    minZoom: 0,
    maxZoom: 22,
    addons: {},
    limitMapExtent: false,
  };
  
  const config = Object.assign({}, defaults, opts);

  return Object.assign(
    {},
    {
      name: config.name,

      getReducer: () => {
        const initialData = {
          map: null,
          opts: null,
          geoProjection: get("EPSG:4326"),
          webProjection: get("EPSG:3857"),
          center: config.center,
          zoom: config.zoom,
          rotation: config.rotation,
          minZoom: config.minZoom,
          maxZoom: config.maxZoom,
          limitMapExtent: config.limitMapExtent,
          shouldInitialize: false,
          shouldResetViewState: false,
          shouldUpdateViewState: false,
        };

        return (state = initialData, { type, payload }) => {
          if (config.additionalActions.indexOf(type) !== -1) {
            return Object.assign({}, state, payload);
          }

          switch (type) {
            case "MAP_INITIALIZED":
            case "MAP_RESET_MAP_VIEW":
            case "MAP_UPDATED_VIEW_STATE_STARTED":
            case "MAP_UPDATED_VIEW_STATE_FINISHED":
            case "MAP_LOADING":
            case "MAP_LOADED":
              return Object.assign({}, state, payload);
            default:
              return state;
          }
        };
      },

      doMapInitialize: (el, opts) => ({ dispatch, store }) => {
        const geoProjection = store.selectMapGeoProjection();
        const webProjection = store.selectMapWebProjection();
        // CWBI-13636 Limit Map Extent
        const limitMapExtent = opts.hasOwnProperty("limitMapExtent")
          ? opts.limitMapExtent
          : null;
        const extent = limitMapExtent 
          ? transformExtent([-180, -85, 180, 85], geoProjection, webProjection) 
          : null;
        const center = opts.hasOwnProperty("center")
          ? opts.center
          : store.selectMapCenter();
        const zoom = opts.hasOwnProperty("zoom")
          ? opts.zoom
          : store.selectMapZoom();
        const rotation = opts.hasOwnProperty("rotation")
          ? opts.rotation
          : store.selectMapRotation();
        const minZoom = opts.hasOwnProperty("minZoom")
          ? opts.minZoom
          : store.selectMapMinZoom();
        const maxZoom = opts.hasOwnProperty("maxZoom")
          ? opts.maxZoom
          : store.selectMapMaxZoom();

      
        console.log("setting up at", opts.center, center);

        const viewParams = {
          center: transform(center, geoProjection, webProjection),
          zoom: zoom,
          rotation: rotation,
          minZoom: minZoom,
          maxZoom: maxZoom,
        };

        // CWBI-13636 Limit Map Extent
        if (extent) { viewParams.extent = extent };

        const view = new View(viewParams);

        const map = new olMap({
          controls: [new ScaleBar({ units: "us" })],
          target: el,
          view: view,
        });

        map.on("moveend", debounce(store.doMapUpdateViewState, 200));
        map.on("precompose", debounce(store.doMapPreCompose, 50));
        map.on("rendercomplete", debounce(store.doMapRenderComplete, 200));

        dispatch({
          type: "MAP_INITIALIZED",
          payload: { map: map, opts: opts },
        });
      },
      doMapPreCompose: () => ({ dispatch }) => {
        dispatch({
          type: "MAP_LOADING",
        });
      },
      doMapRenderComplete: () => ({ dispatch }) => {
        dispatch({
          type: "MAP_LOADED",
        });
      },

      doMapUpdateSize: () => ({ dispatch, store }) => {
        const map = store.selectMap();
        if (map) map.updateSize();
      },

      doMapUpdateViewState: () => ({ dispatch, store }) => {
        dispatch({
          type: "MAP_UPDATED_VIEW_STATE_STARTED",
          payload: { shouldUpdateViewState: false },
        });
        const epsg4326 = store.selectMapGeoProjection();
        const epsg3857 = store.selectMapWebProjection();
        const map = store.selectMap();
        const view = map.getView();
        const center = transform(view.getCenter(), epsg3857, epsg4326);
        const rotation = view.getRotation() * (180 / Math.PI);
        const zoom = view.getZoom();
        const minZoom = view.getMinZoom();
        const maxZoom = view.getMaxZoom();
        dispatch({
          type: "MAP_UPDATED_VIEW_STATE_FINISHED",
          payload: {
            center: center,
            rotation: rotation,
            zoom: zoom,
            minZoom: minZoom,
            maxZoom: maxZoom,
          },
        });
      },

      doMapResetViewState: () => ({ dispatch, store }) => {
        dispatch({
          type: "MAP_RESET_MAP_VIEW",
          payload: { shouldResetViewState: false },
        });
        const { center, zoom, rotation } = config;
        const map = store.selectMap();
        const view = map.getView();
        const geoProjection = store.selectMapGeoProjection();
        const webProjection = store.selectMapWebProjection();
        view.animate(
          {
            center: transform(center, geoProjection, webProjection),
            zoom: zoom,
            rotation: rotation,
          },
          200
        );
      },
      
      doMapZoomTo: (lonLat, z) => ({ store }) => {
        const map = store.selectMap();
        const view = map.getView();
        view.animate({
          zoom: z || 10,
          center: fromLonLat(lonLat),
        });
      },

      // doLimitMapExtent: (value) => ({ dispatch, store }) => {
      //   dispatch({
      //     type: "MAP_SET_LIMIT_EXTENT",
      //     payload: { limitExtent: value, shouldResetViewState: true },
      //   });
      // },

      selectMap: (state) => {
        return state.map.map;
      },

      selectMapOpts: (state) => {
        return state.map.opts;
      },

      selectMapGeoProjection: (state) => {
        return state.map.geoProjection;
      },

      selectMapWebProjection: (state) => {
        return state.map.webProjection;
      },

      selectMapCenter: (state) => {
        return state.map.center;
      },

      selectMapZoom: createSelector("selectAppTime", "selectMap", (_, map) => {
        if (!map) return 0;
        const view = map.getView();
        return view.getZoom();
      }),

      selectMapRotation: (state) => {
        return state.map.rotation;
      },

      selectMapMinZoom: (state) => {
        return state.map.minZoom;
      },

      selectMapMaxZoom: (state) => {
        return state.map.maxZoom;
      },

      reactMapShouldResetViewState: (state) => {
        if (state.map.shouldResetViewState)
          return { actionCreator: "doMapResetViewState" };
      },
      reactMapShouldUpdateViewState: (state) => {
        if (state.map.shouldUpdateViewState)
          return { actionCreator: "doMapUpdateViewState" };
      },

      persistActions: config.persistMapState
        ? ["MAP_UPDATED_VIEW_STATE_FINISHED", "MAP_RESET_MAP_VIEW"]
        : [],

      init: (store) => {
        window.addEventListener("resize", store.doMapUpdateSize);
      },
    },
    config.addons
  );
};
