import React from "react";
import ReactDOM from "react-dom";
import IdentifySidebar from "./identify-sidebar";
import IdentifyDetails from "./identify-details-table";
import IdentifyTable from "./identify-table";
import Popup from "./identify-popup";
import IdentifyBufferSettings from "./identify-buffer-settings";
import { getLayer } from "../../openlayers-utils/layer-utilities";
import createInteraction from "../../openlayers-utils/interaction-creator";
import Overlay from "ol/Overlay";
import { transform } from "ol/proj";
import centroid from "@turf/centroid";
import find from "lodash/find";
import { Fill, Stroke, Style, Circle as CircleStyle } from "ol/style";
import Feature from "ol/Feature";
import Point from "ol/geom/Point";
import Polygon from "ol/geom/Polygon";
import buffer from "@turf/buffer";
import { point, polygon } from "@turf/helpers";
import GeoJSON from "ol/format/GeoJSON";

const style = new Style({
  fill: new Fill({
    color: "rgba(84, 240, 240, 0.3)",
  }),
  stroke: new Stroke({
    color: "rgb(84, 240, 240)",
    width: 1,
  }),
  image: new CircleStyle({
    radius: 5,
    stroke: new Stroke({
      color: "rgb(84, 240, 240)",
      width: 1,
    }),
    fill: new Fill({
      color: "rgba(84, 240, 240, 0.3)",
    }),
  }),
});

const polygonLayer = getLayer({
  serviceType: "LocalVector",
  visible: true,
  geometryType: "Polygon",
  style: style,
  zIndex: 1000,
});

const polygonDraw = createInteraction({
  interaction: "draw",
  source: polygonLayer.getSource(),
  type: "Polygon",
});

const pointLayer = getLayer({
  serviceType: "LocalVector",
  visible: true,
  geometryType: "Point",
  style: style,
  zIndex: 1000,
});

//TODO: debug the add'l geom rendered off the coast of Africa
//       when source is set using "pointLayer.getSource()"
const pointDraw = createInteraction({
  interaction: "draw",
  source: polygonLayer.getSource(),
  type: "Point",
});

const bufferLayer = getLayer({
  serviceType: "LocalVector",
  visible: true,
  geometryType: "Polygon",
  style: new Style({
    fill: new Fill({
      color: "rgba(192,192,192,0.4)",
    }),
    stroke: new Stroke({
      color: "rgba(192,192,192,1)",
      width: 1,
    }),
    image: new CircleStyle({
      radius: 5,
      stroke: new Stroke({
        color: "rgba(192,192,192,1)",
        width: 1,
      }),
      fill: new Fill({
        color: "rgba(192,192,192,0.4)",
      }),
    }),
  }),
  zIndex: 1000,
});

const identify = {
  overlays: [],
};

export default {
  name: "identifyUi",

  getReducer() {
    const initialData = {
      selectBy: null,
      displayAs: "sidebar",
      bufferUnit: "km",
      bufferUnitFull: "kilometers",
      bufferValue: "",
      bufferFeature: null,
      shouldQuery: false,
      shouldRenderResults: false,
      shouldRenderDetails: false,
      geom: null,
      results: [],
      selectedFeature: null,
      selectedLayer: null,
    };

    return (state = initialData, { type, payload }) => {
      switch (type) {
        case "IDENTIFY_UI_STARTED":
        case "IDENTIFY_UI_STOPPED":
        case "IDENTIFY_UI_DISPLAY_AS":
        case "IDENTIFY_UI_RESULTS_DISPLAYED":
        case "IDENTIFY_UI_RESULT_PICKED":
        case "IDENTIFY_UI_DETAILS_DISPLAYED":
        case "IDENTIFY_UI_TABLE_DISPLAYED":
        case "IDENTIFY_UI_NO_RESULTS_RETURNED":
        case "IDENTIFY_UI_BUFFER_SETTINGS_OPENED":
        case "IDENTIFY_UI_BUFFER_UNIT_UPDATED":
        case "IDENTIFY_UI_BUFFER_VALUE_UPDATED":
        case "IDENTIFY_UI_BUFFER_FEATURE_UPDATED":
          return Object.assign({}, state, payload);
        case "IDENTIFY_ERROR":
          return Object.assign({}, state, {
            selectBy: null,
          });
        case "IDENTIFY_SUCCESS":
          return Object.assign({}, state, {
            shouldRenderResults: true,
            selectBy: null,
          });
        default:
          return state;
      }
    };
  },
  doToggleIdentifyView:
    (newView) =>
    ({ dispatch, store }) => {
      const displayAs = store.selectIdentifyDisplayAs();

      // close/set display
      switch (displayAs) {
        case "sidebar":
          store.doPrimaryPanelClose();
          store.doRenderIdentifyTable();
          break;
        case "table":
          store.doSouthRegionClose();
          store.doRenderIdentifySidebar();
          break;
        default:
          break;
      }

      //set new display
      store.doSetDisplayAs(null, newView);
    },
  doUpdateIdentifyBuffer:
    (e) =>
    ({ dispatch, store }) => {
      e.preventDefault();
      e.stopPropagation();

      dispatch({
        type: "IDENTIFY_UI_BUFFER_VALUE_UPDATED",
        payload: { bufferValue: e.target.value },
      });
    },
  doStartIdentify:
    (e) =>
    ({ dispatch, store }) => {
      e.preventDefault();
      e.stopPropagation();
      const selectBy = e.currentTarget.dataset.selectby;

      dispatch({
        type: "IDENTIFY_UI_STARTED",
        payload: { selectBy: selectBy },
      });
      store.doAddIdentifyInteractions();
    },

  doAddIdentifyInteractions:
    () =>
    ({ store }) => {
      const selectBy = store.selectIdentifySelectBy();
      store.doRemoveIdentifyInteractions();
      if (selectBy === "point") {
        store.doIdentifyByPoint();
      } else if (selectBy === "polygon") {
        store.doIdentifyByPolygon();
      }
    },

  doRemoveIdentifyInteractions:
    () =>
    ({ dispatch, store }) => {
      store.doSetCursor();
      const map = store.selectMap();
      map.removeInteraction(pointDraw);
      map.removeInteraction(polygonDraw);
    },

  doSetIdentifyGeom:
    (e) =>
    ({ store }) => {
      e.preventDefault();
      e.stopPropagation();
      const map = store.selectMap();
      const feature = e.feature;
      const geom = feature.getGeometry();

      pointLayer.getSource().clear();
      polygonLayer.getSource().clear();
      bufferLayer.getSource().clear();

      map.removeLayer(pointLayer);
      map.removeLayer(polygonLayer);
      map.removeLayer(bufferLayer);

      store.doRenderIdentifyGeometry(geom);
      store.doIdentifySetCoords(geom);
    },
  doRenderIdentifyGeometry:
    (geom) =>
    ({ store }) => {
      const map = store.selectMap();
      const selectBy = store.selectIdentifySelectBy();
      const coords = geom.getCoordinates();

      if (selectBy === "point") {
        const src = pointLayer.getSource();
        src.addFeature(new Feature({ geometry: new Point(coords) }));

        store.doRenderIdentifyBufferPoint(new Point(coords));
        map.addLayer(pointLayer);
      } else {
        const src = polygonLayer.getSource();
        src.addFeature(new Feature({ geometry: new Polygon(coords) }));

        store.doRenderIdentifyBufferPolygon(new Polygon(coords));
        map.addLayer(polygonLayer);
      }
      store.doPauseIdentify();
    },
  doRenderIdentifyBufferPoint:
    (drawPt) =>
    ({ dispatch, store }) => {
      const bufferValue = store.selectIdentifyBufferValue();
      if (!bufferValue.length) return;

      const map = store.selectMap();
      const bufferUnitFull = store.selectIdentifyBufferUnitFull();
      const displayProjection = store.selectDisplayProjection();
      const projected = drawPt.transform("EPSG:3857", displayProjection);
      const pt = point(projected.getCoordinates());
      const src = bufferLayer.getSource();
      src.clear();

      const bufferFeature = buffer(pt, Number(bufferValue), {
        units: bufferUnitFull,
      });

      // convert the generated feature to a OpenLayers feature
      const identifyBuffer = new GeoJSON().readFeature(bufferFeature, {
        dataProjection: "EPSG:4326",
        featureProjection: "EPSG:3857",
      });

      src.addFeature(identifyBuffer);
      map.addLayer(bufferLayer);

      dispatch({
        type: "IDENTIFY_UI_BUFFER_FEATURE_UPDATED",
        payload: { bufferFeature: bufferFeature },
      });
    },
  doRenderIdentifyBufferPolygon:
    (drawPoly) =>
    ({ dispatch, store }) => {
      const bufferValue = store.selectIdentifyBufferValue();
      if (!bufferValue.length) return;

      const map = store.selectMap();
      const bufferUnitFull = store.selectIdentifyBufferUnitFull();
      const displayProjection = store.selectDisplayProjection();
      const projected = drawPoly.transform("EPSG:3857", displayProjection);

      const plyn = polygon(projected.getCoordinates());
      const src = bufferLayer.getSource();
      src.clear();

      const bufferFeature = buffer(plyn, Number(bufferValue), {
        units: bufferUnitFull,
      });

      // convert the generated feature to a OpenLayers feature
      const identifyBuffer = new GeoJSON().readFeature(bufferFeature, {
        dataProjection: "EPSG:4326",
        featureProjection: "EPSG:3857",
      });

      src.addFeature(identifyBuffer);
      map.addLayer(bufferLayer);

      dispatch({
        type: "IDENTIFY_UI_BUFFER_FEATURE_UPDATED",
        payload: { bufferFeature: bufferFeature },
      });
    },

  doIdentifyByPoint:
    () =>
    ({ dispatch, store }) => {
      store.doSetCursor("default");
      const map = store.selectMap();
      map.addInteraction(pointDraw);
    },

  doIdentifyByPolygon:
    () =>
    ({ dispatch, store }) => {
      store.doSetCursor("default");
      const map = store.selectMap();
      map.addInteraction(polygonDraw);
    },

  doPauseIdentify:
    () =>
    ({ dispatch, store }) => {
      store.doRemoveIdentifyInteractions();
      dispatch({ type: "IDENTIFY_UI_STOPPED", payload: { selectBy: null } });
    },

  doStopIdentify:
    () =>
    ({ dispatch, store }) => {
      const map = store.selectMap();
      const displayAs = store.selectIdentifyDisplayAs();
      store.doRemoveIdentifyInteractions();
      map.removeLayer(pointLayer);
      map.removeLayer(polygonLayer);
      map.removeLayer(bufferLayer);

      switch (displayAs) {
        case "popup":
          store.doClearIdentifyPopups();
          break;
        case "sidebar":
          store.doPrimaryPanelClose();
          break;
        case "table":
          store.doSouthRegionClose();
          break;
        default:
          break;
      }
      // close identify menu
      store.doBasicToolbarDeactivate();

      dispatch({ type: "IDENTIFY_UI_STOPPED", payload: { selectBy: null } });
    },

  doSetDisplayAs:
    (e, displayAs) =>
    ({ dispatch }) => {
      let newDisplay = null;
      if (e) {
        e.preventDefault();
        e.stopPropagation();

        newDisplay = e.currentTarget.dataset.displayas;
      } else {
        newDisplay = displayAs;
      }

      dispatch({
        type: "IDENTIFY_UI_DISPLAY_AS",
        payload: { displayAs: newDisplay },
      });
    },

  doRenderIdentifyPopups:
    () =>
    ({ dispatch, store }) => {
      dispatch({
        type: "IDENTIFY_UI_RESULTS_DISPLAYED",
        payload: { shouldRenderResults: false },
      });
      const map = store.selectMap();
      const results = store.selectIdentifyResults();
      const coords = store.selectIdentifyCoordinates();
      let popupCoords = coords;

      if (coords.length === 1) {
        // Get coords from polygon
        popupCoords = coords[0][0];
      }

      results.forEach((r) => {
        Object.keys(r.result).forEach((key) => {
          const fc = r.result[key];
          if (fc && fc.features && fc.features.length) {
            fc.features.forEach((feature) => {
              const el = document.createElement("div");
              const overlay = new Overlay({
                element: el,
                positioning: "bottom-center",
                offset: [0, -10],
              });
              map.addOverlay(overlay);
              ReactDOM.render(
                <Popup
                  overlay={overlay}
                  feature={feature}
                  map={map}
                  onClear={store.doClearOverlay}
                />,
                el
              );

              overlay.setPosition(
                transform(popupCoords, "EPSG:4326", "EPSG:3857")
              );
              identify.overlays.push(overlay);
            });
          } else {
            dispatch({
              type: "IDENTIFY_UI_NO_RESULTS_RETURNED",
              payload: {
                notification: {
                  message: `No results at this location for ${r.layerName}`,
                  level: "warning",
                },
              },
            });
          }
        });
      });
    },

  doClearOverlay:
    (overlay) =>
    ({ store }) => {
      const map = store.selectMap();
      if (identify.overlays && identify.overlays.length) {
        const idx = identify.overlays.indexOf(overlay);
        overlay.setPosition(undefined);
        const el = overlay.getElement();
        el.parentNode.removeChild(el);
        map.removeOverlay(overlay);
        identify.overlays.splice(idx, 1);
      }
    },

  doClearIdentifyPopups:
    () =>
    ({ store }) => {
      const map = store.selectMap();
      if (identify.overlays && identify.overlays.length) {
        identify.overlays.forEach((overlay) => {
          overlay.setPosition(undefined);
          const el = overlay.getElement();
          el.parentNode.removeChild(el);
          map.removeOverlay(overlay);
        });
        identify.overlays = [];
      }
    },

  doRenderIdentifySidebar:
    () =>
    ({ dispatch, store }) => {
      dispatch({
        type: "IDENTIFY_UI_RESULTS_DISPLAYED",
        payload: { shouldRenderResults: false },
      });
      const results = store.selectIdentifyResults();

      if (results.length) {
        for (let i = 0; i < results.length; i++) {
          const rDataset = Object.keys(results[i].result);
          for (let j = 0; j < rDataset.length; j++) {
            const fc = results[i].result[rDataset[j]];
            if (fc && fc.features && fc.features.length) {
              //open sidebar panel
              store.doPrimaryPanelOpen({
                openedBy: "IdentifyTools",
                children: [IdentifySidebar],
              });
              break;
            } else {
              dispatch({
                type: "IDENTIFY_UI_NO_RESULTS_RETURNED",
                payload: {
                  notification: {
                    message: `No results at this location for ${results[i].layerName}`,
                    level: "warning",
                  },
                },
              });
            }
          }
        }
      }
    },

  doOpenIdentifyDetails:
    (feature, layer) =>
    ({ dispatch }) => {
      dispatch({
        type: "IDENTIFY_UI_RESULT_PICKED",
        payload: {
          selectedFeature: feature,
          selectedLayer: layer,
          shouldRenderDetails: true,
        },
      });
    },

  doRenderIdentifyDetails:
    () =>
    ({ dispatch, store }) => {
      dispatch({
        type: "IDENTIFY_UI_DETAILS_DISPLAYED",
        payload: { shouldRenderDetails: false },
      });
      store.doPrimaryPanelOpen({
        openedBy: "IdentifyTools",
        children: [IdentifyDetails],
      });
    },

  doRenderIdentifyTable:
    () =>
    ({ dispatch, store }) => {
      dispatch({
        type: "IDENTIFY_UI_RESULTS_DISPLAYED",
        payload: { shouldRenderResults: false },
      });
      const results = store.selectIdentifyResults();
      const cmLayerNames = store.selectCmLayerNames();
      const uidMap = store.selectUidMap();
      const subLayerNameMatch = /[:](.+)/i;

      results.forEach((r) => {
        const id = r.dataset;
        const lyr = find(cmLayerNames, { teamDatasetId: id });
        const lyrName = !lyr ? uidMap[id] : lyr.displayName;

        Object.keys(r.result).forEach((key) => {
          const fc = r.result[key];
          const subLayer = subLayerNameMatch.exec(key);
          const subLayerName = subLayer
            ? subLayer[0] !== lyrName
              ? subLayer[0]
              : ""
            : "";
          if (fc && fc.features && fc.features.length) {
            store.doSouthRegionOpen({
              component: IdentifyTable,
              options: {
                features: fc.features,
              },
              openedBy: "identify",
              displayName: `${lyrName}${subLayerName}`,
            });
          } else {
            dispatch({
              type: "IDENTIFY_UI_NO_RESULTS_RETURNED",
              payload: {
                notification: {
                  message: `No results at this location for ${r.layerName}`,
                  level: "warning",
                },
              },
            });
          }
        });
      });
    },
  doShowBufferUnits:
    (e) =>
    ({ dispatch, store }) => {
      e.preventDefault();
      e.stopPropagation();

      store.doModalOpen({
        children: [IdentifyBufferSettings],
        options: { smSize: true },
        openedBy: "Identify Tool",
      });
      dispatch({ type: "IDENTIFY_UI_BUFFER_SETTINGS_OPENED" });
    },
  doUpdateBufferUnit:
    (e) =>
    ({ dispatch, store }) => {
      e.preventDefault();
      e.stopPropagation();

      dispatch({
        type: "IDENTIFY_UI_BUFFER_UNIT_UPDATED",
        payload: {
          bufferUnit: e.target.name,
          bufferUnitFull: e.target.textContent.toLowerCase(),
        },
      });

      //close modal after selection
      store.doModalClose();
    },

  selectIdentifySelectBy: (state) => {
    return state.identifyUi.selectBy;
  },

  selectIdentifyDisplayAs: (state) => {
    return state.identifyUi.displayAs;
  },

  selectIdentifyDetailFeature: (state) => {
    return state.identifyUi.selectedFeature;
  },

  selectIdentifyDetailLayer: (state) => {
    return state.identifyUi.selectedLayer;
  },
  selectIdentifyBufferUnit: (state) => {
    return state.identifyUi.bufferUnit;
  },
  selectIdentifyBufferUnitFull: (state) => {
    return state.identifyUi.bufferUnitFull;
  },
  selectIdentifyBufferValue: (state) => {
    return state.identifyUi.bufferValue;
  },
  selectIdentifyBufferFeature: (state) => {
    return state.identifyUi.bufferFeature;
  },

  reactShouldRunIdentify: (state) => {
    if (state.identifyUi.shouldQuery) return { actionCreator: "doIdentify" };
  },

  reactShouldRenderIdentifyResults: (state) => {
    if (state.identifyUi.shouldRenderResults) {
      if (state.identifyUi.displayAs === "popup") {
        return { actionCreator: "doRenderIdentifyPopups" };
      } else if (state.identifyUi.displayAs === "sidebar") {
        return { actionCreator: "doRenderIdentifySidebar" };
      } else if (state.identifyUi.displayAs === "table") {
        return { actionCreator: "doRenderIdentifyTable" };
      }
    }
  },

  reactShouldRenderIdentifyDetails: (state) => {
    if (state.identifyUi.shouldRenderDetails)
      return { actionCreator: "doRenderIdentifyDetails" };
  },

  init: (store) => {
    pointDraw.on("drawend", store.doSetIdentifyGeom);
    polygonDraw.on("drawend", store.doSetIdentifyGeom);
  },
};
