/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * 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.
 */

package android.bluetooth;

import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;

import com.android.bluetooth.flags.Flags;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;

/**
 * Represents the codec configuration for a Bluetooth LE Audio source device.
 *
 * <p>Contains the source codec type.
 *
 * <p>The source codec type values are the same as those supported by the device hardware.
 *
 * @see BluetoothLeAudioCodecConfig
 */
public final class BluetoothLeAudioCodecConfig implements Parcelable {
    // Add an entry for each source codec here.

    /** @hide */
    @IntDef(
            prefix = "SOURCE_CODEC_TYPE_",
            value = {SOURCE_CODEC_TYPE_LC3, SOURCE_CODEC_TYPE_INVALID})
    @Retention(RetentionPolicy.SOURCE)
    public @interface SourceCodecType {};

    public static final int SOURCE_CODEC_TYPE_LC3 = 0;
    public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;

    /** @hide */
    @IntDef(
            prefix = "CODEC_PRIORITY_",
            value = {CODEC_PRIORITY_DISABLED, CODEC_PRIORITY_DEFAULT, CODEC_PRIORITY_HIGHEST})
    @Retention(RetentionPolicy.SOURCE)
    public @interface CodecPriority {}

    /**
     * Codec priority disabled. Used to indicate that this codec is disabled and should not be used.
     */
    public static final int CODEC_PRIORITY_DISABLED = -1;

    /** Codec priority default. Default value used for codec priority. */
    public static final int CODEC_PRIORITY_DEFAULT = 0;

    /** Codec priority highest. Used to indicate the highest priority a codec can have. */
    public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000;

    /** @hide */
    @IntDef(
            flag = true,
            prefix = "SAMPLE_RATE_",
            value = {
                SAMPLE_RATE_NONE,
                SAMPLE_RATE_8000,
                SAMPLE_RATE_11025,
                SAMPLE_RATE_16000,
                SAMPLE_RATE_22050,
                SAMPLE_RATE_24000,
                SAMPLE_RATE_32000,
                SAMPLE_RATE_44100,
                SAMPLE_RATE_48000,
                SAMPLE_RATE_88200,
                SAMPLE_RATE_96000,
                SAMPLE_RATE_176400,
                SAMPLE_RATE_192000,
                SAMPLE_RATE_384000
            })
    @Retention(RetentionPolicy.SOURCE)
    public @interface SampleRate {}

    /**
     * Codec sample rate 0 Hz. Default value used for codec sample rate. Values are the bit mask as
     * defined in the Bluetooth Assigned Numbers, Generic Audio, Supported_Sampling_Frequencies
     * table.
     */
    public static final int SAMPLE_RATE_NONE = 0;

    /** Codec sample rate 8000 Hz. */
    public static final int SAMPLE_RATE_8000 = 0x01 << 0;

    /** Codec sample rate 11025 Hz. */
    @FlaggedApi(Flags.FLAG_LEAUDIO_ADD_SAMPLING_FREQUENCIES)
    public static final int SAMPLE_RATE_11025 = 0x01 << 1;

    /** Codec sample rate 16000 Hz. */
    public static final int SAMPLE_RATE_16000 = 0x01 << 2;

    /** Codec sample rate 22050 Hz. */
    @FlaggedApi(Flags.FLAG_LEAUDIO_ADD_SAMPLING_FREQUENCIES)
    public static final int SAMPLE_RATE_22050 = 0x01 << 3;

    /** Codec sample rate 24000 Hz. */
    public static final int SAMPLE_RATE_24000 = 0x01 << 4;

    /** Codec sample rate 32000 Hz. */
    public static final int SAMPLE_RATE_32000 = 0x01 << 5;

    /** Codec sample rate 44100 Hz. */
    public static final int SAMPLE_RATE_44100 = 0x01 << 6;

    /** Codec sample rate 48000 Hz. */
    public static final int SAMPLE_RATE_48000 = 0x01 << 7;

    /** Codec sample rate 88200 Hz. */
    @FlaggedApi(Flags.FLAG_LEAUDIO_ADD_SAMPLING_FREQUENCIES)
    public static final int SAMPLE_RATE_88200 = 0x01 << 8;

    /** Codec sample rate 96000 Hz. */
    @FlaggedApi(Flags.FLAG_LEAUDIO_ADD_SAMPLING_FREQUENCIES)
    public static final int SAMPLE_RATE_96000 = 0x01 << 9;

    /** Codec sample rate 176400 Hz. */
    @FlaggedApi(Flags.FLAG_LEAUDIO_ADD_SAMPLING_FREQUENCIES)
    public static final int SAMPLE_RATE_176400 = 0x01 << 10;

    /** Codec sample rate 192000 Hz. */
    @FlaggedApi(Flags.FLAG_LEAUDIO_ADD_SAMPLING_FREQUENCIES)
    public static final int SAMPLE_RATE_192000 = 0x01 << 11;

    /** Codec sample rate 384000 Hz. */
    @FlaggedApi(Flags.FLAG_LEAUDIO_ADD_SAMPLING_FREQUENCIES)
    public static final int SAMPLE_RATE_384000 = 0x01 << 12;

    /** @hide */
    @IntDef(
            flag = true,
            prefix = "BITS_PER_SAMPLE_",
            value = {
                BITS_PER_SAMPLE_NONE,
                BITS_PER_SAMPLE_16,
                BITS_PER_SAMPLE_24,
                BITS_PER_SAMPLE_32
            })
    @Retention(RetentionPolicy.SOURCE)
    public @interface BitsPerSample {}

    /** Codec bits per sample 0. Default value of the codec bits per sample. */
    public static final int BITS_PER_SAMPLE_NONE = 0;

    /** Codec bits per sample 16. */
    public static final int BITS_PER_SAMPLE_16 = 0x01 << 0;

    /** Codec bits per sample 24. */
    public static final int BITS_PER_SAMPLE_24 = 0x01 << 1;

    /** Codec bits per sample 32. */
    public static final int BITS_PER_SAMPLE_32 = 0x01 << 3;

    /**
     * Values are the bit mask as defined in the Bluetooth Assigned Numbers, Generic Audio,
     * Supported_Audio_Channel_Counts table Note: We use only part of it.
     *
     * @hide
     */
    @IntDef(
            flag = true,
            prefix = "CHANNEL_COUNT_",
            value = {CHANNEL_COUNT_NONE, CHANNEL_COUNT_1, CHANNEL_COUNT_2})
    @Retention(RetentionPolicy.SOURCE)
    public @interface ChannelCount {}

    /** Codec channel mode NONE. Default value of the codec channel mode. */
    public static final int CHANNEL_COUNT_NONE = 0;

    /** Codec channel mode MONO. */
    public static final int CHANNEL_COUNT_1 = 0x01 << 0;

    /** Codec channel mode STEREO. */
    public static final int CHANNEL_COUNT_2 = 0x01 << 1;

    /**
     * Values are the bit mask as defined in the Bluetooth Assigned Numbers, Generic Audio,
     * Supported_Frame_Durations table
     *
     * @hide
     */
    @IntDef(
            flag = true,
            prefix = "FRAME_DURATION_",
            value = {FRAME_DURATION_NONE, FRAME_DURATION_7500, FRAME_DURATION_10000})
    @Retention(RetentionPolicy.SOURCE)
    public @interface FrameDuration {}

    /** Frame duration 0. Default value of the frame duration. */
    public static final int FRAME_DURATION_NONE = 0;

    /** Frame duration 7500 us. */
    public static final int FRAME_DURATION_7500 = 0x01 << 0;

    /** Frame duration 10000 us. */
    public static final int FRAME_DURATION_10000 = 0x01 << 1;

    private final @SourceCodecType int mCodecType;
    private final @CodecPriority int mCodecPriority;
    private final @SampleRate int mSampleRate;
    private final @BitsPerSample int mBitsPerSample;
    private final @ChannelCount int mChannelCount;
    private final @FrameDuration int mFrameDuration;
    private final int mOctetsPerFrame;
    private final int mMinOctetsPerFrame;
    private final int mMaxOctetsPerFrame;

    /**
     * Creates a new BluetoothLeAudioCodecConfig.
     *
     * @param codecType the source codec type
     * @param codecPriority the priority of this codec
     * @param sampleRate the codec sample rate
     * @param bitsPerSample the bits per sample of this codec
     * @param channelCount the channel count of this codec
     * @param frameDuration the frame duration of this codec
     * @param octetsPerFrame the octets per frame of this codec
     * @param minOctetsPerFrame the minimum octets per frame of this codec
     * @param maxOctetsPerFrame the maximum octets per frame of this codec
     */
    private BluetoothLeAudioCodecConfig(
            @SourceCodecType int codecType,
            @CodecPriority int codecPriority,
            @SampleRate int sampleRate,
            @BitsPerSample int bitsPerSample,
            @ChannelCount int channelCount,
            @FrameDuration int frameDuration,
            int octetsPerFrame,
            int minOctetsPerFrame,
            int maxOctetsPerFrame) {
        mCodecType = codecType;
        mCodecPriority = codecPriority;
        mSampleRate = sampleRate;
        mBitsPerSample = bitsPerSample;
        mChannelCount = channelCount;
        mFrameDuration = frameDuration;
        mOctetsPerFrame = octetsPerFrame;
        mMinOctetsPerFrame = minOctetsPerFrame;
        mMaxOctetsPerFrame = maxOctetsPerFrame;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    /** {@link Parcelable.Creator} interface implementation. */
    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothLeAudioCodecConfig>
            CREATOR =
                    new Parcelable.Creator<BluetoothLeAudioCodecConfig>() {
                        public BluetoothLeAudioCodecConfig createFromParcel(Parcel in) {
                            int codecType = in.readInt();
                            int codecPriority = in.readInt();
                            int sampleRate = in.readInt();
                            int bitsPerSample = in.readInt();
                            int channelCount = in.readInt();
                            int frameDuration = in.readInt();
                            int octetsPerFrame = in.readInt();
                            int minOctetsPerFrame = in.readInt();
                            int maxOctetsPerFrame = in.readInt();
                            return new BluetoothLeAudioCodecConfig(
                                    codecType,
                                    codecPriority,
                                    sampleRate,
                                    bitsPerSample,
                                    channelCount,
                                    frameDuration,
                                    octetsPerFrame,
                                    minOctetsPerFrame,
                                    maxOctetsPerFrame);
                        }

                        public BluetoothLeAudioCodecConfig[] newArray(int size) {
                            return new BluetoothLeAudioCodecConfig[size];
                        }
                    };

    @Override
    public void writeToParcel(@NonNull Parcel out, int flags) {
        out.writeInt(mCodecType);
        out.writeInt(mCodecPriority);
        out.writeInt(mSampleRate);
        out.writeInt(mBitsPerSample);
        out.writeInt(mChannelCount);
        out.writeInt(mFrameDuration);
        out.writeInt(mOctetsPerFrame);
        out.writeInt(mMinOctetsPerFrame);
        out.writeInt(mMaxOctetsPerFrame);
    }

    private String sampleRateToString(@SampleRate int sampleRateBit) {
        switch (sampleRateBit) {
            case SAMPLE_RATE_NONE:
                return "None";
            case SAMPLE_RATE_8000:
                return "8 kHz";
            case SAMPLE_RATE_11025:
                return "11.025 kHz";
            case SAMPLE_RATE_16000:
                return "16 kHz";
            case SAMPLE_RATE_22050:
                return "22.05 kHz";
            case SAMPLE_RATE_24000:
                return "24 kHz";
            case SAMPLE_RATE_32000:
                return "32 kHz";
            case SAMPLE_RATE_44100:
                return "44.1 kHz";
            case SAMPLE_RATE_48000:
                return "48 kHz";
            case SAMPLE_RATE_88200:
                return "88.2 kHz";
            case SAMPLE_RATE_96000:
                return "96 kHz";
            case SAMPLE_RATE_176400:
                return "176.4 kHz";
            case SAMPLE_RATE_192000:
                return "192 kHz";
            case SAMPLE_RATE_384000:
                return "384 kHz";
            default:
                return "Unknown bit " + sampleRateBit;
        }
    }

    private String frameDurationToString(@FrameDuration int frameDurationBit) {
        switch (frameDurationBit) {
            case FRAME_DURATION_NONE:
                return "None";
            case FRAME_DURATION_7500:
                return "7.5 ms";
            case FRAME_DURATION_10000:
                return "10 ms";
            default:
                return "Unknown bit " + frameDurationBit;
        }
    }

    @Override
    public String toString() {
        return "{codecName:"
                + getCodecName()
                + ",mCodecType:"
                + mCodecType
                + ",mCodecPriority:"
                + mCodecPriority
                + ",mSampleRate:"
                + sampleRateToString(mSampleRate)
                + ",mBitsPerSample:"
                + mBitsPerSample
                + ",mChannelCountBitMask:"
                + mChannelCount
                + ",mFrameDuration:"
                + frameDurationToString(mFrameDuration)
                + ",mOctetsPerFrame:"
                + mOctetsPerFrame
                + ",mMinOctetsPerFrame:"
                + mMinOctetsPerFrame
                + ",mMaxOctetsPerFrame:"
                + mMaxOctetsPerFrame
                + "}";
    }

    /**
     * Gets the codec type.
     *
     * @return the codec type
     */
    public @SourceCodecType int getCodecType() {
        return mCodecType;
    }

    /**
     * Gets the codec name.
     *
     * @return the codec name
     */
    public @NonNull String getCodecName() {
        switch (mCodecType) {
            case SOURCE_CODEC_TYPE_LC3:
                return "LC3";
            case SOURCE_CODEC_TYPE_INVALID:
                return "INVALID CODEC";
            default:
                break;
        }
        return "UNKNOWN CODEC(" + mCodecType + ")";
    }

    /**
     * Returns the codec selection priority.
     *
     * <p>The codec selection priority is relative to other codecs: larger value means higher
     * priority.
     */
    public @CodecPriority int getCodecPriority() {
        return mCodecPriority;
    }

    /** Returns the codec sample rate. */
    public @SampleRate int getSampleRate() {
        return mSampleRate;
    }

    /** Returns the codec bits per sample. */
    public @BitsPerSample int getBitsPerSample() {
        return mBitsPerSample;
    }

    /** Returns the codec channel mode. */
    public @ChannelCount int getChannelCount() {
        return mChannelCount;
    }

    /** Returns the frame duration. */
    public @FrameDuration int getFrameDuration() {
        return mFrameDuration;
    }

    /** Returns the octets per frame */
    public int getOctetsPerFrame() {
        return mOctetsPerFrame;
    }

    /** Returns the minimum octets per frame */
    public int getMinOctetsPerFrame() {
        return mMinOctetsPerFrame;
    }

    /** Returns the maximum octets per frame */
    public int getMaxOctetsPerFrame() {
        return mMaxOctetsPerFrame;
    }

    @Override
    public boolean equals(@Nullable Object o) {
        if (o instanceof BluetoothLeAudioCodecConfig) {
            BluetoothLeAudioCodecConfig other = (BluetoothLeAudioCodecConfig) o;
            return (other.getCodecType() == mCodecType
                    && other.getCodecPriority() == mCodecPriority
                    && other.getSampleRate() == mSampleRate
                    && other.getBitsPerSample() == mBitsPerSample
                    && other.getChannelCount() == mChannelCount
                    && other.getFrameDuration() == mFrameDuration
                    && other.getOctetsPerFrame() == mOctetsPerFrame
                    && other.getMinOctetsPerFrame() == mMinOctetsPerFrame
                    && other.getMaxOctetsPerFrame() == mMaxOctetsPerFrame);
        }
        return false;
    }

    /**
     * Returns a hash representation of this BluetoothLeAudioCodecConfig based on all the config
     * values.
     */
    @Override
    public int hashCode() {
        return Objects.hash(
                mCodecType,
                mCodecPriority,
                mSampleRate,
                mBitsPerSample,
                mChannelCount,
                mFrameDuration,
                mOctetsPerFrame,
                mMinOctetsPerFrame,
                mMaxOctetsPerFrame);
    }

    /**
     * Builder for {@link BluetoothLeAudioCodecConfig}.
     *
     * <p>By default, the codec type will be set to {@link
     * BluetoothLeAudioCodecConfig#SOURCE_CODEC_TYPE_INVALID}
     */
    public static final class Builder {
        private int mCodecType = BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID;
        private int mCodecPriority = BluetoothLeAudioCodecConfig.CODEC_PRIORITY_DEFAULT;
        private int mSampleRate = BluetoothLeAudioCodecConfig.SAMPLE_RATE_NONE;
        private int mBitsPerSample = BluetoothLeAudioCodecConfig.BITS_PER_SAMPLE_NONE;
        private int mChannelCount = BluetoothLeAudioCodecConfig.CHANNEL_COUNT_NONE;
        private int mFrameDuration = BluetoothLeAudioCodecConfig.FRAME_DURATION_NONE;
        private int mOctetsPerFrame = 0;
        private int mMinOctetsPerFrame = 0;
        private int mMaxOctetsPerFrame = 0;

        public Builder() {}

        public Builder(@NonNull BluetoothLeAudioCodecConfig config) {
            mCodecType = config.getCodecType();
            mCodecPriority = config.getCodecPriority();
            mSampleRate = config.getSampleRate();
            mBitsPerSample = config.getBitsPerSample();
            mChannelCount = config.getChannelCount();
            mFrameDuration = config.getFrameDuration();
            mOctetsPerFrame = config.getOctetsPerFrame();
            mMinOctetsPerFrame = config.getMinOctetsPerFrame();
            mMaxOctetsPerFrame = config.getMaxOctetsPerFrame();
        }

        /**
         * Set codec type for Bluetooth LE audio codec config.
         *
         * @param codecType of this codec
         * @return the same Builder instance
         */
        public @NonNull Builder setCodecType(@SourceCodecType int codecType) {
            mCodecType = codecType;
            return this;
        }

        /**
         * Set codec priority for Bluetooth LE audio codec config.
         *
         * @param codecPriority of this codec
         * @return the same Builder instance
         */
        public @NonNull Builder setCodecPriority(@CodecPriority int codecPriority) {
            mCodecPriority = codecPriority;
            return this;
        }

        /**
         * Set sample rate for Bluetooth LE audio codec config.
         *
         * @param sampleRate of this codec
         * @return the same Builder instance
         */
        public @NonNull Builder setSampleRate(@SampleRate int sampleRate) {
            mSampleRate = sampleRate;
            return this;
        }

        /**
         * Set the bits per sample for LE audio codec config.
         *
         * @param bitsPerSample of this codec
         * @return the same Builder instance
         */
        public @NonNull Builder setBitsPerSample(@BitsPerSample int bitsPerSample) {
            mBitsPerSample = bitsPerSample;
            return this;
        }

        /**
         * Set the channel count for Bluetooth LE audio codec config.
         *
         * @param channelCount of this codec
         * @return the same Builder instance
         */
        public @NonNull Builder setChannelCount(@ChannelCount int channelCount) {
            mChannelCount = channelCount;
            return this;
        }

        /**
         * Set the frame duration for Bluetooth LE audio codec config.
         *
         * @param frameDuration of this codec
         * @return the same Builder instance
         */
        public @NonNull Builder setFrameDuration(@FrameDuration int frameDuration) {
            mFrameDuration = frameDuration;
            return this;
        }

        /**
         * Set the octets per frame for Bluetooth LE audio codec config.
         *
         * @param octetsPerFrame of this codec
         * @return the same Builder instance
         */
        public @NonNull Builder setOctetsPerFrame(int octetsPerFrame) {
            mOctetsPerFrame = octetsPerFrame;
            return this;
        }

        /**
         * Set the minimum octets per frame for Bluetooth LE audio codec config.
         *
         * @param minOctetsPerFrame of this codec
         * @return the same Builder instance
         */
        public @NonNull Builder setMinOctetsPerFrame(int minOctetsPerFrame) {
            mMinOctetsPerFrame = minOctetsPerFrame;
            return this;
        }

        /**
         * Set the maximum octets per frame for Bluetooth LE audio codec config.
         *
         * @param maxOctetsPerFrame of this codec
         * @return the same Builder instance
         */
        public @NonNull Builder setMaxOctetsPerFrame(int maxOctetsPerFrame) {
            mMaxOctetsPerFrame = maxOctetsPerFrame;
            return this;
        }

        /**
         * Build {@link BluetoothLeAudioCodecConfig}.
         *
         * @return new BluetoothLeAudioCodecConfig built
         */
        public @NonNull BluetoothLeAudioCodecConfig build() {
            return new BluetoothLeAudioCodecConfig(
                    mCodecType,
                    mCodecPriority,
                    mSampleRate,
                    mBitsPerSample,
                    mChannelCount,
                    mFrameDuration,
                    mOctetsPerFrame,
                    mMinOctetsPerFrame,
                    mMaxOctetsPerFrame);
        }
    }
}
