// 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.

///|
pub struct Chirp {
  sample_rate : SampleRate
  start_frequency : Double
  end_frequency : Double
  total_samples : Int
  elapsed_samples : Ref[Int]
  phase : Ref[Double]
}

///|
fn chirp_duration_to_samples(
  sample_rate : SampleRate,
  duration : @moon_cpal.Duration,
) -> Int {
  let secs_part = duration.secs.to_int() * sample_rate
  let nanos_part = duration.nanos * sample_rate / 1_000_000_000
  secs_part + nanos_part
}

///|
pub fn chirp(
  sample_rate : SampleRate,
  start_frequency : Double,
  end_frequency : Double,
  duration : @moon_cpal.Duration,
) -> Chirp {
  guard sample_rate > 0 else { panic() }
  guard start_frequency >= 0.0 else { panic() }
  guard end_frequency >= 0.0 else { panic() }

  {
    sample_rate,
    start_frequency,
    end_frequency,
    total_samples: chirp_duration_to_samples(sample_rate, duration),
    elapsed_samples: @ref.new(0),
    phase: @ref.new(0.0),
  }
}

///|
pub fn Chirp::next(self : Chirp) -> Sample? {
  if self.total_samples <= 0 || self.elapsed_samples.val >= self.total_samples {
    return None
  }

  let ratio = Double::from_int(self.elapsed_samples.val) /
    Double::from_int(self.total_samples)
  let freq = self.start_frequency * (1.0 - ratio) + self.end_frequency * ratio
  let out = @math.sin(2.0 * @math.PI * self.phase.val)
  self.phase.val += freq / Double::from_int(self.sample_rate)
  self.phase.val = wrap_phase(self.phase.val)
  self.elapsed_samples.val += 1
  Some(out)
}

///|
pub fn Chirp::channels(_self : Chirp) -> ChannelCount {
  1
}

///|
pub fn Chirp::sample_rate(self : Chirp) -> SampleRate {
  self.sample_rate
}

///|
pub impl Source for Chirp with next(self : Chirp) {
  self.next()
}

///|
pub impl Source for Chirp with channels(self : Chirp) {
  self.channels()
}

///|
pub impl Source for Chirp with sample_rate(self : Chirp) {
  self.sample_rate()
}

///|
pub impl Source for Chirp with current_span_len(self : Chirp) {
  let remaining = self.total_samples - self.elapsed_samples.val
  if remaining <= 0 {
    Some(0)
  } else {
    Some(remaining)
  }
}

///|
pub impl Source for Chirp with total_duration(self : Chirp) {
  duration_from_sample_count(
    self.total_samples,
    self.channels(),
    self.sample_rate(),
  )
}

///|
pub impl Source for Chirp with try_seek(self : Chirp, pos : @moon_cpal.Duration) -> Unit raise SeekError {
  let target = sample_index_from_duration(
    pos,
    self.channels(),
    self.sample_rate(),
  )
  let clamped = if target < 0 {
    0
  } else if target > self.total_samples {
    self.total_samples
  } else {
    target
  }
  self.elapsed_samples.val = clamped
}

///|
pub struct FromFactoryIter[S] {
  factory : () -> S?
  current : Ref[DynSource?]
  channels : Ref[ChannelCount]
  sample_rate : Ref[SampleRate]
}

///|
pub fn[S : Source] from_factory(factory : () -> S?) -> FromFactoryIter[S] {
  let initial = factory()
  match initial {
    None =>
      {
        factory,
        current: @ref.new(None),
        channels: @ref.new(1),
        sample_rate: @ref.new(hz_44100),
      }
    Some(source) =>
      {
        factory,
        current: @ref.new(Some(to_dyn(source))),
        channels: @ref.new(source.channels()),
        sample_rate: @ref.new(source.sample_rate()),
      }
  }
}

///|
pub fn[S : Source] FromFactoryIter::next(self : FromFactoryIter[S]) -> Sample? {
  while true {
    match self.current.val {
      None => {
        let source = (self.factory)()
        match source {
          None => return None
          Some(next_source) => {
            self.channels.val = next_source.channels()
            self.sample_rate.val = next_source.sample_rate()
            self.current.val = Some(to_dyn(next_source))
          }
        }
      }
      Some(current) =>
        match current.next() {
          None => self.current.val = None
          Some(v) => return Some(v)
        }
    }
  }

  None
}

///|
pub fn[S] FromFactoryIter::channels(self : FromFactoryIter[S]) -> ChannelCount {
  self.channels.val
}

///|
pub fn[S] FromFactoryIter::sample_rate(self : FromFactoryIter[S]) -> SampleRate {
  self.sample_rate.val
}

///|
pub impl[S : Source] Source for FromFactoryIter[S] with next(
  self : FromFactoryIter[S],
) {
  self.next()
}

///|
pub impl[S : Source] Source for FromFactoryIter[S] with channels(
  self : FromFactoryIter[S],
) {
  self.channels()
}

///|
pub impl[S : Source] Source for FromFactoryIter[S] with sample_rate(
  self : FromFactoryIter[S],
) {
  self.sample_rate()
}

///|
pub impl[S : Source] Source for FromFactoryIter[S] with current_span_len(
  self : FromFactoryIter[S],
) {
  if self.current.val is Some(current) {
    match current.current_span_len() {
      Some(v) => if v != 0 { return Some(v) }
      None => ()
    }
  }
  Some(10_240)
}

///|
pub impl[S : Source] Source for FromFactoryIter[S] with total_duration(
  _self : FromFactoryIter[S],
) {
  source_default_total_duration()
}

///|
pub impl[S : Source] Source for FromFactoryIter[S] with try_seek(
  self : FromFactoryIter[S],
  pos : @moon_cpal.Duration,
) -> Unit raise SeekError {
  match self.current.val {
    None => ()
    Some(current) => current.try_seek(pos)
  }
}