use serde::{Serialize, Deserialize};

/// Audio clip instance ID type
pub type AudioClipInstanceId = u32;

/// Type alias for backwards compatibility
pub type ClipId = AudioClipInstanceId;

/// Audio clip instance that references content in the AudioClipPool
///
/// This represents a placed instance of audio content on the timeline.
/// The actual audio data is stored in the AudioClipPool and referenced by `audio_pool_index`.
///
/// ## Timing Model
/// - `internal_start` / `internal_end`: Define the region of the source audio to play (trimming)
/// - `external_start` / `external_duration`: Define where the clip appears on the timeline and how long
///
/// ## Looping
/// If `external_duration` is greater than `internal_end - internal_start`,
/// the clip will seamlessly loop back to `internal_start` when it reaches `internal_end`.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AudioClipInstance {
    pub id: AudioClipInstanceId,
    pub audio_pool_index: usize,

    /// Start position within the audio content (seconds)
    pub internal_start: f64,
    /// End position within the audio content (seconds)
    pub internal_end: f64,

    /// Start position on the timeline (seconds)
    pub external_start: f64,
    /// Duration on the timeline (seconds) - can be longer than internal duration for looping
    pub external_duration: f64,

    /// Clip-level gain
    pub gain: f32,
}

/// Type alias for backwards compatibility
pub type Clip = AudioClipInstance;

impl AudioClipInstance {
    /// Create a new audio clip instance
    pub fn new(
        id: AudioClipInstanceId,
        audio_pool_index: usize,
        internal_start: f64,
        internal_end: f64,
        external_start: f64,
        external_duration: f64,
    ) -> Self {
        Self {
            id,
            audio_pool_index,
            internal_start,
            internal_end,
            external_start,
            external_duration,
            gain: 1.0,
        }
    }

    /// Create a clip instance from legacy parameters (for backwards compatibility)
    /// Maps old start_time/duration/offset to new timing model
    pub fn from_legacy(
        id: AudioClipInstanceId,
        audio_pool_index: usize,
        start_time: f64,
        duration: f64,
        offset: f64,
    ) -> Self {
        Self {
            id,
            audio_pool_index,
            internal_start: offset,
            internal_end: offset + duration,
            external_start: start_time,
            external_duration: duration,
            gain: 1.0,
        }
    }

    /// Check if this clip instance is active at a given timeline position
    pub fn is_active_at(&self, time_seconds: f64) -> bool {
        time_seconds >= self.external_start && time_seconds < self.external_end()
    }

    /// Get the end time of this clip instance on the timeline
    pub fn external_end(&self) -> f64 {
        self.external_start + self.external_duration
    }

    /// Get the end time of this clip instance on the timeline
    /// (Alias for external_end(), for backwards compatibility)
    pub fn end_time(&self) -> f64 {
        self.external_end()
    }

    /// Get the start time on the timeline
    /// (Alias for external_start, for backwards compatibility)
    pub fn start_time(&self) -> f64 {
        self.external_start
    }

    /// Get the internal (content) duration
    pub fn internal_duration(&self) -> f64 {
        self.internal_end - self.internal_start
    }

    /// Check if this clip instance loops
    pub fn is_looping(&self) -> bool {
        self.external_duration > self.internal_duration()
    }

    /// Get the position within the audio content for a given timeline position
    /// Returns None if the timeline position is outside this clip instance
    /// Handles looping automatically
    pub fn get_content_position(&self, timeline_pos: f64) -> Option<f64> {
        if timeline_pos < self.external_start || timeline_pos >= self.external_end() {
            return None;
        }

        let relative_pos = timeline_pos - self.external_start;
        let internal_duration = self.internal_duration();

        if internal_duration <= 0.0 {
            return None;
        }

        // Wrap around for looping
        let content_offset = relative_pos % internal_duration;
        Some(self.internal_start + content_offset)
    }

    /// Set clip gain
    pub fn set_gain(&mut self, gain: f32) {
        self.gain = gain.max(0.0);
    }
}
