// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
fn clamp_sample(value : Sample) -> Sample {
  if value < -1.0 {
    -1.0
  } else if value > 1.0 {
    1.0
  } else {
    value
  }
}

///|
fn[S : Source] source_to_wav_bytes(source : S) -> Bytes {
  let channels = source.channels()
  let sample_rate = source.sample_rate()
  guard channels > 0 else { panic() }
  guard sample_rate > 0 else { panic() }

  let samples : Array[Sample] = []
  while true {
    match source.next() {
      None => break
      Some(v) => samples.push(clamp_sample(v))
    }
  }

  let whole_samples = samples.length() / channels * channels
  let data_size = whole_samples * 4
  let riff_size = 36 + data_size
  let byte_rate = sample_rate * channels * 4
  let block_align = channels * 4

  let buf = Buffer(size_hint=44 + data_size)
  buf.write_bytes(b"RIFF")
  buf.write_uint_le(riff_size.reinterpret_as_uint())
  buf.write_bytes(b"WAVE")
  buf.write_bytes(b"fmt ")
  buf.write_uint_le((16 : UInt))
  buf.write_uint16_le((3 : UInt16))
  buf.write_uint16_le(channels.to_uint16())
  buf.write_uint_le(sample_rate.reinterpret_as_uint())
  buf.write_uint_le(byte_rate.reinterpret_as_uint())
  buf.write_uint16_le(block_align.to_uint16())
  buf.write_uint16_le((32 : UInt16))
  buf.write_bytes(b"data")
  buf.write_uint_le(data_size.reinterpret_as_uint())

  for i in 0.. logger.write_string("ToWavError::OpenFile")
    Creating => logger.write_string("ToWavError::Creating")
    Writing => logger.write_string("ToWavError::Writing")
    Finishing => logger.write_string("ToWavError::Finishing")
    Flushing => logger.write_string("ToWavError::Flushing")
  }
}

///|
pub(open) trait WavWriter {
  fn write_all(Self, bytes : Bytes) -> Unit raise ToWavError
}

///|
pub impl WavWriter for @buffer.Buffer with fn write_all(
  self : @buffer.Buffer,
  bytes : Bytes,
) -> Unit raise ToWavError {
  self.write_bytes(bytes)
}

///|
pub fn[S : Source, W : WavWriter] wav_to_writer(
  source : S,
  writer : W,
) -> Unit raise ToWavError {
  writer.write_all(source_to_wav_bytes(source))
}

///|
pub fn[S : Source] wav_to_file(
  source : S,
  wav_file : StringView,
) -> Unit raise ToWavError {
  let wav_bytes = source_to_wav_bytes(source)
  @fs.write_bytes_to_file(wav_file.to_owned(), wav_bytes) catch {
    _ => raise Writing
  }
}

///|
pub fn[S : Source] output_to_wav(
  source : S,
  wav_file : StringView,
) -> Unit raise ToWavError {
  wav_to_file(source, wav_file)
}