//! Typification of the various AudioFormat codes and flags offered by the Core Audio API.
//!
//! See the Core Audio Data Types Reference
//! [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/Audio_Data_Format_Identifiers) for more info.

use std::os::raw::c_uint;

/// A type-safe representation of both the `AudioFormatId` and their associated flags.
#[derive(Copy, Clone, Debug)]
#[allow(non_camel_case_types)]
pub enum AudioFormat {
    /// Linear PCM; a non-compressed audio data format with one frame per packet.
    ///
    /// **Available** in OS X v10.0 and later.
    LinearPCM(LinearPcmFlags), // = 1819304813,
    /// An AC-3 codec.
    ///
    /// **Available** in OS X v10.2 and later.
    AC3, // = 1633889587,
    /// AC-3 codec that provides data packaged for transport over an IEC 60958 compliant digital
    /// audio interface.
    ///
    /// **Available** in OS X v10.2 and later.
    F60958AC3(StandardFlags), // = 1667326771,
    /// Apple's implementation of the IMA 4:1 ADPCM codec.
    ///
    /// **Available** in OS X v10.2 and later.
    AppleIMA4, // = 1768775988,
    /// MPEG-4 AAC codec.
    ///
    /// **Available** in OS X v10.2 and later.
    MPEG4AAC(Mpeg4ObjectId), // = 1633772320,
    /// MPEG-4 CELP codec.
    ///
    /// **Available** in OS X v10.2 and later.
    MPEG4CELP(Mpeg4ObjectId), // = 1667591280,
    /// MPEG-4 HVXC codec.
    ///
    /// **Available** in OS X v10.2 and later.
    MPEG4HVXC(Mpeg4ObjectId), // = 1752594531,
    /// MPEG-4 TwinVQ codec.
    ///
    /// **Available** in OS X v10.2 and later.
    MPEG4TwinVQ(Mpeg4ObjectId), // = 1953986161,
    /// MACE 3:1.
    ///
    /// **Available** in OS X v10.3 and later.
    MACE3, // = 1296122675,
    /// MACE 6:1.
    ///
    /// **Available** in OS X v10.3 and later.
    MACE6, // = 1296122678,
    /// μLaw 2:1.
    ///
    /// **Available** in OS X v10.3 and later.
    ULaw, // = 1970037111,
    /// aLaw 2:1.
    ///
    /// **Available** in OS X v10.3 and later.
    ALaw, // = 1634492791,
    /// QDesign Music.
    ///
    /// **Available** in OS X v10.3 and later.
    QDesign, // = 1363430723,
    /// QDesign2 Music.
    ///
    /// **Available** in OS X v10.3 and later.
    QDesign2, // = 1363430706,
    /// QUALCOMM PureVoice.
    ///
    /// **Available** in OS X v10.3 and later.
    QUALCOMM, // = 1365470320,
    /// MPEG-1/2, Layer 1 audio.
    ///
    /// **Available** in OS X v10.3 and later.
    MPEGLayer1, // = 778924081,
    /// MPEG-1/2, Layer 2 audio.
    ///
    /// **Available** in OS X v10.3 and later.
    MPEGLayer2, // = 778924082,
    /// MPEG-1/2, Layer 3 audio.
    ///
    /// **Available** in OS X v10.3 and later.
    MPEGLayer3, // = 778924083,
    /// A stream of IOAudioTimeStamp structures.
    ///
    /// **Available** in OS X v10.2 and later.
    TimeCode(AudioTimeStampFlags), // = 1953066341,
    /// A stream of MIDIPacketList structures where the time stamps in the MIDIPacket structures
    /// are sample offsets in the stream. The `sample_rate` field in the **StreamFormat** structure
    /// is used to describe how time is passed in this kind of stream.
    ///
    /// An audio unit that receives or generates this stream can use this sample rate together with
    /// the number of frames it is rendering.
    ///
    /// The sample offsets within the MIDIPacketList can be used to define the time for any MIDI
    /// event within the list.
    ///
    /// **Available** in OS X v10.2 and later.
    ///
    /// TODO: Review whether or not this audio format should indicate some fundamental change
    /// within the **StreamFormat**.
    MIDIStream, // = 1835623529,
    /// A "side-chain" of f32 data that can be fed or generated by an audio unit and that is used
    /// to send a high density of parameter value control information.
    ///
    /// An audio unit typically runs a parameter value stream at either the sample rate of the
    /// audio unit's audio data, or some integer quotient of this (i.e. a half or a third of the
    /// sample rate of the audio).
    ///
    /// The `sample_rate` field in the **StreamFormat** type describes this relationship.
    ///
    /// **Available** in OS X v10.2 and later.
    ParameterValueStream, // = 1634760307,
    /// Apple Lossless format.
    ///
    /// **Available** in OS X v10.3 and later.
    AppleLossless(AppleLosslessFlags), // = 1634492771,
    /// MPEG-4 High Efficiency AAC audio object.
    ///
    /// **Available** in OS X v10.5 and later.
    MPEG4AAC_HE, // = 1633772392,
    /// MPEG-4 AAC Low Delay audio object.
    ///
    /// **Available** in OS X v10.5 and later.
    MPEG4AAC_LD, // = 1633772396,
    /// MPEG-4 AAC Enhanced Low Delay audio object.
    ///
    /// **Available** in OS X v10.7 and later.
    MPEG4AAC_ELD, // = 1633772389,
    /// MPEG-4 AAC Enhanced Low Delay audio object with SBR (spectral band replication) extension
    /// layer.
    ///
    /// **Available** in OS X v10.7 and later.
    MPEG4AAC_ELD_SBR, // = 1633772390,
    MPEG4AAC_ELD_V2, // = 1633772391,
    /// MPEG-4 High Efficiency AAC Version 2 audio object.
    ///
    /// **Available** in OS X v10.5 and later.
    MPEG4AAC_HE_V2, // = 1633772400,
    /// MPEG-4 Apatial Audio audio object.
    ///
    /// **Available** in OS X v10.5 and later.
    MPEG4AAC_Spatial, // = 1633772403,
    /// The AMR (adaptive Multi-Rate) narrow band speech codec.
    ///
    /// **Available** in OS X v10.5 and later.
    AMR, // = 1935764850,
    AMR_WB,          // = 1935767394,
    /// The codec used for Audible, Inc. audio books.
    ///
    /// **Available** in OS X v10.6 and later.
    Audible, // = 1096107074,
    /// The iLBC (internet Low Bitrate Codec) narrow band cpeech codec.
    ///
    /// **Available** in OS X v10.6 and later.
    iLBC, // = 1768710755,
    /// DVI/Intel IMA ADPCM - ACM code 17.
    ///
    /// **Available** in OS X v10.6 and later.
    DVIIntelIMA, // = 1836253201,
    /// Microsoft GSM 6.10 - ACM code 49.
    ///
    /// **Available** in OS X v10.6 and later.
    MicrosoftGSM, // = 1836253233,
    /// The format defined by the AES3-2003 standard.
    ///
    /// Adopted into MXF and MPEG-2 containers and SDTI transport streams with SMPTE specs
    /// 203M-2002 and 331M-2000.
    AES3, // = 1634038579,
}

impl AudioFormat {
    /// Convert from the FFI C format and flags to a typesafe Rust enum representation.
    pub fn from_format_and_flag(format: c_uint, flag: Option<u32>) -> Option<AudioFormat> {
        match (format, flag) {
            (1819304813, Some(i)) => Some(AudioFormat::LinearPCM(
                LinearPcmFlags::from_bits_truncate(i),
            )),
            (1633889587, _) => Some(AudioFormat::AC3),
            (1667326771, Some(i)) => {
                Some(AudioFormat::F60958AC3(StandardFlags::from_bits_truncate(i)))
            }
            (1768775988, _) => Some(AudioFormat::AppleIMA4),
            (1633772320, Some(i)) => Some(AudioFormat::MPEG4AAC(
                Mpeg4ObjectId::from_u32(i).expect("Unknown Mpeg4ObjectId"),
            )),
            (1667591280, Some(i)) => Some(AudioFormat::MPEG4CELP(
                Mpeg4ObjectId::from_u32(i).expect("Unknown Mpeg4ObjectId"),
            )),
            (1752594531, Some(i)) => Some(AudioFormat::MPEG4HVXC(
                Mpeg4ObjectId::from_u32(i).expect("Unknown Mpeg4ObjectId"),
            )),
            (1953986161, Some(i)) => Some(AudioFormat::MPEG4TwinVQ(
                Mpeg4ObjectId::from_u32(i).expect("Unknown Mpeg4ObjectId"),
            )),
            (1296122675, _) => Some(AudioFormat::MACE3),
            (1296122678, _) => Some(AudioFormat::MACE6),
            (1970037111, _) => Some(AudioFormat::ULaw),
            (1634492791, _) => Some(AudioFormat::ALaw),
            (1363430723, _) => Some(AudioFormat::QDesign),
            (1363430706, _) => Some(AudioFormat::QDesign2),
            (1365470320, _) => Some(AudioFormat::QUALCOMM),
            (778924081, _) => Some(AudioFormat::MPEGLayer1),
            (778924082, _) => Some(AudioFormat::MPEGLayer2),
            (778924083, _) => Some(AudioFormat::MPEGLayer3),
            (1953066341, Some(i)) => Some(AudioFormat::TimeCode(
                AudioTimeStampFlags::from_bits_truncate(i),
            )),
            (1835623529, _) => Some(AudioFormat::MIDIStream),
            (1634760307, _) => Some(AudioFormat::ParameterValueStream),
            (1634492771, Some(i)) => Some(AudioFormat::AppleLossless(
                AppleLosslessFlags::from_bits_truncate(i),
            )),
            (1633772392, _) => Some(AudioFormat::MPEG4AAC_HE),
            (1633772396, _) => Some(AudioFormat::MPEG4AAC_LD),
            (1633772389, _) => Some(AudioFormat::MPEG4AAC_ELD),
            (1633772390, _) => Some(AudioFormat::MPEG4AAC_ELD_SBR),
            (1633772391, _) => Some(AudioFormat::MPEG4AAC_ELD_V2),
            (1633772400, _) => Some(AudioFormat::MPEG4AAC_HE_V2),
            (1633772403, _) => Some(AudioFormat::MPEG4AAC_Spatial),
            (1935764850, _) => Some(AudioFormat::AMR),
            (1935767394, _) => Some(AudioFormat::AMR_WB),
            (1096107074, _) => Some(AudioFormat::Audible),
            (1768710755, _) => Some(AudioFormat::iLBC),
            (1836253201, _) => Some(AudioFormat::DVIIntelIMA),
            (1836253233, _) => Some(AudioFormat::MicrosoftGSM),
            (1634038579, _) => Some(AudioFormat::AES3),
            _ => None,
        }
    }

    /// Convert from the Rust enum to the C format and flag.
    pub fn as_format_and_flag(&self) -> (c_uint, Option<u32>) {
        match *self {
            AudioFormat::LinearPCM(flag) => (1819304813, Some(flag.bits())),
            AudioFormat::AC3 => (1633889587, None),
            AudioFormat::F60958AC3(flag) => (1667326771, Some(flag.bits())),
            AudioFormat::AppleIMA4 => (1768775988, None),
            AudioFormat::MPEG4AAC(flag) => (1633772320, Some(flag as u32)),
            AudioFormat::MPEG4CELP(flag) => (1667591280, Some(flag as u32)),
            AudioFormat::MPEG4HVXC(flag) => (1752594531, Some(flag as u32)),
            AudioFormat::MPEG4TwinVQ(flag) => (1953986161, Some(flag as u32)),
            AudioFormat::MACE3 => (1296122675, None),
            AudioFormat::MACE6 => (1296122678, None),
            AudioFormat::ULaw => (1970037111, None),
            AudioFormat::ALaw => (1634492791, None),
            AudioFormat::QDesign => (1363430723, None),
            AudioFormat::QDesign2 => (1363430706, None),
            AudioFormat::QUALCOMM => (1365470320, None),
            AudioFormat::MPEGLayer1 => (778924081, None),
            AudioFormat::MPEGLayer2 => (778924082, None),
            AudioFormat::MPEGLayer3 => (778924083, None),
            AudioFormat::TimeCode(flag) => (1953066341, Some(flag.bits())),
            AudioFormat::MIDIStream => (1835623529, None),
            AudioFormat::ParameterValueStream => (1634760307, None),
            AudioFormat::AppleLossless(flag) => (1634492771, Some(flag.bits())),
            AudioFormat::MPEG4AAC_HE => (1633772392, None),
            AudioFormat::MPEG4AAC_LD => (1633772396, None),
            AudioFormat::MPEG4AAC_ELD => (1633772389, None),
            AudioFormat::MPEG4AAC_ELD_SBR => (1633772390, None),
            AudioFormat::MPEG4AAC_ELD_V2 => (1633772391, None),
            AudioFormat::MPEG4AAC_HE_V2 => (1633772400, None),
            AudioFormat::MPEG4AAC_Spatial => (1633772403, None),
            AudioFormat::AMR => (1935764850, None),
            AudioFormat::AMR_WB => (1935767394, None),
            AudioFormat::Audible => (1096107074, None),
            AudioFormat::iLBC => (1768710755, None),
            AudioFormat::DVIIntelIMA => (1836253201, None),
            AudioFormat::MicrosoftGSM => (1836253233, None),
            AudioFormat::AES3 => (1634038579, None),
        }
    }
}

bitflags! {
    /// Standard flags for use in the **F60958AC3** **AudioFormat** variant.
    ///
    /// Note: In the original Core Audio API these are consolidated with what we have named the
    /// **StandardFlags** and **AppleLosslessFlags** types under the `AudioFormatFlag` type. We
    /// have chosen to separate these for greater type safety and clearer compatibility with
    /// the **AudioFormat** type.
    ///
    /// Original documentation [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/AudioStreamBasicDescription_Flags).
    pub struct StandardFlags: u32 {
        /// Set for floating point, clear for integer.
        ///
        /// **Available** in OS X v10.2 and later.
        const IS_FLOAT = 1;
        /// Set for big endian, clear for little endian.
        ///
        /// **Available** in OS X v10.2 and later.
        const IS_BIG_ENDIAN = 2;
        /// Set for signed integer, clear for unsigned integer.
        ///
        /// Note: This is only valid if `IS_FLOAT` is clear.
        ///
        /// **Available** in OS X v10.2 and later.
        const IS_SIGNED_INTEGER = 4;
        /// Set if the sample bits occupy the entire available bits for the channel, clear if they
        /// are high- or low-aligned within the channel.
        ///
        /// **Available** in OS X v10.2 and later.
        const IS_PACKED = 8;
        /// Set if the sample bits are placed into the high bits of the channel, clear for low bit
        /// placement.
        ///
        /// Note: This is only valid if `IS_PACKED` is clear.
        ///
        /// **Available** in OS X v10.2 and later.
        const IS_ALIGNED_HIGH = 16;
        /// Set if the sample for each channel are located contiguously and the channels are laid
        /// out end to end.
        ///
        /// Clear if the samples for each frame are laid out contiguously and the frames laid out
        /// end to end.
        ///
        /// **Available** in OS X v10.2 and later.
        const IS_NON_INTERLEAVED = 32;
        /// Set to indicate when a format is nonmixable.
        ///
        /// Note: that this flag is only used when interacting with the HAL's stream format
        /// information. It is **not** valid for any other use.
        ///
        /// **Available** in OS X v10.3 and later.
        const IS_NON_MIXABLE = 64;
    }
}

bitflags! {
    /// Flags for use within the **LinearPCM** **AudioFormat**.
    ///
    /// Note: In the original Core Audio API these are consolidated with what we have named the
    /// **StandardFlags** and **AppleLosslessFlags** types under the `AudioFormatFlag` type. We
    /// have chosen to separate these for greater type safety and clearer compatibility with
    /// the **AudioFormat** type.
    ///
    /// Original documentation [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/AudioStreamBasicDescription_Flags).
    pub struct LinearPcmFlags: u32 {
        /// Synonmyn for the **IS_FLOAT** **StandardFlags**.
        ///
        /// **Available** in OS X v10.0 and later.
        const IS_FLOAT = 1;
        /// Synonmyn for the **IS_BIG_ENDIAN** **StandardFlags**.
        ///
        /// **Available** in OS X v10.0 and later.
        const IS_BIG_ENDIAN = 2;
        /// Synonmyn for the **IS_SIGNED_INTEGER** **StandardFlags**.
        ///
        /// **Available** in OS X v10.0 and later.
        const IS_SIGNED_INTEGER = 4;
        /// Synonmyn for the **IS_PACKED** **StandardFlags**.
        ///
        /// **Available** in OS X v10.0 and later.
        const IS_PACKED = 8;
        /// Synonmyn for the **IS_ALIGNED_HIGH** **StandardFlags**.
        ///
        /// **Available** in OS X v10.0 and later.
        const IS_ALIGNED_HIGH = 16;
        /// Synonmyn for the **IS_NON_INTERLEAVED** **StandardFlags**.
        ///
        /// **Available** in OS X v10.2 and later.
        const IS_NON_INTERLEAVED = 32;
        /// Synonmyn for the **IS_NON_MIXABLE** **StandardFlags**.
        ///
        /// **Available** in OS X v10.3 and later.
        const IS_NON_MIXABLE = 64;
        /// The linear PCM flags contain a 6-bit bitfield indicating that an integer format is to
        /// be interpreted as fixed point.
        ///
        /// The value indicates the number of bits are used to represent the fractional portion of
        /// each sample value.
        ///
        /// This constant indicates the bit position (counting from the right) of the bitfield in
        /// `mFormatFlags` field.
        ///
        /// TODO: Review whether or not this flag indicates that we need to treat LinearPCM format
        /// uniquely in some way.
        ///
        /// **Available** in OS X v10.6 and later.
        const FLAGS_SAMPLE_FRACTION_SHIFT = 7;
        /// The number of fractional bits.
        ///
        /// `== (<other_flags> & FLAGS_SAMPLE_FRACTION_MASK) >> FLAGS_SAMPLE_FRACTION_SHIFT`
        ///
        /// **Available** in OS X v10.6 and later.
        const FLAGS_SAMPLE_FRACTION_MASK = 8064;
    }
}

bitflags! {
    /// Flags set for Apple Lossless data.
    ///
    /// **Available** in OS X v10.3 and later.
    ///
    /// Note: In the original Core Audio API these are consolidated with what we have named the
    /// **StandardFlags** and **AppleLosslessFlags** types under the `AudioFormatFlag` type. We
    /// have chosen to separate these for greater type safety and clearer compatibility with
    /// the **AudioFormat** type.
    ///
    /// Original documentation [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/AudioStreamBasicDescription_Flags).
    pub struct AppleLosslessFlags: u32 {
        /// Sourced from 16 bit native endian signed integer data.
        const BIT_16_SOURCE_DATA = 1;
        /// Sourced from 20 bit native endian signed integer data aligned high in 24 bits.
        const BIT_20_SOURCE_DATA = 2;
        /// Sourced from 24 bit native endian signed integer data.
        const BIT_24_SOURCE_DATA = 3;
        /// Sourced from 32 bit native endian signed integer data.
        const BIT_32_SOURCE_DATA = 4;
    }
}

/// "Used in the `mFormatFlags` field of an `AudioStreamBasicDescription` structure that
/// describes an MPEG-4 audio stream to specify the type of MPEG-4 audio data.
///
/// **Available** in OS X v10.3 and later.
///
/// **Deprecated** in OS X v10.5.
///
/// Note: This type was originally represented using a bitflag field in the original API, however
/// there is only ever one flag set at a time. Thus, we use an enum as a more accurate,
/// user-friendly, type-safe representation.
///
/// Original documenation
/// [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/MPEG_4_Audio_Object_Type_Constants).
#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(non_camel_case_types)]
pub enum Mpeg4ObjectId {
    /// Advanced audio coding; the baisc MPEG-4 technology.
    AAC_Main = 1,
    /// Lossless coding; provides compression with no loss of quality.
    AAC_LC = 2,
    /// Scalable sampling rate; provides different sampling frequencies for different targets.
    AAC_SSR = 3,
    /// Long term prediction; reduces redundancy in a coded signal.
    AAC_LTP = 4,
    /// Spectral band replication; reconstructs high-frequency content from lower frequencies
    /// and side information.
    AAC_SBR = 5,
    /// Scalable lossless coding.
    AAC_Scalable = 6,
    /// Transform-domain weighted interleaved vector quantization; an audio codec optimised for
    /// audio coding at ultra low bit rates around 8kbit/s.
    TwinVQ = 7,
    /// Code Excited Linear Prdiction; a narrow-band/wide-band speech codec.
    CELP = 8,
    /// Harmonic Vector Excitation Coding; a very-low bit-rate parametric speech codec.
    HVXC = 9,
}

impl Mpeg4ObjectId {
    /// Create an Mpeg4ObjectId from a u32.
    pub fn from_u32(u: u32) -> Option<Mpeg4ObjectId> {
        match u {
            1 => Some(Mpeg4ObjectId::AAC_Main),
            2 => Some(Mpeg4ObjectId::AAC_LC),
            3 => Some(Mpeg4ObjectId::AAC_SSR),
            4 => Some(Mpeg4ObjectId::AAC_LTP),
            5 => Some(Mpeg4ObjectId::AAC_SBR),
            6 => Some(Mpeg4ObjectId::AAC_Scalable),
            7 => Some(Mpeg4ObjectId::TwinVQ),
            8 => Some(Mpeg4ObjectId::CELP),
            9 => Some(Mpeg4ObjectId::HVXC),
            _ => None,
        }
    }
}

bitflags! {
    /// "These flags indicate the valuid fields in an AudioTimeStamp structure."
    ///
    /// **Available** in OS X v10.0 and later.
    ///
    /// Original Documentation [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/Audio_Time_Stamp_Flags).
    pub struct AudioTimeStampFlags: u32 {
        /// The sample frame time is valid.
        const SAMPLE_TIME_VALID = 1;
        /// The host time is valid.
        const HOST_TIME_VALID = 2;
        /// The rate scalar is valid.
        const RATE_SCALAR_VALID = 4;
        /// The world clock time is valid.
        const WORLD_CLOCK_TIME_VALID = 8;
        /// The SMPTE time is valid.
        const SMPTE_TIME_VALID = 16;
    }
}
