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.
//
// Both series carry RAW OBSERVATIONS, one number per trip. The chart bins them,
// and every series is binned against the SAME edges, derived from their combined
// extent. That is what makes two distributions comparable: bin each to its own
// range and identical bars would sit at different values.
//
// 900 door-to-door commute times per mode, from a seeded generator so the page
// is deterministic.
var COMMUTES = (function () {
  var seed = 20260813
  function rand() {
    seed = (seed * 16807) % 2147483647
    return (seed - 1) / 2147483646
  }
  // Box-Muller into a log-normal: journey times are right-skewed, since a trip
  // can go badly wrong but cannot finish in less than no time.
  function trips(n, mu, sigma) {
    var out = []
    for (var i = 0; i < n; i++) {
      var u1 = Math.max(rand(), 1e-9)
      var u2 = rand()
      var z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2)
      out.push(Math.round(Math.exp(mu + z * sigma)))
    }
    return out
  }
  return {
    // Driving is quicker on a typical day and far less predictable: a lower
    // centre, a much heavier tail.
    car: trips(900, 3.25, 0.5),
    transit: trips(900, 3.45, 0.2),
  }
})()

function median(values) {
  var sorted = values.slice().sort(function (a, b) {
    return a - b
  })
  var mid = Math.floor(sorted.length / 2)
  return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2
}

// The bad-day figure: the trip you should actually plan around.
function worstTwentieth(values) {
  var sorted = values.slice().sort(function (a, b) {
    return a - b
  })
  return sorted[Math.floor((sorted.length - 1) * 0.95)]
}

function renderStats() {
  var el = document.querySelector('#stats')
  if (!el) return
  el.innerHTML =
    'Car: typical <b>' +
    median(COMMUTES.car) +
    ' min</b>, ' +
    'bad day <b>' +
    worstTwentieth(COMMUTES.car) +
    ' min</b> &middot; ' +
    'Transit: typical <b>' +
    median(COMMUTES.transit) +
    ' min</b>, ' +
    'bad day <b>' +
    worstTwentieth(COMMUTES.transit) +
    ' min</b>'
}

// Wires the arrangement toggle to a live chart. Shared by all three builds.
function wireComparisonControls(chart) {
  var buttons = [].slice.call(document.querySelectorAll('[data-overlap]'))

  buttons.forEach(function (b) {
    b.addEventListener('click', function () {
      var overlap = b.getAttribute('data-overlap') === 'true'
      buttons.forEach(function (other) {
        other.className = other === b ? 'on' : ''
      })
      chart.updateOptions({
        plotOptions: { histogram: { overlap: overlap } },
        // The defaults that come with an overlay are ordinary defaults, so a
        // runtime switch has to carry them itself.
        fill: { opacity: overlap ? 0.65 : 0.85 },
        stroke: overlap
          ? { show: false }
          : { show: true, width: 1, colors: ['#fff'] },
      })
    })
  })

  renderStats()
}

const ApexChart = () => {
  const [state, setState] = React.useState({
    series: [
      {
        name: 'Car',
        data: COMMUTES.car,
      },
      {
        name: 'Transit',
        data: COMMUTES.transit,
      },
    ],
    options: {
      chart: {
        id: 'commutes',
        type: 'histogram',
        // Fixed width, not responsive: thin bars make every bar edge a hairline, so a
        // page-width nudge of a pixel or two visibly moves the whole distribution.
        width: 700,
        height: 400,
        toolbar: {
          show: false,
        },
      },
      plotOptions: {
        histogram: {
          bins: 'auto',
          // The default with more than one series. Every distribution is drawn across
          // the full bin so they lie on top of one another; set false for side-by-side
          // bars. All series share one set of bin edges either way.
          overlap: true,
        },
      },
      colors: ['#f2a43a', '#5d6d9e'],
      xaxis: {
        title: {
          text: 'Door-to-door time (minutes)',
        },
        labels: {
          formatter: function (val) {
            return Math.round(val)
          },
        },
      },
      yaxis: {
        title: {
          text: 'Trips',
        },
      },
      legend: {
        position: 'top',
        horizontalAlign: 'right',
      },
    },
  })

  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('commutes')
      if (!chart) return
      window.clearInterval(timer)
      wireComparisonControls(chart)
    }, 50)

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

  return (
    <div>
      <div className="wrap">
        <h1>Which commute would you rather have?</h1>
        <p>
          Two samples, 900 trips each, drawn on one set of bins. Driving has the
          lower typical time, so on the averages it wins and the question looks
          settled. The shapes say otherwise: the car's distribution is wide with
          a tail that runs off to the right, while the train's is narrow and
          stops. You are choosing between a faster average and a bad day you can
          plan around. Overlaying the two is what makes that legible; side by
          side, you end up comparing bar heights instead of shapes.
        </p>

        <div className="actions">
          <button data-overlap="true" className="on">
            Overlaid
          </button>
          <button data-overlap="false">Side by side</button>
        </div>

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

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

export default ApexChart
Comparing Distributions - React Histogram Charts | ApexCharts.js | ApexCharts.js