/*
 * 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.
 */

#define LOG_TAG "TrackPlayerBaseTest"

#include <binder/ProcessState.h>
#include <gtest/gtest.h>
#include <media/TrackPlayerBase.h>

#include "test_execution_tracer.h"

using namespace android;
using namespace android::media;

class TrackPlayer : public TrackPlayerBase, public AudioTrack::IAudioTrackCallback {
  public:
    // methods protected in base class
    using TrackPlayerBase::playerPause;
    using TrackPlayerBase::playerSetVolume;
    using TrackPlayerBase::playerStart;
    using TrackPlayerBase::playerStop;
};

class TrackPlayerBaseTest
    : public ::testing::TestWithParam<std::tuple<double, double, uint32_t, uint32_t>> {
  public:
    TrackPlayerBaseTest()
        : mDuration(std::get<0>(GetParam())),
          mPulseFreq(std::get<1>(GetParam())),
          mChannelCount(std::get<2>(GetParam())),
          mSampleRate(std::get<3>(GetParam())){};

    virtual void SetUp() override {
        mFrameCount = mDuration * mSampleRate;
        audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(mChannelCount);
        sp<AudioTrack> track = new AudioTrack(mStreamType, mSampleRate, mFormat, channelMask,
                                              mFrameCount, mFlags, nullptr /* callback */,
                                              0 /* notificationFrames */, AUDIO_SESSION_NONE);
        ASSERT_EQ(track->initCheck(), NO_ERROR);

        mPlayer = new TrackPlayer();
        mPlayer->init(track.get(), mPlayer, PLAYER_TYPE_AAUDIO, AUDIO_USAGE_MEDIA,
                      AUDIO_SESSION_NONE);
        sp<AudioTrack> playerTrack = mPlayer->getAudioTrack();
        ASSERT_EQ(playerTrack->initCheck(), NO_ERROR);

        mBufferSize = mFrameCount * playerTrack->frameSize();
        mBuffer.resize(mBufferSize, 0);

        // populate buffer
        ASSERT_NE(mPulseFreq, 0);
        int32_t nPulseSamples = mSampleRate / mPulseFreq;
        int32_t pulseSize = nPulseSamples * playerTrack->frameSize();

        int32_t marker = 0;
        while (marker + pulseSize <= mBufferSize) {
            memset(mBuffer.data() + marker, 127, pulseSize / 2);
            marker += pulseSize;
        }
    }

    void playBuffer() {
        bool blocking = true;
        ssize_t nbytes = mPlayer->getAudioTrack()->write(mBuffer.data(), mBufferSize, blocking);
        EXPECT_EQ(nbytes, mBufferSize) << "Did not write all data in blocking mode";
    }

    const double mDuration;  // seconds
    sp<TrackPlayer> mPlayer;

  private:
    const double mPulseFreq;
    const uint32_t mChannelCount;
    const uint32_t mSampleRate;

    const audio_format_t mFormat = AUDIO_FORMAT_PCM_16_BIT;
    const audio_output_flags_t mFlags = AUDIO_OUTPUT_FLAG_NONE;
    const audio_stream_type_t mStreamType = AUDIO_STREAM_MUSIC;

    int32_t mBufferSize;
    int32_t mFrameCount;
    std::vector<uint8_t> mBuffer;
};

class PlaybackTestParam : public TrackPlayerBaseTest {};

TEST_P(PlaybackTestParam, PlaybackTest) {
    // no-op implementation
    EXPECT_TRUE(mPlayer->setStartDelayMs(0).isOk());

    ASSERT_EQ(mPlayer->playerStart(), NO_ERROR);
    ASSERT_NO_FATAL_FAILURE(playBuffer());
    EXPECT_EQ(mPlayer->playerStop(), NO_ERROR);
}

INSTANTIATE_TEST_SUITE_P(TrackPlayerTest, PlaybackTestParam,
                         ::testing::Values(std::make_tuple(2.5, 25.0, 2, 48000)));

class ChangeVolumeTestParam : public TrackPlayerBaseTest {};

TEST_P(ChangeVolumeTestParam, ChangeVolumeTest) {
    float volume = 1.0f;
    (void)mPlayer->setPlayerVolume(volume / 2, volume);

    ASSERT_TRUE(mPlayer->start().isOk());
    ASSERT_EQ(mPlayer->playerSetVolume(), NO_ERROR);

    ASSERT_NO_FATAL_FAILURE(playBuffer());

    EXPECT_TRUE(mPlayer->stop().isOk());

    std::vector<float> setVol = {0.95f, 0.05f, 0.5f, 0.25f, -1.0f, 1.0f, 1.0f};
    std::vector<float> setPan = {0.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.5f, -0.5f};

    ASSERT_TRUE(mPlayer->start().isOk());

    for (int32_t i = 0; i < setVol.size(); i++) {
        EXPECT_TRUE(mPlayer->setVolume(setVol[i]).isOk());
        EXPECT_TRUE(mPlayer->setPan(setPan[i]).isOk());
        ASSERT_NO_FATAL_FAILURE(playBuffer());
    }
    EXPECT_TRUE(mPlayer->stop().isOk());
}

INSTANTIATE_TEST_SUITE_P(TrackPlayerTest, ChangeVolumeTestParam,
                         ::testing::Values(std::make_tuple(1.0, 100.0, 1, 24000)));

class PauseTestParam : public TrackPlayerBaseTest {};

TEST_P(PauseTestParam, PauseTest) {
    ASSERT_EQ(mPlayer->playerStart(), NO_ERROR);
    ASSERT_NO_FATAL_FAILURE(playBuffer());

    ASSERT_EQ(mPlayer->playerPause(), NO_ERROR);
    ASSERT_EQ(mPlayer->playerStart(), NO_ERROR);

    ASSERT_NO_FATAL_FAILURE(playBuffer());

    EXPECT_EQ(mPlayer->playerStop(), NO_ERROR);

    for (int32_t i = 0; i < 5; i++) {
        ASSERT_TRUE(mPlayer->start().isOk());
        ASSERT_NO_FATAL_FAILURE(playBuffer());
        ASSERT_TRUE(mPlayer->pause().isOk());
    }
    EXPECT_TRUE(mPlayer->stop().isOk());
}

INSTANTIATE_TEST_SUITE_P(TrackPlayerTest, PauseTestParam,
                         ::testing::Values(std::make_tuple(1.0, 75.0, 2, 24000)));

int main(int argc, char** argv) {
    android::ProcessState::self()->startThreadPool();
    ::testing::InitGoogleTest(&argc, argv);
    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
    return RUN_ALL_TESTS();
}
