import React from 'react';

const trackers = {};
let tick = null;
let fps = 0;
let counter = 0;
function startLoop() {
  if (tick) return;
  const frames = [performance.now(), 0];
  tick = () => {
    // @ts-expect-error
    Object.values(trackers).forEach(tracker => tracker.update());

    // Compute fps over last 60 frames
    counter++;
    const fpsN = 60;
    frames.unshift(performance.now());
    if (frames.length > fpsN) frames.pop();
    const l = frames.length;
    if (counter % 30 === 0) {
      fps = Math.round((l * 1000) / (frames[0] - frames[l - 1]));
    }

    requestAnimationFrame(tick);
  };
  tick();
}

export const createTracker = label => {
  if (!trackers[label]) {
    const N = 10;
    let value = 0;
    let valueAtFrameStart = value;
    const stats = [];
    const result: {countPerFrame?: number, maxPerFrame?: number, avPerFrame?: number} = {};
    const update = () => {
      result.countPerFrame = value - valueAtFrameStart;
      valueAtFrameStart = value;
      stats.unshift(result.countPerFrame);
      if (stats.length > N) stats.pop();
      result.maxPerFrame = Math.max(...stats);
      result.avPerFrame = stats.reduce((a, b) => a + b, 0) / N;
    };
    const render = () => {
      if (!tick) startLoop();
      return (
        <div key={label}>
          <span>{`${label} per frame: ${result.countPerFrame}, max: ${result.maxPerFrame}, av: ${result.avPerFrame}`}</span>
        </div>
      );
    };
    const increment = () => {
      value++;
    };
    const tracker = {update, render, increment};
    trackers[label] = tracker;
  }
  return trackers[label];
};

const patchUpdateState = (tracker, LayerClass) => {
  const _oldUpdateState = LayerClass.prototype.updateState;
  LayerClass.prototype.updateState = function(params) {
    const {
      extensionsChanged,
      propsOrDataChanged,
      stateChanged,
      updateTriggersChanged
    } = params.changeFlags;
    if (extensionsChanged || propsOrDataChanged || stateChanged || updateTriggersChanged) {
      tracker.increment();
    }
    _oldUpdateState.call(this, params);
  };
};

export const createLayerUpdateTracker = (label, layers) => {
  const tracker = createTracker('state updates');
  layers.forEach(layer => patchUpdateState(tracker, layer));
  return tracker;
};

export const renderPerformanceStats = () => {
  if (!tick) startLoop();
  return (
    <div
      id="performance-metrics-overlay"
      style={{
        bottom: 0,
        right: 0,
        position: 'absolute',
        fontSize: 14,
        background: 'whitesmoke',
        padding: 8,
        zIndex: 10,
        border: '1px solid red',
        width: 400
      }}
    >
      {/* @ts-expect-error */}
      {Object.values(trackers).map(tracker => tracker.render())}
      <span>{`FPS: ${fps}`}</span>
    </div>
  );
};
