import Collection from "ol/Collection";
import Style from "ol/style/Style";
import Fill from "ol/style/Fill";
import Stroke from "ol/style/Stroke";
import Circle from "ol/style/Circle";
import XYZ from "ol/source/XYZ";
import TileArcGISRest from "ol/source/TileArcGISRest";
import ImageArcGISRest from "ol/source/ImageArcGISRest";
import ImageWMS from "ol/source/ImageWMS";
import TileWMS from "ol/source/TileWMS";
import VectorSrc from "ol/source/Vector";
import Tile from "ol/layer/Tile";
import Image from "ol/layer/Image";
import Vector from "ol/layer/Vector";
import GeoJSON from "ol/format/GeoJSON";
import vectorStyleCreator from "./vector-style";
import xhr from "xhr";
import EsriJSON from "ol/format/EsriJSON.js";
import { tile, bbox } from "ol/loadingstrategy";
import TileGrid from "ol/tilegrid/TileGrid";
import { createXYZ } from "ol/tilegrid";
import async from "async";
import { createStyleFunctionFromUrl, setMapProjection } from "ol-esri-style";

const esrijsonFormat = new EsriJSON();

const origin = window.location.origin;

const isCrossOrigin = (url) => {
  if (!!window.MSInputMethodContext && !!document.documentMode) return null;
  if (url) {
    if (url.indexOf(origin) === -1) return "anonymous";
  } else {
    return null;
  }
};

// const defaultVectorStyle = new Style({
//   fill: new Fill({
//     color: 'rgba(255, 255, 255, 0.2)'
//   }),
//   stroke: new Stroke({
//     color: 'rgba(0, 0, 0, 0.5)',
//     lineDash: [10, 10],
//     width: 2
//   }),
//   image: new Circle({
//     radius: 3,
//     stroke: new Stroke({
//       color: 'rgba(0, 0, 0, 0.7)'
//     }),
//     fill: new Fill({
//       color: 'rgba(255, 255, 255, 0.2)'
//     })
//   })
// });

function formatLabeledUrl(url) {
  const delim = url.indexOf("?") === -1 ? "?" : "&";
  return `${url}${delim}label=true`;
}

function getXyzSource(options) {
  const labelsOn = !!options.labelDefault;
  const url = labelsOn ? formatLabeledUrl(options.url) : options.url;
  return new XYZ({
    attributions: options.attributions || options.attribution,
    attributionsCollapsible: true,
    maxZoom: options.maxzoom,
    minZoom: options.minzoom,
    url: url,
    crossOrigin: isCrossOrigin(options.url),
  });
}

function getArcGISTiledSource(options) {
  return new TileArcGISRest({
    url: options.url,
    params: {
      LAYERS: "show:" + options.layerList,
    },
    crossOrigin: isCrossOrigin(options.url),
  });
}

function getArcGISImageSource(options) {
  return new ImageArcGISRest({
    url: options.url,
    params: {
      LAYERS: "show:" + options.layerList,
    },
    crossOrigin: isCrossOrigin(options.url),
  });
}

function getWmsSource(options) {
  const params = options.params || options;
  params.LAYERS = options.layerList || options.LAYERS;
  if (options.tiled === true) {
    return new TileWMS({
      url: options.url,
      params: params,
      crossOrigin: isCrossOrigin(options.url),
    });
  }
  return new ImageWMS({
    url: options.url,
    params: params,
    crossOrigin: isCrossOrigin(options.url),
  });
}

function getLocalVectorSource(options) {
  const src = new VectorSrc();
  const { features } = options;
  if (features) src.addFeatures(features);
  return src;
}

function getRemoteVectorSource(options) {
  const src = new VectorSrc({
    format: new GeoJSON(),
    url: options.url,
  });
  // @TODO figure out how to re-enable adding features to the identify tool
  // src.on('addfeature', (e) => {
  //   const uid = options.uid;
  //   const g = new GeoJSON();
  //   const geojson = g.writeFeatureObject(e.feature, { featureProjection: 'EPSG:3857', dataProjection: 'EPSG:4326' });
  //   window.store.doGeojsonIdentAddFeatures(uid, geojson);
  // })
  return src;
}

function getEsriVectorSource(options) {
  let serviceUrl = options.url;
  const layer = options.layerList;
  const trailingSlashMatch = /\/$/;

  if (!trailingSlashMatch.test(serviceUrl)) serviceUrl += "/";

  const src = new VectorSrc({
    loader: function (extent, resolution, projection) {
      const g = encodeURIComponent(
        `{"xmin":${extent[0]},"ymin":${extent[1]},"xmax":${extent[2]},"ymax":${extent[3]},"spatialReference":{"wkid":102100}}`
      );
      const url = `${serviceUrl}${layer}/query/?f=json&returnGeometry=true&spatialRel=esriSpatialRelIntersects&geometry=${g}&geometryType=esriGeometryEnvelope&inSR=102100&outFields=*&outSR=102100`;
      xhr.get(url, (err, response, body) => {
        if (err) return console.log(err);
        const data = JSON.parse(body);
        const features = esrijsonFormat.readFeatures(data, {
          featureProjection: projection,
        });
        if (features.length > 0) {
          src.addFeatures(features);
        }
      });
    },
    strategy: tile(
      createXYZ({
        tileSize: 512,
      })
    ),
  });
  return src;
}

function getTileLayer(options, source) {
  return new Tile({
    source: source,
    visible: options.visible,
    zIndex: options.zIndex || 0,
    opacity: options.opacity || 1,
    maxZoom: options.maxzoom,
    minZoom: options.minzoom,
  });
}

function getImageLayer(options, source) {
  return new Image({
    source: source,
    visible: options.visible,
    zIndex: options.zIndex || 0,
    opacity: options.opacity || 1,
    maxZoom: options.maxzoom,
    minZoom: options.minzoom,
  });
}

function getLocalVectorLayer(options, source) {
  return new Vector({
    altitudeMode: "clampToGround",
    source: source,
    visible: options.visible,
    style: vectorStyleCreator(options),
    zIndex: options.zIndex || 0,
    opacity: options.opacity || 1,
    maxZoom: options.maxzoom,
    minZoom: options.minzoom,
  });
}

function getRemoteVectorLayer(options, source) {
  const lyr = new Vector({
    altitudeMode: "clampToGround",
    source: source,
    visible: options.visible,
    style: vectorStyleCreator(options),
    zIndex: options.zIndex || 0,
    opacity: options.opacity || 1,
    renderMode: "image",
    maxZoom: options.maxzoom,
    minZoom: options.minzoom,
  });
  const uid = options.uid;
  // window.store.doGeojsonIdentAddFeatures(uid, null, lyr);
  return lyr;
}

function getEsriVectorLayer(options, source, map) {
  setMapProjection(map.getView().getProjection());
  const lyr = new Vector({
    altitudeMode: "clampToGround",
    source: source,
    visible: options.visible,
    zIndex: options.zIndex || 0,
    opacity: options.opacity || 1,
    renderMode: "image",
    maxZoom: options.maxzoom,
    minZoom: options.minzoom,
  });
  
  if(options.styleFn) {    
    const styleFn = eval(options.styleFn);
    lyr.setStyle(styleFn[0]);
  } 
  else
  {
    createStyleFunctionFromUrl(`${options.url}/${options.layerList}`)
      .then((styleFunction) => {
        lyr.setStyle(styleFunction);
      })
      .catch(function (error) {
        console.log(`${options.url}/${options.layerList}:  ${error}`);
      });
  }
  return lyr;
}

function getSource(options) {
  switch (options.serviceType) {
    case "XYZ":
      return getXyzSource(options);
    case "ArcGIS":
      return getArcGISTiledSource(options);
    case "ArcGISImage":
      return getArcGISImageSource(options);
    case "WMS":
      return getWmsSource(options);
    case "LocalVector":
      return getLocalVectorSource(options);
    case "RemoteVector":
      return getRemoteVectorSource(options);
    case "EsriFeatureService":
      return getEsriVectorSource(options);
    default:
      return null;
  }
}

function getLayer(options, source, map) {
  if (!source) source = getSource(options);
  switch (options.serviceType) {
    case "XYZ":
      return getTileLayer(options, source);
    case "ArcGIS":
      return getTileLayer(options, source);
    case "ArcGISImage":
      return getImageLayer(options, source);
    case "WMS":
      return getImageLayer(options, source);
    case "LocalVector":
      return getLocalVectorLayer(options, source);
    case "RemoteVector":
      return getRemoteVectorLayer(options, source);
    case "EsriFeatureService":
      return getEsriVectorLayer(options, source, map);
    default:
      return null;
  }
}

export { getSource, getLayer };
