import React from 'react'
import ReactApexChart from 'react-apexcharts'
import ApexCharts from 'apexcharts'
import './styles.css'

var TOTAL_POINTS = 500000

function generateData(count) {
  var data = new Array(count)
  var startTime = new Date('2010-01-01T00:00:00Z').getTime()
  var step = 60 * 1000 // 1 minute
  var y = 100
  for (var i = 0; i < count; i++) {
    // Random walk with periodic components — gives visible structure at
    // multiple zoom levels (slow trend + faster cycle + noise).
    y +=
      (Math.random() - 0.5) * 2 +
      Math.sin(i / 5000) * 0.05 +
      Math.cos(i / 1200) * 0.02
    data[i] = [startTime + i * step, +y.toFixed(3)]
  }
  return data
}

var seriesData = generateData(TOTAL_POINTS)
var lastRenderMs = 0
var lastZoomLabel = 'full range'

function fmt(ts) {
  var d = new Date(ts)
  return (
    d.getFullYear() +
    '-' +
    String(d.getMonth() + 1).padStart(2, '0') +
    '-' +
    String(d.getDate()).padStart(2, '0')
  )
}

// Accepts the live chart instance as an argument so the React and Vue builds
// (which have no global `chart` var) can share this stateless helper.
function updateStats(chart) {
  var statsEl = document.getElementById('stats')
  if (!statsEl || !chart || !chart.w) return
  var drawn = (chart.w.globals.series[0] || []).length
  statsEl.innerHTML =
    'Raw points: <b>' +
    TOTAL_POINTS.toLocaleString() +
    '</b> &nbsp;·&nbsp; Currently drawn: <b>' +
    drawn.toLocaleString() +
    '</b> &nbsp;·&nbsp; Window: <b>' +
    lastZoomLabel +
    '</b> &nbsp;·&nbsp; Render: <b>' +
    lastRenderMs +
    ' ms</b>'
}

const ApexChart = () => {
  const [state, setState] = React.useState({
    series: [{ name: 'Sensor reading', data: seriesData }],
    options: {
      chart: {
        height: 460,
        type: 'line',
        animations: { enabled: false },
        zoom: { enabled: true, type: 'x', autoScaleYaxis: true },
        toolbar: { autoSelected: 'zoom' },
        dataReducer: {
          enabled: true,
          algorithm: 'lttb',
          targetPoints: 500,
          threshold: 1000,
        },
        events: {
          zoomed: function (chartCtx, args) {
            var xaxis = args && args.xaxis
            lastZoomLabel =
              xaxis && xaxis.min && xaxis.max
                ? fmt(xaxis.min) + ' → ' + fmt(xaxis.max)
                : 'full range'
            requestAnimationFrame(function () {
              updateStats(chartCtx)
            })
          },
          beforeResetZoom: function () {
            lastZoomLabel = 'full range'
          },
        },
      },
      stroke: { curve: 'straight', width: 1 },
      dataLabels: { enabled: false },
      markers: { size: 0 },
      title: {
        text: 'Line chart — 500,000 raw datapoints',
        align: 'left',
      },
      subtitle: {
        text: 'LTTB downsampling enabled — zoom in to re-sample the raw series at higher resolution within the visible window',
        align: 'left',
      },
      xaxis: {
        type: 'datetime',
        labels: { datetimeUTC: false },
      },
      yaxis: {
        labels: {
          formatter: function (val) {
            return val.toFixed(2)
          },
        },
      },
      tooltip: { x: { format: 'yyyy-MM-dd HH:mm' } },
    },
  })

  React.useEffect(() => {
    // This demo destroys and recreates the chart imperatively on every re-render,
    // so it skips the react-apexcharts wrapper and creates the instance in #chart
    // directly, exactly like the vanilla build. TOTAL_POINTS, seriesData, fmt,
    // updateStats and the lastRenderMs / lastZoomLabel state live in the shared
    // head script.
    let chart = null
    let disposed = false

    // The vanilla build gets a mutable `options` global from the template; rebuild
    // it here from the parsed chart state.
    const options = Object.assign({ series: state.series }, state.options)
    options.chart = Object.assign({}, state.options.chart)
    options.chart.dataReducer = Object.assign(
      {},
      state.options.chart.dataReducer,
    )

    function rebuild() {
      const enabled = document.getElementById('toggleReducer').checked
      const targetPoints = parseInt(
        document.getElementById('targetPoints').value,
        10,
      )

      options.chart.dataReducer.enabled = enabled
      options.chart.dataReducer.targetPoints = targetPoints
      options.subtitle.text = enabled
        ? 'LTTB downsampling enabled — drawing ~' +
          targetPoints.toLocaleString() +
          ' points'
        : 'Downsampling disabled — drawing every raw point'

      const t2 = performance.now()
      if (chart) chart.destroy()
      chart = new ApexCharts(document.querySelector('#chart'), options)
      chart.render().then(function () {
        if (disposed) return
        lastRenderMs = Math.round(performance.now() - t2)
        updateStats(chart)
      })
    }

    document.getElementById('rerender').addEventListener('click', rebuild)
    document.getElementById('toggleReducer').addEventListener('change', rebuild)
    document.getElementById('resetZoom').addEventListener('click', function () {
      if (chart) chart.resetSeries(true, true)
    })

    // Initial render into #chart, then seed the stats once the first paint settles.
    chart = new ApexCharts(document.querySelector('#chart'), options)
    chart.render().then(function () {
      if (disposed) return
      updateStats(chart)
    })

    return () => {
      disposed = true
      if (chart) chart.destroy()
    }
  }, [])

  return (
    <div>
      <div className="panel">
        <div className="controls">
          <label>
            <input type="checkbox" id="toggleReducer" checked />
            Enable LTTB downsampling
          </label>
          <label>
            Target points:
            <input
              type="number"
              id="targetPoints"
              className="target-input"
              value="500"
              min="50"
              max="5000"
              step="50"
            />
          </label>
          <button id="rerender">Re-render</button>
          <button id="resetZoom">Reset zoom</button>
        </div>

        <div className="stats" id="stats"></div>
      </div>

      <div id="chart"></div>
    </div>
  )
}

export default ApexChart
Large Dataset Downsampling - React Line Charts | ApexCharts.js | ApexCharts.js