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

// Shared by the vanilla-js, React and Vue builds.
//
// Each device gets its RAW measurements: one number per page load. No
// quartiles, no median, nothing precomputed. `points` is the sample, and the
// library derives the five-number summary from it.
var DEVICES = [
  { name: 'Desktop', center: 1.4, spread: 0.3, slowTail: 1.4 },
  { name: 'Laptop', center: 1.9, spread: 0.38, slowTail: 1.8 },
  { name: 'Tablet', center: 2.8, spread: 0.55, slowTail: 2.4 },
  { name: 'Phone (5G)', center: 3.2, spread: 0.62, slowTail: 3.0 },
  { name: 'Phone (3G)', center: 6.1, spread: 1.3, slowTail: 4.5 },
]

function makeSample(cfg, index) {
  var seed = 8191 * (index + 3)
  function rand() {
    seed = (seed * 16807) % 2147483647
    return (seed - 1) / 2147483646
  }
  var out = []
  for (var i = 0; i < 140; i++) {
    // Box-Muller, then a few genuinely slow loads: the tail is the point of
    // the chart, and it is what the two whisker rules disagree about.
    var u1 = Math.max(rand(), 1e-9)
    var z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * rand())
    var v = cfg.center + z * cfg.spread
    if (i % 34 === 0) v = cfg.center + cfg.slowTail * (0.6 + rand())
    out.push(Math.round(Math.max(0.2, v) * 100) / 100)
  }
  return out
}

var RAW = DEVICES.map(function (cfg, i) {
  return { x: cfg.name, points: makeSample(cfg, i) }
})

function wireWhiskerControls(chart) {
  var buttons = [].slice.call(document.querySelectorAll('[data-whiskers]'))
  var note = document.querySelector('#note')

  function apply(mode) {
    buttons.forEach(function (b) {
      b.className = b.getAttribute('data-whiskers') === mode ? 'on' : ''
    })
    chart.updateOptions({
      plotOptions: { boxPlot: { whiskers: mode } },
    })
    if (note) {
      note.textContent =
        mode === 'tukey'
          ? 'Whiskers stop at 1.5 x IQR. The dots past the whisker are the outliers.'
          : 'Whiskers reach the slowest and fastest load. Nothing is hidden.'
    }
  }

  buttons.forEach(function (b) {
    b.addEventListener('click', function () {
      apply(b.getAttribute('data-whiskers'))
    })
  })
  apply('minmax')
}

const ApexChart = () => {
  const [state, setState] = React.useState({
    series: [
      {
        name: 'Load time',
        type: 'boxPlot',
        data: RAW,
      },
    ],
    options: {
      chart: {
        id: 'loadTimes',
        type: 'boxPlot',
        // Fixed width so the layout cannot shift with the page.
        width: 700,
        height: 440,
        toolbar: {
          show: false,
        },
      },
      plotOptions: {
        boxPlot: {
          colors: {
            upper: '#5A87FF',
            lower: '#B7BEFF',
          },
          // Every observation, drawn over its own box. With the 1.5 x IQR rule the
          // dots beyond the whisker are exactly the outliers that rule excludes.
          points: {
            show: true,
            size: 2.5,
            jitter: 0.45,
            opacity: 0.55,
          },
        },
      },
      legend: {
        show: false,
      },
      yaxis: {
        title: {
          text: 'Page load (seconds)',
        },
        min: 0,
      },
      xaxis: {
        title: {
          text: 'Device',
        },
      },
      tooltip: {
        shared: false,
        intersect: true,
      },
    },
  })

  React.useEffect(() => {
    // The react-apexcharts wrapper owns the render, so reach the live instance
    // by its chart.id before wiring the controls.
    const timer = window.setInterval(() => {
      const chart = ApexCharts.getChartByID('loadTimes')
      if (!chart) return
      window.clearInterval(timer)
      wireWhiskerControls(chart)
    }, 50)

    return () => window.clearInterval(timer)
  }, [])

  return (
    <div>
      <div className="wrap">
        <h1>Hand it the measurements, not the quartiles</h1>
        <p>
          Every box below is built from 140 raw page-load timings. The series
          carries those measurements and nothing else: the median, the quartiles
          and the whiskers are computed for you. Switch the whisker rule to see
          the choice it involves, because a distribution with a slow tail looks
          like a different story depending on where the whisker stops.
        </p>

        <div className="actions">
          <button data-whiskers="minmax" className="on">
            Whiskers: min to max
          </button>
          <button data-whiskers="tukey">Whiskers: 1.5 x IQR</button>
        </div>

        <div className="chart-wrap">
          <div id="chart">
            <ReactApexChart
              options={state.options}
              series={state.series}
              type="boxPlot"
              height={440}
              width={700}
            />
          </div>
        </div>

        <div className="note" id="note"></div>
      </div>
    </div>
  )
}

export default ApexChart
BoxPlot from Raw Observations - React BoxPlot Charts | ApexCharts.js | ApexCharts.js