import { transform } from "ol/proj";
import { createSelector } from "redux-bundler";
import _ from "lodash";

const animateView = (options, view) => {
  view.animate(options);
};

const zoomToLocation = (center, zoom, view) => {
  animateView(
    {
      center: center,
      zoom: zoom,
    },
    view
  );
};

export default {
  name: "view",

  getReducer: () => {
    const initialData = {
      shouldReadUrlParams: false,
      shouldSetDefaults: false,
      center: [-98.579, 39.828],
      zoom: 4,
      rotation: 0,
      position: {},
      heading: 0,
      pitch: 0,
      roll: 0,
      minZoom: 0,
      maxZoom: 22,
      action: null,
      updating: false,
      listening: false,
      shouldUpdateZoom: false,
      // horse: true,
      // limitMapExtent: true,
    };

    return (state = initialData, { type, payload }) => {
      switch (type) {
        case "VIEW_SET_DEFAULTS_START":
        case "VIEW_SET_DEFAULTS":
        case "VIEW_SET_PARAMS_START":
        case "VIEW_SET_PARAMS_COMPLETE":
          return Object.assign({}, state, payload);
        case "WINDOW_LOCATION_PARSED":
          return Object.assign({}, state, {
            shouldReadUrlParams: true,
          });
        case "MAP_INITIALIZED":
          return Object.assign({}, state, {
            shouldSetDefaults: true,
          });
      }

      if (type === "VIEW_START_ACTION") {
        return Object.assign({}, state, {
          updating: true,
        });
      }

      if (type === "VIEW_END_ACTION") {
        return Object.assign({}, state, {
          updating: false,
        });
      }

      if (type === "VIEW_EVENTS_REGISTERED") {
        return Object.assign({}, state, {
          listening: true,
        });
      }

      if (type === "VIEW_CHANGED") {
        return Object.assign({}, state, payload);
      }

      return state;
    };
  },

  doViewSetDefaults: (d) => ({ dispatch, store }) => {
    // grab the config from our launch config, override built-in defaults

    dispatch({
      type: "VIEW_SET_DEFAULTS_START",
      payload: { shouldSetDefaults: false },
    });
    const config = {
      center:
        d.centerX && d.centerY ? [d.centerX, d.centerY] : [-95.579, 39.828],
      zoom: d.centerZ ? d.centerZ : 4,
      rotation: d.rotation ? d.rotation : 0,
      position: {},
      heading: d.heading3d ? d.heading3d : 0,
      pitch: 0,
      roll: 0,
      minZoom: d.minzoom ? d.minzoom : 0,
      maxZoom: d.maxzoom ? d.maxzoom : 22,
      // limitMapExtent: d.limitMapExtent ? d.limitMapExtent : false,
      
    };
    
    dispatch({ type: "VIEW_SET_DEFAULTS", payload: config });
  },

  doViewSetFromParams: () => ({ dispatch, store }) => {
    dispatch({
      type: "VIEW_SET_PARAMS_START",
      payload: { shouldReadUrlParams: false },
    });
    const config = store.selectViewState();
    const params = store.selectWindowLocationParams();

    // grab config from url parameters and override everything
    if (Number(params.x)) config.center[0] = Number(params.x);
    if (Number(params.y)) config.center[1] = Number(params.y);
    if (Number(params.z)) config.zoom = Number(params.z);

    store.doSetView(config);
    //dispatch({ type: 'VIEW_SET_PARAMS_COMPLETE', payload: config });
  },

  doZoomToExtent: (extent, callback) => ({ dispatch, store }) => {
    dispatch({ type: "VIEW_START_ACTION" });

    const map = store.selectMap();
    const view = store.selectView();
    view.fit(extent, {
      size: map.getSize(),
      duration: 500,
      minResolution: 10,
      callback: () => {
        if (callback && typeof callback === "function") callback();
        dispatch({ type: "VIEW_END_ACTION" });
      },
    });
  },

  doZoomToLayerExtent: (uid) => ({ dispatch, store }) => {
    // @TODO get the layer from the store and figure out where to zoom to
  },

  doZoomToLocation: (center, zoom) => ({ dispatch, store }) => {
    dispatch({ type: "VIEW_START_ACTION" });
    const view = store.selectView();
    zoomToLocation(center, zoom, view);
    dispatch({ type: "VIEW_END_ACTION" });
  },

  doZoomIn: () => ({ dispatch, store }) => {
    dispatch({ type: "VIEW_START_ACTION" });
    const view = store.selectView();
    const currentZoom = view.getZoom();
    animateView(
      {
        zoom: currentZoom + 1,
      },
      view
    );
    dispatch({ type: "VIEW_END_ACTION" });
  },

  doZoomOut: () => ({ dispatch, store }) => {
    dispatch({ type: "VIEW_START_ACTION" });
    const view = store.selectView(); // pulling view 
    const currentZoom = view.getZoom(); // getting current view
    animateView( // subtract 1 from current zoom 
      {
        zoom: currentZoom - 1,
      },
      view
    );
    dispatch({ type: "VIEW_END_ACTION" });
  },

  doSetRotationNorth: () => ({ dispatch, store }) => {
    dispatch({ type: "VIEW_START_ACTION" });
    const view = store.selectView();
    animateView(
      {
        rotation: 0,
      },
      view
    );
    dispatch({ type: "VIEW_END_ACTION" });
  },

  doUpdateView: (e) => ({ dispatch, store }) => {
    const map = e.map;
    const view = map.getView();
    const zoom = view.getZoom();
    const center = transform(
      view.getCenter(),
      store.selectMapProjection(),
      "EPSG:4326"
    );
    const rotation = view.getRotation() * (180 / Math.PI);
    dispatch({ type: "VIEW_CHANGED", payload: { center, zoom, rotation } });
  },

  doSetView: (options) => ({ dispatch, store }) => {
    const view = store.selectView();
    if (!view) return null;
    animateView(
      {
        center: transform(
          options.center,
          "EPSG:4326",
          store.selectMapProjection()
        ),
        zoom: options.zoom,
        rotation: options.rotation,
      },
      view
    );
  },

  doListenToMap: () => ({ dispatch, store }) => {
    const map = store.selectMap();
    // now handled in the map bundle, need to dedupe these
    //map.on('moveend', _.throttle(store.doUpdateView, 200));
    dispatch({ type: "VIEW_EVENTS_REGISTERED" });
  },

  selectViewState: (state) => {
    return state.view;
  },

  selectViewAction: (state) => {
    return state.view.action;
  },

  selectViewCenter: createSelector(
    "selectAppTime",
    "selectMap",
    "selectViewState",
    (_, map, viewState) => {
      if (!map) return viewState.center;
      const view = map.getView();
      if (!view) return viewState.center;
      return view.getCenter();
    }
  ),

  selectMapSize: createSelector(
    "selectMap",
    "selectAppTime",
    (map, appTime) => {
      if (!map) return null;
      return map.getSize();
    }
  ),

  selectMapImageDisplay: createSelector("selectMapSize", (size) => {
    if (!size) return null;
    return [size[0], size[1], 92];
  }),

  selectMapExtent: createSelector(
    "selectAppTime",
    "selectView",
    "selectMapSize",
    "selectMapProjection",
    (_, view, size, mapProjection) => {
      if (!view) return null;
      const mapExtentWm = view.calculateExtent(size);
      const ll = transform(
        [mapExtentWm[0], mapExtentWm[1]],
        mapProjection,
        "EPSG:4326"
      );
      const ur = transform(
        [mapExtentWm[2], mapExtentWm[3]],
        mapProjection,
        "EPSG:4326"
      );
      return [ll[0], ll[1], ur[0], ur[1]];
    }
  ),

  selectResolution: createSelector(
    "selectView",
    "selectAppTime",
    (view, appTime) => {
      if (!view) return null;
      return view.getResolution();
    }
  ),

  reactAddListeners: (state) => {
    if (!state.view.listening && state.map.map2d)
      return { actionCreator: "doListenToMap" };
  },

  reactViewShouldReadUrlParams: (state) => {
    if (state.view.shouldReadUrlParams)
      return { actionCreator: "doViewSetFromParams" };
  },

  reactViewShouldSetDefaults: (state) => {
    if (state.view.shouldSetDefaults)
      return {
        actionCreator: "doViewSetDefaults",
        args: [state.config.default.map],
      };
  },
};
