#include "portaudiocpp/DirectionSpecificStreamParameters.hxx"

#include "portaudiocpp/Device.hxx"

namespace portaudio
{

    // -----------------------------------------------------------------------------------

    //////
    /// Returns a `nil' DirectionSpecificStreamParameters object. This can be used to
    /// specify that one direction of a Stream is not required (i.e. when creating
    /// a half-duplex Stream). All fields of the null DirectionSpecificStreamParameters
    /// object are invalid except for the device and the number of channel, which are set
    /// to paNoDevice and 0 respectively.
    //////
    DirectionSpecificStreamParameters DirectionSpecificStreamParameters::null()
    {
        DirectionSpecificStreamParameters tmp;
        tmp.paStreamParameters_.device = paNoDevice;
        tmp.paStreamParameters_.channelCount = 0;
        return tmp;
    }

    // -----------------------------------------------------------------------------------

    //////
    /// Default constructor -- all parameters will be uninitialized.
    //////
    DirectionSpecificStreamParameters::DirectionSpecificStreamParameters()
    {
    }

    //////
    /// Constructor which sets all required fields.
    //////
    DirectionSpecificStreamParameters::DirectionSpecificStreamParameters(const Device &device, int numChannels,
        SampleDataFormat format, bool interleaved, PaTime suggestedLatency, void *hostApiSpecificStreamInfo)
    {
        setDevice(device);
        setNumChannels(numChannels);
        setSampleFormat(format, interleaved);
        setSuggestedLatency(suggestedLatency);
        setHostApiSpecificStreamInfo(hostApiSpecificStreamInfo);
    }

    // -----------------------------------------------------------------------------------

    void DirectionSpecificStreamParameters::setDevice(const Device &device)
    {
        paStreamParameters_.device = device.index();
    }

    void DirectionSpecificStreamParameters::setNumChannels(int numChannels)
    {
        paStreamParameters_.channelCount = numChannels;
    }

    void DirectionSpecificStreamParameters::setSampleFormat(SampleDataFormat format, bool interleaved)
    {
        paStreamParameters_.sampleFormat = static_cast<PaSampleFormat>(format);

        if (!interleaved)
            paStreamParameters_.sampleFormat |= paNonInterleaved;
    }

    void DirectionSpecificStreamParameters::setHostApiSpecificSampleFormat(PaSampleFormat format, bool interleaved)
    {
        paStreamParameters_.sampleFormat = format;

        paStreamParameters_.sampleFormat |= paCustomFormat;

        if (!interleaved)
            paStreamParameters_.sampleFormat |= paNonInterleaved;
    }

    void DirectionSpecificStreamParameters::setSuggestedLatency(PaTime latency)
    {
        paStreamParameters_.suggestedLatency = latency;
    }

    void DirectionSpecificStreamParameters::setHostApiSpecificStreamInfo(void *streamInfo)
    {
        paStreamParameters_.hostApiSpecificStreamInfo = streamInfo;
    }

    // -----------------------------------------------------------------------------------

    PaStreamParameters *DirectionSpecificStreamParameters::paStreamParameters()
    {
        if (paStreamParameters_.channelCount > 0 && paStreamParameters_.device != paNoDevice)
            return &paStreamParameters_;
        else
            return NULL;
    }

    const PaStreamParameters *DirectionSpecificStreamParameters::paStreamParameters() const
    {
        if (paStreamParameters_.channelCount > 0 && paStreamParameters_.device != paNoDevice)
            return &paStreamParameters_;
        else
            return NULL;
    }

    Device &DirectionSpecificStreamParameters::device() const
    {
        return System::instance().deviceByIndex(paStreamParameters_.device);
    }

    int DirectionSpecificStreamParameters::numChannels() const
    {
        return paStreamParameters_.channelCount;
    }

    //////
    /// Returns the (non host api-specific) sample format, without including
    /// the paNonInterleaved flag. If the sample format is host api-spefific,
    /// INVALID_FORMAT (0) will be returned.
    //////
    SampleDataFormat DirectionSpecificStreamParameters::sampleFormat() const
    {
        if (isSampleFormatHostApiSpecific())
            return INVALID_FORMAT;
        else
            return static_cast<SampleDataFormat>(paStreamParameters_.sampleFormat & ~paNonInterleaved);
    }

    bool DirectionSpecificStreamParameters::isSampleFormatInterleaved() const
    {
        return ((paStreamParameters_.sampleFormat & paNonInterleaved) == 0);
    }

    bool DirectionSpecificStreamParameters::isSampleFormatHostApiSpecific() const
    {
        return ((paStreamParameters_.sampleFormat & paCustomFormat) == 0);
    }

    //////
    /// Returns the host api-specific sample format, without including any
    /// paCustomFormat or paNonInterleaved flags. Will return 0 if the sample format is
    /// not host api-specific.
    //////
    PaSampleFormat DirectionSpecificStreamParameters::hostApiSpecificSampleFormat() const
    {
        if (isSampleFormatHostApiSpecific())
            return paStreamParameters_.sampleFormat & ~paCustomFormat & ~paNonInterleaved;
        else
            return 0;
    }

    PaTime DirectionSpecificStreamParameters::suggestedLatency() const
    {
        return paStreamParameters_.suggestedLatency;
    }

    void *DirectionSpecificStreamParameters::hostApiSpecificStreamInfo() const
    {
        return paStreamParameters_.hostApiSpecificStreamInfo;
    }

    // -----------------------------------------------------------------------------------

} // namespace portaudio
