local createLibTest = require('/apis/libtest');
local createAudio = require('/apis/libaudio');

local testlib = createLibTest({ ... });

local function constant(value, n)
  local buf = {};
  for i = 1, n do buf[i] = value; end
  return buf;
end

local function impulse(value, n)
  local buf = constant(0, n);
  buf[1] = value;
  return buf;
end

local function allInRange(buf)
  for i = 1, #buf do
    if buf[i] < -128 or buf[i] > 127 or buf[i] ~= math.floor(buf[i]) then
      return false;
    end
  end
  return true;
end

testlib.test('low-pass passes DC and stays in range, rising toward the input', function()
  local audio = createAudio();
  local lp = audio.lowPass();
  local out = lp.process(constant(127, 2000));
  testlib.assertTrue(allInRange(out), 'samples must be valid 8-bit integers');
  testlib.assertTrue(out[#out] > out[1], 'output should rise toward the DC level');
  testlib.assertTrue(out[#out] <= 127, 'output must not exceed the DC level');
end);

testlib.test('high-pass blocks DC: a constant decays toward zero', function()
  local audio = createAudio();
  local hp = audio.highPass();
  local out = hp.process(constant(100, 200));
  testlib.assertTrue(allInRange(out), 'samples must be valid 8-bit integers');
  testlib.assertTrue(math.abs(out[#out]) < math.abs(out[1]), 'DC component should decay');
  testlib.assertTrue(math.abs(out[#out]) <= 1, 'DC should fall close to zero');
end);

testlib.test('set changes the response and reset clears state', function()
  local audio = createAudio();
  local lp = audio.lowPass();

  lp.set(200);
  local low = lp.process(constant(127, 1))[1];
  lp.reset();
  lp.set(5000);
  local high = lp.process(constant(127, 1))[1];
  testlib.assertTrue(low ~= high, 'different cutoffs should give a different first sample');

  -- reset must make a repeated run identical
  lp.reset();
  local a = lp.process(constant(50, 16));
  lp.reset();
  local b = lp.process(constant(50, 16));
  testlib.assertEquals(a[#a], b[#b]);
end);

testlib.test('set clamps the param to the spec range', function()
  local audio = createAudio();
  local rv = audio.reverb();
  rv.set(10);
  testlib.assertEquals(rv.get(), 0.9);
  rv.set(-5);
  testlib.assertEquals(rv.get(), 0);
end);

testlib.test('delay re-emits the dry signal after the ring length', function()
  local audio = createAudio({ sampleRate = 8 });
  local d = audio.delay(1000); -- 1000ms * 8/s = 8-sample ring
  local out = d.process(impulse(100, 10));

  testlib.assertTrue(allInRange(out), 'samples must be valid 8-bit integers');
  testlib.assertEquals(out[1], 60); -- 100 * 0.6 dry
  testlib.assertEquals(out[9], 40); -- 100 * 0.4 delayed by 8 samples
  testlib.assertEquals(out[5], 0);  -- nothing in between
end);

testlib.test('reverb stays bounded for a loud impulse', function()
  local audio = createAudio();
  local rv = audio.reverb(0.8);
  local buf = constant(0, 4000);
  for i = 1, 200 do buf[i] = 127; end
  local out = rv.process(buf);
  testlib.assertTrue(allInRange(out), 'reverb output must never clip past 8-bit range');
end);

testlib.run();
