import sortBy from "lodash/sortBy";
import max from "lodash/max";
import sum from "lodash/sum";

export default {
  name: "print",

  getReducer: () => {
    const initialData = {
      mapCanvas: null,
      legendCanvas: null,
      _shouldExport: false,
    };

    return (state = initialData, { type, payload }) => {
      switch (type) {
        case "PRINT_ERROR":
        case "PRINT_LOADING":
        case "PRINT_MAP_CANVAS_GENERATED":
        case "PRINT_LEGEND_CANVAS_GENERATED":
        case "PRINT_EXPORTING":
          return Object.assign({}, state, payload);
        default:
          return state;
      }
    };
  },
  doSetMapCanvas: (map) => ({ dispatch }) => {
    let mapCanvas = document.createElement("canvas");
    const size = map.getSize();
    mapCanvas.width = size[0];
    mapCanvas.height = size[1];
    let mapContext = mapCanvas.getContext("2d");

    //Get visible layers on map
    Array.prototype.forEach.call(
      document.querySelectorAll(".ol-layer canvas"),
      function (canvas) {
        if (canvas.width > 0) {
          const opacity = canvas.parentNode.style.opacity;
          mapContext.globalAlpha = opacity === "" ? 1 : Number(opacity);
          var transform = canvas.style.transform;
          // Get the transform parameters from the style's transform matrix
          const matrix = transform
            .match(/^matrix\(([^\(]*)\)$/)[1]
            .split(",")
            .map(Number);
          // Apply the transform to the export map context
          CanvasRenderingContext2D.prototype.setTransform.apply(
            mapContext,
            matrix
          );
          mapContext.drawImage(canvas, 0, 0);
        }
      }
    );

    //Get scale on map
    Array.prototype.forEach.call(
      document.querySelectorAll(".ol-scale-line .ol-scale-line-inner"),
      function (node) {
        //Get scale value & width of scale div
        const scaleValue = node.textContent;
        const scaleWidth = node.style.width.replace("px", "").trim();

        //Calculate rect positioning
        const rectH = 20;
        const rectW = Number(scaleWidth); //width in pixels
        const rectX = 5; // x-coordinate of the upper-left corner
        const rectY = 5;

        //Draw/Style rect
        mapContext.fillStyle = "#000";
        mapContext.fillRect(rectX, rectY, rectW, rectH);

        //Draw/Style path within rect
        const pathStartX = rectX + 3;
        const pathStartY = rectY + 3;
        mapContext.beginPath();
        mapContext.moveTo(pathStartX, pathStartY);
        mapContext.lineTo(pathStartX, pathStartY + rectH / 2);
        mapContext.lineTo(pathStartX + (rectW - 6), pathStartY + rectH / 2);
        mapContext.lineTo(pathStartX + (rectW - 6), pathStartY);
        mapContext.strokeStyle = "#fff";

        //draw defined path
        mapContext.stroke();

        //Draw/Style text
        const txtX = pathStartX + (rectW / 2 - 6);
        const txtY = pathStartY + (rectH / 2 - 4);
        mapContext.font = "10px";
        mapContext.fillStyle = "#fff";
        mapContext.textAlign = "center";
        mapContext.fillText(scaleValue, txtX, txtY);
      }
    );

    dispatch({
      type: "PRINT_MAP_CANVAS_GENERATED",
      payload: {
        mapCanvas: mapCanvas,
      },
    });
  },
  doSetLegendCanvas: () => ({ dispatch, store }) => {
    const legendCanvases = store.selectLegendCanvases();
    const visibleLayers = store.selectVisibleLayers();
    const sortedLayers = sortBy(visibleLayers, ["sortOrder"]).reverse();
    let canvases = [];

    let legendCanvas = document.createElement("canvas");
    let legendCTX = legendCanvas.getContext("2d");

    sortedLayers.forEach((layer) => {
      if (legendCanvases.hasOwnProperty(layer.uid)) {
        // create single canvas for layer
        let layerCanvas = document.createElement("canvas");
        let layerCTX = layerCanvas.getContext("2d");

        // get displayName width
        const titleWidth =
          Number(
            layerCTX.measureText(legendCanvases[layer.uid].displayName).width
          ) + 50;

        //check if layer has a canvas
        if (legendCanvases[layer.uid].canvas.length) {
          //get largest width
          const largestWidth = max(legendCanvases[layer.uid].widths);

          //set canvas dimensions
          layerCanvas.width =
            titleWidth > largestWidth ? titleWidth : largestWidth;
          layerCanvas.height = sum(legendCanvases[layer.uid].heights) + 20;

          //set background
          layerCTX.fillStyle = "white";
          layerCTX.fillRect(0, 0, layerCanvas.width, layerCanvas.height);

          //add header
          layerCTX.font = "bold 12px Arial";
          layerCTX.fillStyle = "black";
          layerCTX.fillText(legendCanvases[layer.uid].displayName, 0, 15);

          let dy = 20; //starting y position

          legendCanvases[layer.uid].canvas.forEach((item) => {
            layerCTX.drawImage(item, 0, dy);
            dy += Number(item.height);
          });

          canvases.push(layerCanvas);
        } else {
          //set canvas dimensions
          layerCanvas.width = titleWidth;
          layerCanvas.height = 40;

          //set background
          layerCTX.fillStyle = "white";
          layerCTX.fillRect(0, 0, layerCanvas.width, layerCanvas.height);

          //add header
          layerCTX.font = "bold 12px Arial";
          layerCTX.fillStyle = "black";
          layerCTX.fillText(legendCanvases[layer.uid].displayName, 0, 15);

          //add text
          layerCTX.font = "10px Arial";
          layerCTX.fillStyle = "black";
          layerCTX.fillText("N/A", 5, 30);

          canvases.push(layerCanvas);
        }
      }
    });

    //calculate width & height for legend canvas.
    let legendCanvasHeight = 0;
    let legendCanvasWidth = 0;
    canvases.forEach((canvas) => {
      legendCanvasHeight += canvas.height;
      legendCanvasWidth =
        legendCanvasWidth > canvas.width ? legendCanvasWidth : canvas.width;
    });

    //set legend canvas dimensions
    legendCanvas.height = legendCanvasHeight;
    legendCanvas.width = legendCanvasWidth;

    //set legend canvas background
    legendCTX.fillStyle = "white";
    legendCTX.fillRect(0, 0, legendCanvas.width, legendCanvas.height);

    //add layer canvases to legend canvas
    let legendDY = 0;
    canvases.forEach((canvas) => {
      legendCTX.drawImage(canvas, 0, legendDY);
      legendDY += Number(canvas.height);
    });

    dispatch({
      type: "PRINT_LEGEND_CANVAS_GENERATED",
      payload: {
        legendCanvas: legendCanvas,
        _shouldExport: true,
      },
    });
  },
  doPrintMap: () => ({ dispatch, store }) => {
    if (window.navigator.msSaveBlob) {
      // check browser support
      dispatch({
        type: "PRINT_ERROR",
        payload: {
          notification: {
            title: "Print Notification",
            message: "Browser not supported.",
            level: "danger",
          },
        },
      });
      store.doBasicToolbarDeactivate();
    } else {
      dispatch({
        type: "PRINT_LOADING",
        payload: {
          notification: {
            title: "Print Notification",
            message: "Loading Image..",
            level: "success",
          },
        },
      });
      const map = store.selectMap();

      map.once("rendercomplete", () => {
        //Draw map canvas
        store.doSetMapCanvas(map);

        //Draw legend canvas
        store.doSetLegendCanvas();
      });

      //https://openlayers.org/en/latest/apidoc/module-ol_Map-Map.html#renderSync
      map.renderSync();
    }
  },
  doPrintMapExport: () => ({ dispatch, store }) => {
    dispatch({
      type: "PRINT_EXPORTING",
      payload: {
        _shouldExport: false,
      },
    });
    let print = document.createElement("a");
    const mapCanvas = store.selectPrintMapCanvas();
    const legendCanvas = store.selectPrintLegendCanvas();

    //create single canvas for map and legend
    let canvas = document.createElement("canvas");
    let ctx = canvas.getContext("2d");

    //set dimensions
    canvas.width = Number(mapCanvas.width) + Number(legendCanvas.width);
    canvas.height =
      Number(mapCanvas.height) > Number(legendCanvas.height)
        ? Number(mapCanvas.height)
        : Number(legendCanvas.height);

    //set background
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    ctx.drawImage(mapCanvas, 0, 0);
    ctx.drawImage(legendCanvas, mapCanvas.width, 0); //draw legend to the right of mapCanvas

    print.download = "map.png";
    print.href = canvas.toDataURL();
    document.body.appendChild(print);
    print.click();
    print.remove();

    store.doBasicToolbarDeactivate();
  },
  selectPrintMapCanvas: (state) => {
    return state.print.mapCanvas;
  },
  selectPrintLegendCanvas: (state) => {
    return state.print.legendCanvas;
  },
  reactLegendShouldExport: (state) => {
    if (state.print._shouldExport) return { actionCreator: "doPrintMapExport" };
  },
};
