function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }

function _createForOfIteratorHelperLoose(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } it = o[Symbol.iterator](); return it.next.bind(it); }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

import React from 'react';
import { render } from "react-dom";
import { DeckGL } from '@deck.gl/react';
import { GeoJsonLayer } from '@deck.gl/layers';
import { StaticMap } from 'react-map-gl';
import { BASEMAP } from '@deck.gl/carto';
import { feature } from 'topojson-client';
import chroma from "chroma-js";
import AbsoluteLegend from './AbsoluteLegend';
import { WebMercatorViewport } from '@deck.gl/core';
import bbox from "@turf/bbox";
import "./custom.css";
import Plot from 'react-plotly.js'; // Custom react-plotly

/*
import Plotly from '@uoa-eresearch/plotly.js/dist/plotly'
import createPlotlyComponent from 'react-plotly.js/factory';
const Plot = createPlotlyComponent(Plotly);
*/

import { ReflexContainer, ReflexSplitter, ReflexElement } from 'react-reflex';
import 'react-reflex/styles.css';

function addObjects(a, b) {
  for (var k in b) {
    if (typeof b[k] == "number") {
      if (!a[k]) a[k] = 0;
      a[k] += b[k];
    } else if (typeof b[k] == "object") {
      if (!a[k]) a[k] = {};
      a[k] = addObjects(a[k], b[k]);
    }
  }

  return a;
}

function aggregate(items) {
  var result = {};

  for (var _iterator = _createForOfIteratorHelperLoose(items), _step; !(_step = _iterator()).done;) {
    var item = _step.value;
    result = addObjects(result, item);
  }

  result["aggregate"] = true;
  console.log("Aggregate calculated. Result:", result);
  return result;
}

function getViewStateForBounds(BOUNDS) {
  var viewport = new WebMercatorViewport({
    width: window.innerWidth,
    height: window.innerHeight
  });

  var _viewport$fitBounds = viewport.fitBounds(BOUNDS, {
    padding: 100
  }),
      longitude = _viewport$fitBounds.longitude,
      latitude = _viewport$fitBounds.latitude,
      zoom = _viewport$fitBounds.zoom;

  return {
    longitude: longitude,
    latitude: latitude,
    zoom: zoom
  };
}

var DecklyComponent = /*#__PURE__*/function (_React$Component) {
  _inheritsLoose(DecklyComponent, _React$Component);

  function DecklyComponent(props) {
    var _this;

    _this = _React$Component.call(this, props) || this;
    var accessor = _this.props.colorBy;

    if (typeof accessor == "object") {
      var accessorName = Object.keys(accessor)[0];
      accessor = accessor[accessorName];
    }

    var legendTitle = _this.props.legendTitle;

    if (typeof legendTitle == "object") {
      var accessorName = Object.keys(legendTitle)[0];
      legendTitle = legendTitle[accessorName];
    }

    _this.state = {
      items: [],
      accessor: accessor,
      accessorName: accessorName,
      legendTitle: legendTitle,
      isLoaded: false,
      per: false,
      height: true,
      // if bivariate, should second variable be shown as height?
      animating: false,
      // true when shifting between height == True and height == False
      hoverInfo: {},
      limits: _this.props.limits,
      selected: null,
      viewport: {}
    };
    _this.handleInputChange = _this.handleInputChange.bind(_assertThisInitialized(_this));
    _this.changeAccessor = _this.changeAccessor.bind(_assertThisInitialized(_this));
    _this.toggleHeight = _this.toggleHeight.bind(_assertThisInitialized(_this));
    _this.setAnimating = _this.setAnimating.bind(_assertThisInitialized(_this));
    return _this;
  }

  var _proto = DecklyComponent.prototype;

  _proto.changeAccessor = function changeAccessor(accessorName) {
    this.setState({
      accessorName: accessorName,
      accessor: this.props.colorBy[accessorName],
      legendTitle: this.props.legendTitle[accessorName]
    });
  };

  _proto.toggleHeight = function toggleHeight() {
    this.setState({
      height: !this.state.height,
      animating: true
    });
  };

  _proto.setAnimating = function setAnimating(animating) {
    this.setState({
      animating: animating
    });
  };

  _proto.componentDidMount = function componentDidMount() {
    var _this2 = this;

    document.title = this.props.title;
    fetch(this.props.data).then(function (results) {
      return results.json();
    }).then(function (json) {
      if (json.type == "Topology") {
        json = feature(json, Object.keys(json.objects)[0]); // Convert topojson to geojson
      }

      var b = bbox(json);
      console.log("GeoJSON loaded:", json, "Bounds:", b);
      b = [[b[0], b[1]], [b[2], b[3]]];

      _this2.setState({
        isLoaded: true,
        items: json.features,
        aggregate: aggregate(json.features.map(function (f) {
          return f.properties;
        })),
        viewport: getViewStateForBounds(b)
      });
    });
  };

  _proto.handleInputChange = function handleInputChange(event) {
    var _this$setState;

    var target = event.target;
    var value = target.type === 'checkbox' ? target.checked : target.value;
    var name = target.name;
    this.setState((_this$setState = {}, _this$setState[name] = value, _this$setState));
  };

  _proto.render = function render() {
    var _this3 = this;

    if (!this.state.isLoaded) return null;
    var data = this.state.items.map(this.state.accessor);

    if (!this.props.limits) {
      // Unless the user manually passed in limits, calculate them
      if (typeof data[0] == "number") {
        this.state.limits = chroma.limits(data, 'e', 1);
      } else if (typeof data[0] == "object") {
        this.state.limits = [chroma.limits(data.map(function (v) {
          return v[0];
        }), 'e', 1), chroma.limits(data.map(function (v) {
          return v[1];
        }), 'e', 1)];
      }

      console.log("Colourmap limits:", this.state.limits);
    }

    var colorScale = this.props.colorScale;
    console.log("Render()", this.state);

    if (typeof colorScale == "string" || typeof data[0] == "number") {
      if (typeof colorScale == "object") {
        colorScale = colorScale[0];
      }

      colorScale = chroma.scale(colorScale).domain(this.state.limits);
    } else if (typeof colorScale == "object") {
      // Bivariate
      colorScale = [chroma.scale(colorScale[0]).domain(this.state.limits[0]), chroma.scale(colorScale[1]).domain(this.state.limits[1])];
    }

    var layers = [new GeoJsonLayer({
      id: 'geojson',
      data: this.state.items,
      opacity: 0.8,
      lineWidthUnits: "pixels",
      lineWidthMinPixels: 1,
      extruded: this.state.height || this.state.animating,
      wireframe: true,
      getElevation: this.state.height && typeof colorScale == "object" ? function (f) {
        return (_this3.state.accessor(f)[1] - _this3.state.limits[1][0]) / _this3.state.limits[1][0] * 20000;
      } : null,
      getLineWidth: function getLineWidth(f) {
        return f == _this3.state.selected ? 3 : 1;
      },
      getFillColor: function getFillColor(f) {
        if (typeof colorScale == "object") {
          return _this3.state.height ? colorScale[0](_this3.state.accessor(f)[0]).rgb() : chroma.blend(colorScale[0](_this3.state.accessor(f)[0]), colorScale[1](_this3.state.accessor(f)[1]), "multiply").rgb();
        } else {
          return colorScale(_this3.state.accessor(f)).rgb();
        }
      },
      getLineColor: function getLineColor(f) {
        return f == _this3.state.selected ? [255, 69, 0] : [0, 0, 0];
      },
      pickable: true,
      onHover: function onHover(info) {
        return _this3.state.selected == null ? _this3.setState({
          hoverInfo: info
        }) : null;
      },
      onClick: function onClick(info) {
        return _this3.state.selected == info.object ? _this3.setState({
          selected: null
        }) : _this3.setState({
          selected: info.object,
          hoverInfo: info
        });
      },
      transitions: {
        getElevation: {
          duration: 400,
          onEnd: function onEnd() {
            return _this3.setAnimating(false);
          }
        },
        getFillColor: 400
      },
      updateTriggers: {
        getLineWidth: this.state.selected,
        getLineColor: this.state.selected,
        getElevation: [this.state.height, this.state.accessor],
        getFillColor: [this.state.accessor, this.state.height]
      }
    })];
    return /*#__PURE__*/React.createElement(ReflexContainer, {
      orientation: "horizontal"
    }, /*#__PURE__*/React.createElement(ReflexElement, {
      className: "title",
      maxSize: 20
    }, this.props.title), /*#__PURE__*/React.createElement(ReflexElement, null, /*#__PURE__*/React.createElement(ReflexContainer, {
      orientation: "vertical"
    }, /*#__PURE__*/React.createElement(ReflexElement, {
      className: "map"
    }, /*#__PURE__*/React.createElement(DeckGL, {
      initialViewState: this.state.viewport,
      controller: true,
      layers: layers
    }, /*#__PURE__*/React.createElement(StaticMap, {
      mapStyle: BASEMAP.POSITRON
    }), this.state.hoverInfo.object && /*#__PURE__*/React.createElement("div", {
      className: "tooltip",
      style: {
        position: 'absolute',
        zIndex: 1,
        pointerEvents: 'none',
        left: this.state.hoverInfo.x,
        top: this.state.hoverInfo.y
      }
    }, this.props.hoverMessage(this.state.hoverInfo.object) + ": " + this.state.accessor(this.state.hoverInfo.object).toLocaleString()), /*#__PURE__*/React.createElement(AbsoluteLegend, {
      title: this.state.legendTitle,
      colorScale: colorScale,
      colorBy: this.props.colorBy,
      height: this.state.height,
      toggleHeight: this.toggleHeight,
      accessorName: this.state.accessorName,
      changeAccessor: this.changeAccessor,
      limits: this.state.limits,
      labels: this.props.legendLabels,
      steps: 5
    }))), /*#__PURE__*/React.createElement(ReflexSplitter, null), /*#__PURE__*/React.createElement(ReflexElement, {
      className: "plots"
    }, /*#__PURE__*/React.createElement("div", {
      id: "controls"
    }, this.props.perText && /*#__PURE__*/React.createElement("div", {
      id: "perWrapper"
    }, /*#__PURE__*/React.createElement("input", {
      name: "per",
      type: "checkbox",
      checked: this.state.per,
      onChange: this.handleInputChange
    }), /*#__PURE__*/React.createElement("label", {
      htmlFor: "per"
    }, this.props.perText))), this.props.plots.map(function (p) {
      var data = _this3.state.hoverInfo.object ? _this3.state.hoverInfo.object.properties : _this3.state.aggregate;
      data.aggregateData = _this3.state.aggregate;
      data.hoverObject = _this3.state.hoverInfo.object;
      if (!p.style) p.style = {};
      var DEFAULT_STYLE = {
        width: "100%",
        height: "300px"
      };

      if (p.type == "PCP") {
        data = _this3.state.items;

        if (_this3.state.per) {
          console.log("preperfunc", data);
          data = data.map(function (f) {
            return _this3.props.perFunc(f);
          });
          console.log("perfunc", data);
        }

        data = p.data(data, _this3.state.hoverInfo);
      } else {
        if (_this3.state.per) {
          data = _this3.props.perFunc(data);
        }

        data = p.data(data);
      }

      return /*#__PURE__*/React.createElement(Plot, {
        key: p.id,
        data: data,
        layout: _extends({}, p.layout, {
          title: p.layout.title(_this3.state.hoverInfo.object),
          barmode: 'stack'
        }),
        useResizeHandler: true,
        style: _extends({}, DEFAULT_STYLE, p.style),
        onHover: function onHover(d) {
          return _this3.setState({
            "plotly_hover": d
          });
        }
      });
    })))));
  };

  return DecklyComponent;
}(React.Component);

export default function Deckly(props) {
  document.body.innerHTML = "<div id='root'></div>";
  render( /*#__PURE__*/React.createElement(DecklyComponent, props), document.querySelector("#root"));
}