/*
 * Copyright (C) 2020 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.telecom;

import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;

import java.util.Objects;

/**
 * This class represents the quality report that bluetooth framework sends
 * whenever there's a bad voice quality is detected from their side.
 * It is sent as part of a call event via {@link Call#sendCallEvent(String, Bundle)}
 * associated with extra EXTRA_BLUETOOTH_CALL_QUALITY_REPORT.
 * Note that this report will be sent only during an active voice/voip call.
 * @hide
 */
@SystemApi
public final class BluetoothCallQualityReport implements Parcelable {

    /**
     * Event that is sent via {@link Call#sendCallEvent(String, Bundle)} for a call quality report
     */
    public static final String EVENT_BLUETOOTH_CALL_QUALITY_REPORT =
            "android.telecom.event.BLUETOOTH_CALL_QUALITY_REPORT";

    /**
     * Extra key sent with {@link Call#sendCallEvent(String, Bundle)}
     */
    public static final String EXTRA_BLUETOOTH_CALL_QUALITY_REPORT =
            "android.telecom.extra.BLUETOOTH_CALL_QUALITY_REPORT";

    private final long mSentTimestampMillis;
    private final boolean mChoppyVoice;
    private final int mRssiDbm;
    private final int mSnrDb;
    private final int mRetransmittedPacketsCount;
    private final int mPacketsNotReceivedCount;
    private final int mNegativeAcknowledgementCount;

    /**
     * @return Time in milliseconds since the epoch. Designates when report was sent.
     * Used to determine whether this report arrived too late to be useful.
     */
    public @ElapsedRealtimeLong long getSentTimestampMillis() {
        return mSentTimestampMillis;
    }

    /**
     * When the bluetooth controller detects factors that cause choppy voice,
     * the controller reports an (e)SCO Voice Choppy event to the host
     * @return {@code true} when we receive (e)SCO Voice Choppy event from the controller
     */
    public boolean isChoppyVoice() {
        return mChoppyVoice;
    }

    /**
     * @return Received Signal Strength Indication (RSSI) value in dBm.
     * This value shall be an absolute received signal strength value.
     */
    public @IntRange(from = -127, to = 20) int getRssiDbm() {
        return mRssiDbm;
    }

    /**
     * @return Signal-to-Noise Ratio (SNR) value in dB.
     * The controller shall provide the average SNR of all the channels currently used by the link.
     */
    public int getSnrDb() {
        return mSnrDb;
    }

    /**
     * @return The number of retransmissions since the last event.
     * This count shall be reset after it is reported.
     */
    public @IntRange(from = 0) int getRetransmittedPacketsCount() {
        return mRetransmittedPacketsCount;
    }

    /**
     * @return No RX count since the last event.
     * The count increases when no packet is received at the scheduled time slot or the received
     * packet is corrupted.
     * This count shall be reset after it is reported.
     */
    public @IntRange(from = 0) int getPacketsNotReceivedCount() {
        return mPacketsNotReceivedCount;
    }

    /**
     * @return NAK (Negative Acknowledge) count since the last event.
     * This count shall be reset after it is reported.
     */
    public @IntRange(from = 0) int getNegativeAcknowledgementCount() {
        return mNegativeAcknowledgementCount;
    }

    //
    // Parcelable implementation
    //

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

    @Override
    public void writeToParcel(@NonNull Parcel out, int flags) {
        out.writeLong(mSentTimestampMillis);
        out.writeBoolean(mChoppyVoice);
        out.writeInt(mRssiDbm);
        out.writeInt(mSnrDb);
        out.writeInt(mRetransmittedPacketsCount);
        out.writeInt(mPacketsNotReceivedCount);
        out.writeInt(mNegativeAcknowledgementCount);
    }

    public static final @android.annotation.NonNull Creator<BluetoothCallQualityReport> CREATOR =
            new Creator<BluetoothCallQualityReport>() {
                @Override
                public BluetoothCallQualityReport createFromParcel(Parcel in) {
                    return new BluetoothCallQualityReport(in);
                }

                @Override
                public BluetoothCallQualityReport[] newArray(int size) {
                    return new BluetoothCallQualityReport[size];
                }
            };

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        BluetoothCallQualityReport that = (BluetoothCallQualityReport) o;
        return mSentTimestampMillis == that.mSentTimestampMillis
                && mChoppyVoice == that.mChoppyVoice && mRssiDbm == that.mRssiDbm
                && mSnrDb == that.mSnrDb
                && mRetransmittedPacketsCount == that.mRetransmittedPacketsCount
                && mPacketsNotReceivedCount == that.mPacketsNotReceivedCount
                && mNegativeAcknowledgementCount == that.mNegativeAcknowledgementCount;
    }

    @Override
    public int hashCode() {
        return Objects.hash(mSentTimestampMillis, mChoppyVoice, mRssiDbm, mSnrDb,
                mRetransmittedPacketsCount, mPacketsNotReceivedCount,
                mNegativeAcknowledgementCount);
    }

    /**
     * Builder class for {@link ConnectionRequest}
     */
    public static final class Builder {
        private long mSentTimestampMillis;
        private boolean mChoppyVoice;
        private int mRssiDbm;
        private int mSnrDb;
        private int mRetransmittedPacketsCount;
        private int mPacketsNotReceivedCount;
        private int mNegativeAcknowledgementCount;

        public Builder() { }

        /**
         * Set the time when report was sent in milliseconds since the epoch.
         * @param sentTimestampMillis
         */
        public @NonNull Builder setSentTimestampMillis(long sentTimestampMillis) {
            mSentTimestampMillis = sentTimestampMillis;
            return this;
        }

        /**
         * Set if bluetooth hardware detects voice is choppy
         * @param choppyVoice
         */
        public @NonNull Builder setChoppyVoice(boolean choppyVoice) {
            mChoppyVoice = choppyVoice;
            return this;
        }

        /**
         * Set Received Signal Strength Indication (RSSI) value in dBm.
         * @param rssiDbm
         */
        public @NonNull Builder setRssiDbm(int rssiDbm) {
            mRssiDbm = rssiDbm;
            return this;
        }

        /**
         * Set Signal-to-Noise Ratio (SNR) value in dB.
         * @param snrDb
         */
        public @NonNull Builder setSnrDb(int snrDb) {
            mSnrDb = snrDb;
            return this;
        }

        /**
         * Set The number of retransmissions since the last event.
         * @param retransmittedPacketsCount
         */
        public @NonNull Builder setRetransmittedPacketsCount(
                int retransmittedPacketsCount) {
            mRetransmittedPacketsCount = retransmittedPacketsCount;
            return this;
        }

        /**
         * Set No RX count since the last event.
         * @param packetsNotReceivedCount
         */
        public @NonNull Builder setPacketsNotReceivedCount(
                int packetsNotReceivedCount) {
            mPacketsNotReceivedCount = packetsNotReceivedCount;
            return this;
        }

        /**
         * Set NAK (Negative Acknowledge) count since the last event.
         * @param negativeAcknowledgementCount
         */
        public @NonNull Builder setNegativeAcknowledgementCount(
                int negativeAcknowledgementCount) {
            mNegativeAcknowledgementCount = negativeAcknowledgementCount;
            return this;
        }

        /**
         * Build the {@link BluetoothCallQualityReport}
         * @return Result of the builder
         */
        public @NonNull BluetoothCallQualityReport build() {
            return new BluetoothCallQualityReport(this);
        }
    }

    private BluetoothCallQualityReport(Parcel in) {
        mSentTimestampMillis = in.readLong();
        mChoppyVoice = in.readBoolean();
        mRssiDbm = in.readInt();
        mSnrDb = in.readInt();
        mRetransmittedPacketsCount = in.readInt();
        mPacketsNotReceivedCount = in.readInt();
        mNegativeAcknowledgementCount = in.readInt();
    }

    private BluetoothCallQualityReport(Builder builder) {
        mSentTimestampMillis = builder.mSentTimestampMillis;
        mChoppyVoice = builder.mChoppyVoice;
        mRssiDbm = builder.mRssiDbm;
        mSnrDb = builder.mSnrDb;
        mRetransmittedPacketsCount = builder.mRetransmittedPacketsCount;
        mPacketsNotReceivedCount = builder.mPacketsNotReceivedCount;
        mNegativeAcknowledgementCount = builder.mNegativeAcknowledgementCount;
    }
}
