import { useMemo } from 'react';
import { useDataStore } from '../stores/dataStore';
import { useDeviceStore } from '../stores/deviceStore';
import type { MeasurementFrame } from '../protocol/types';

const MAX_DISPLAY_POINTS = 512;

// sampleFreq code → Hz (from SAMPLE_FREQ_TABLE)
const SAMPLE_FREQ_HZ: Record<number, number> = {
  0x00: 250000, 0x01: 125000, 0x02: 62500,  0x03: 31250,
  0x04: 15600,  0x05: 7800,   0x06: 3900,   0x07: 1950,
  0x08: 977,    0x09: 488,    0x0a: 244,    0x0b: 122,    0x0c: 61,
};

function downsample(data: Float64Array, targetLen: number): Float64Array {
  if (data.length <= targetLen) return data;
  const pairLen = Math.floor(targetLen / 2);
  const ratio = data.length / pairLen;
  const out = new Float64Array(pairLen * 2);
  for (let i = 0; i < pairLen; i++) {
    const start = Math.floor(i * ratio);
    const end = Math.min(Math.floor((i + 1) * ratio), data.length);
    let mn = data[start], mx = data[start];
    for (let j = start + 1; j < end; j++) {
      if (data[j] < mn) mn = data[j];
      if (data[j] > mx) mx = data[j];
    }
    out[i * 2] = mn;
    out[i * 2 + 1] = mx;
  }
  return out;
}

export interface WaveformData {
  channels:     Float64Array[];   // downsampled μV per channel
  timeMs:       Float64Array;     // time axis in ms (physical)
  totalTimeMs:  number;           // total window duration in ms
  minLogUV:     number;           // log10(min |value|) for log scale
  maxLogUV:     number;           // log10(max |value|) for log scale
  minUV:        number;           // actual min value (signed) for linear scale
  maxUV:        number;           // actual max value (signed) for linear scale
  sampleDepth:  number;
}

export function computeWaveformData(
  frame: MeasurementFrame,
  visibleChannels: boolean[],
  sampleFreqCode: number,
): WaveformData | null {
  const { adcUV } = frame;
  const sampleDepth = adcUV[0]?.length ?? 0;
  if (sampleDepth === 0) return null;

  const sampleHz   = SAMPLE_FREQ_HZ[sampleFreqCode] ?? 31250;
  const displayLen = Math.min(sampleDepth, MAX_DISPLAY_POINTS);

  // Physical time axis
  const timeMs = new Float64Array(displayLen);
  for (let i = 0; i < displayLen; i++) {
    timeMs[i] = (i / sampleHz) * 1000;
  }
  const totalTimeMs = ((displayLen - 1) / sampleHz) * 1000;

  let logMin =  Infinity;
  let logMax = -Infinity;
  let linMin =  Infinity;
  let linMax = -Infinity;

  const channels: Float64Array[] = adcUV.map((ch, idx) => {
    if (!visibleChannels[idx]) return new Float64Array(displayLen);
    const ds = downsample(ch, displayLen);
    for (let i = 0; i < ds.length; i++) {
      const v    = ds[i];
      const absV = Math.abs(v);
      if (absV > 1e-9) {
        logMin = Math.min(logMin, absV);
        logMax = Math.max(logMax, absV);
      }
      linMin = Math.min(linMin, v);
      linMax = Math.max(linMax, v);
    }
    return ds;
  });

  if (!isFinite(logMin)) logMin = 1e-3;
  if (!isFinite(logMax)) logMax = 1e6;
  if (!isFinite(linMin)) linMin = -1;
  if (!isFinite(linMax)) linMax =  1;

  // Auto-range: pad the actual data range by 10% for visual breathing room
  const linRange = linMax - linMin;
  if (linRange < 1e-9) {
    linMin -= 1;
    linMax += 1;
  } else {
    const pad = linRange * 0.1;
    linMin -= pad;
    linMax += pad;
  }

  return {
    channels, timeMs, totalTimeMs,
    minLogUV: Math.log10(logMin), maxLogUV: Math.log10(logMax),
    minUV: linMin, maxUV: linMax,
    sampleDepth,
  };
}

export function useWaveform(visibleChannels: boolean[]): WaveformData | null {
  const frame          = useDataStore((s) => s.currentFrame);
  const sampleFreqCode = useDeviceStore((s) => s.config.sampleFreq);

  return useMemo(() => {
    if (!frame) return null;
    return computeWaveformData(frame, visibleChannels, sampleFreqCode);
  }, [frame, visibleChannels, sampleFreqCode]);
}
