/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "vie_autotest_defines.h"
#include "vie_autotest.h"
#include "engine_configurations.h"

#include "testsupport/fileutils.h"
#include "tb_interfaces.h"
#include "tb_capture_device.h"

#include "voe_codec.h"

class ViEAutotestFileObserver: public webrtc::ViEFileObserver
{
public:
    ViEAutotestFileObserver() {};
    ~ViEAutotestFileObserver() {};

    void PlayFileEnded(const WebRtc_Word32 fileId)
    {
        ViETest::Log("PlayFile ended");
    }
};

void ViEAutoTest::ViEFileStandardTest()
{
#ifdef WEBRTC_VIDEO_ENGINE_FILE_API
    //***************************************************************
    //	Begin create/initialize WebRTC Video Engine for testing
    //***************************************************************
    {
        ViETest::Log("Starting a loopback call...");

        TbInterfaces interfaces = TbInterfaces("ViEFileStandardTest");

        webrtc::VideoEngine* ptrViE = interfaces.video_engine;
        webrtc::ViEBase* ptrViEBase = interfaces.base;
        webrtc::ViECapture* ptrViECapture = interfaces.capture;
        webrtc::ViERender* ptrViERender = interfaces.render;
        webrtc::ViECodec* ptrViECodec = interfaces.codec;
        webrtc::ViERTP_RTCP* ptrViERtpRtcp = interfaces.rtp_rtcp;
        webrtc::ViENetwork* ptrViENetwork = interfaces.network;

        TbCaptureDevice captureDevice = TbCaptureDevice(interfaces);
        int captureId = captureDevice.captureId;

        int videoChannel = -1;
        EXPECT_EQ(0, ptrViEBase->CreateChannel(videoChannel));
        EXPECT_EQ(0, ptrViECapture->ConnectCaptureDevice(
            captureId, videoChannel));

        EXPECT_EQ(0, ptrViERtpRtcp->SetRTCPStatus(
            videoChannel, webrtc::kRtcpCompound_RFC4585));
        EXPECT_EQ(0, ptrViERtpRtcp->SetKeyFrameRequestMethod(
            videoChannel, webrtc::kViEKeyFrameRequestPliRtcp));
        EXPECT_EQ(0, ptrViERtpRtcp->SetTMMBRStatus(videoChannel, true));

        EXPECT_EQ(0, ptrViERender->AddRenderer(
            captureId, _window1, 0, 0.0, 0.0, 1.0, 1.0));
        EXPECT_EQ(0, ptrViERender->AddRenderer(
            videoChannel, _window2, 1, 0.0, 0.0, 1.0, 1.0));
        EXPECT_EQ(0, ptrViERender->StartRender(captureId));
        EXPECT_EQ(0, ptrViERender->StartRender(videoChannel));

        webrtc::VideoCodec videoCodec;
        memset(&videoCodec, 0, sizeof(webrtc::VideoCodec));
        for (int idx = 0; idx < ptrViECodec->NumberOfCodecs(); idx++)
        {
            EXPECT_EQ(0, ptrViECodec->GetCodec(idx, videoCodec));
            EXPECT_EQ(0, ptrViECodec->SetReceiveCodec(videoChannel,
                                                      videoCodec));
        }

        // Find the codec used for encoding the channel
        for (int idx = 0; idx < ptrViECodec->NumberOfCodecs(); idx++)
        {
            EXPECT_EQ(0, ptrViECodec->GetCodec(idx, videoCodec));
            if (videoCodec.codecType == webrtc::kVideoCodecVP8)
            {
                EXPECT_EQ(0, ptrViECodec->SetSendCodec(videoChannel, videoCodec));
                break;
            }
        }
        // Find the codec used for recording.
        for (int idx = 0; idx < ptrViECodec->NumberOfCodecs(); idx++)
        {
            EXPECT_EQ(0, ptrViECodec->GetCodec(idx, videoCodec));
            if (videoCodec.codecType == webrtc::kVideoCodecI420)
            {
                break;
            }
        }


        const char* ipAddress = "127.0.0.1";
        const unsigned short rtpPort = 6000;
        EXPECT_EQ(0, ptrViENetwork->SetLocalReceiver(videoChannel, rtpPort));
        EXPECT_EQ(0, ptrViEBase->StartReceive(videoChannel));
        EXPECT_EQ(0, ptrViENetwork->SetSendDestination(
            videoChannel, ipAddress, rtpPort));
        EXPECT_EQ(0, ptrViEBase->StartSend(videoChannel));
        webrtc::ViEFile* ptrViEFile = webrtc::ViEFile::GetInterface(ptrViE);
        EXPECT_TRUE(ptrViEFile != NULL);

        webrtc::VoiceEngine* ptrVEEngine = webrtc::VoiceEngine::Create();
        webrtc::VoEBase* ptrVEBase = webrtc::VoEBase::GetInterface(ptrVEEngine);
        ptrVEBase->Init();

        int audioChannel = ptrVEBase->CreateChannel();
        ptrViEBase->SetVoiceEngine(ptrVEEngine);
        ptrViEBase->ConnectAudioChannel(videoChannel, audioChannel);

        webrtc::CodecInst audioCodec;
        webrtc::VoECodec* ptrVECodec =
            webrtc::VoECodec::GetInterface(ptrVEEngine);
        for (int index = 0; index < ptrVECodec->NumOfCodecs(); index++)
        {
            ptrVECodec->GetCodec(index, audioCodec);
            if (0 == strcmp(audioCodec.plname, "PCMU") || 0
                == strcmp(audioCodec.plname, "PCMA"))
            {
                break; // these two types are allowed as avi recording formats
            }
        }

        webrtc::CodecInst audioCodec2;

        //***************************************************************
        //	Engine ready. Begin testing class
        //***************************************************************

        // Call started
        ViETest::Log("Call started\nYou should see local preview from camera\n"
                     "in window 1 and the remote video in window 2.");
        AutoTestSleep(2000);

        const int RENDER_TIMEOUT = 1000;
        const int TEST_SPACING = 1000;
        const int VIDEO_LENGTH = 5000;

        const std::string root = webrtc::test::ProjectRootPath() +
            "src/video_engine/test/auto_test/media/";
        const std::string renderStartImage = root + "renderStartImage.jpg";
        const std::string captureDeviceImage = root + "captureDeviceImage.jpg";
        const std::string renderTimeoutFile = root + "renderTimeoutImage.jpg";

        const std::string output = webrtc::test::OutputPath();
        const std::string snapshotCaptureDeviceFileName =
            output + "snapshotCaptureDevice.jpg";
        const std::string incomingVideo = output + "incomingVideo.avi";
        const std::string outgoingVideo = output + "outgoingVideo.avi";
        const std::string snapshotRenderFileName =
            output + "snapshotRenderer.jpg";

        webrtc::ViEPicture capturePicture;
        webrtc::ViEPicture renderPicture;
        webrtc::ViEPicture renderTimeoutPicture; // TODO: init with and image

        ViEAutotestFileObserver fileObserver;
        int fileId;

        AutoTestSleep(TEST_SPACING);

        // testing StartRecordIncomingVideo and StopRecordIncomingVideo
        {
            ViETest::Log("Recording incoming video (currently no audio) for %d "
                         "seconds", VIDEO_LENGTH);

            EXPECT_EQ(0, ptrViEFile->StartRecordIncomingVideo(
                videoChannel, incomingVideo.c_str(), webrtc::NO_AUDIO,
                audioCodec2, videoCodec));

            AutoTestSleep(VIDEO_LENGTH);
            ViETest::Log("Stop recording incoming video");

            EXPECT_EQ(0, ptrViEFile->StopRecordIncomingVideo(videoChannel));
            ViETest::Log("Done\n");
        }

        AutoTestSleep(TEST_SPACING);

        // testing GetFileInformation
        {
            webrtc::VideoCodec fileVideoCodec;
            webrtc::CodecInst fileAudioCodec;
            ViETest::Log("Reading video file information");

            EXPECT_EQ(0, ptrViEFile->GetFileInformation(
                incomingVideo.c_str(), fileVideoCodec, fileAudioCodec));
            PrintAudioCodec(fileAudioCodec);
            PrintVideoCodec(fileVideoCodec);
        }

        // testing StartPlayFile and RegisterObserver
        {
            ViETest::Log("Start playing file: %s with observer",
                         incomingVideo.c_str());
            EXPECT_EQ(0, ptrViEFile->StartPlayFile(incomingVideo.c_str(),
                                                   fileId));

            ViETest::Log("Registering file observer");
            EXPECT_EQ(0, ptrViEFile->RegisterObserver(fileId, fileObserver));
            ViETest::Log("Done\n");
        }

        // testing SendFileOnChannel and StopSendFileOnChannel
        {
            ViETest::Log("Sending video on channel");
            // should fail since we are sending the capture device.
            EXPECT_NE(0, ptrViEFile->SendFileOnChannel(fileId, videoChannel));

            // Disconnect the camera
            EXPECT_EQ(0, ptrViECapture->DisconnectCaptureDevice(videoChannel));

            // And try playing the file again.
            EXPECT_EQ(0, ptrViEFile->SendFileOnChannel(fileId, videoChannel));

            AutoTestSleep(VIDEO_LENGTH);
            ViETest::Log("Stopped sending video on channel");
            EXPECT_EQ(0, ptrViEFile->StopSendFileOnChannel(videoChannel));
            ViETest::Log("Done\n");
        }

        AutoTestSleep(TEST_SPACING);

        // stop playing the file
        {
            ViETest::Log("Stop playing the file.");
            EXPECT_EQ(0, ptrViEFile->StopPlayFile(fileId));
            ViETest::Log("Done\n");
        }

        // testing StartRecordOutgoingVideo and StopRecordOutgoingVideo
        {
            // connect the camera to the output.
            EXPECT_EQ(0, ptrViECapture->ConnectCaptureDevice(
              captureId, videoChannel));

            ViETest::Log("Recording outgoing video (currently no audio) for %d "
                         "seconds", VIDEO_LENGTH);
            EXPECT_EQ(0, ptrViEFile->StartRecordOutgoingVideo(
                videoChannel, outgoingVideo.c_str(), webrtc::NO_AUDIO,
                audioCodec2, videoCodec));

            AutoTestSleep(VIDEO_LENGTH);
            ViETest::Log("Stop recording outgoing video");
            EXPECT_EQ(0, ptrViEFile->StopRecordOutgoingVideo(videoChannel));
            ViETest::Log("Done\n");
        }

        // again testing GetFileInformation
        {
            EXPECT_EQ(0, ptrViEFile->GetFileInformation(
                incomingVideo.c_str(), videoCodec, audioCodec2));
            PrintAudioCodec(audioCodec2);
            PrintVideoCodec(videoCodec);
        }

        AutoTestSleep(TEST_SPACING);

        // GetCaptureDeviceSnapshot
        {
            ViETest::Log("Testing GetCaptureDeviceSnapshot(int, ViEPicture)");
            ViETest::Log("Taking a picture to use for displaying ViEPictures "
                         "for the rest of file test");
            ViETest::Log("Hold an object to the camera. Ready?...");
            AutoTestSleep(1000);
            ViETest::Log("3");
            AutoTestSleep(1000);
            ViETest::Log("...2");
            AutoTestSleep(1000);
            ViETest::Log("...1");
            AutoTestSleep(1000);
            ViETest::Log("...Taking picture!");
            EXPECT_EQ(0, ptrViEFile->GetCaptureDeviceSnapshot(
                captureId, capturePicture));
            ViETest::Log("Picture has been taken.");
            AutoTestSleep(TEST_SPACING);

            ViETest::Log("Done\n");
        }

        AutoTestSleep(TEST_SPACING);

        // GetRenderSnapshot
        {
            ViETest::Log("Testing GetRenderSnapshot(int, char*)");

            ViETest::Log("Taking snapshot of videoChannel %d", captureId);
            EXPECT_EQ(0, ptrViEFile->GetRenderSnapshot(
                captureId, snapshotRenderFileName.c_str()));
            ViETest::Log("Wrote image to file %s",
                         snapshotRenderFileName.c_str());
            ViETest::Log("Done\n");
            AutoTestSleep(TEST_SPACING);
        }

        // GetRenderSnapshot
        {
            ViETest::Log("Testing GetRenderSnapshot(int, ViEPicture)");
            EXPECT_EQ(0, ptrViEFile->GetRenderSnapshot(
                captureId, renderPicture));
            ViETest::Log("Done\n");
        }

        AutoTestSleep(TEST_SPACING);

        // GetCaptureDeviceSnapshot
        {
            ViETest::Log("Testing GetCaptureDeviceSnapshot(int, char*)");
            ViETest::Log("Taking snapshot from capture device %d", captureId);
            EXPECT_EQ(0, ptrViEFile->GetCaptureDeviceSnapshot(
                captureId, snapshotCaptureDeviceFileName.c_str()));
            ViETest::Log("Wrote image to file %s",
                         snapshotCaptureDeviceFileName.c_str());
            ViETest::Log("Done\n");
        }

        AutoTestSleep(TEST_SPACING);

        // Testing: SetCaptureDeviceImage
        {
            ViETest::Log("Testing SetCaptureDeviceImage(int, char*)");
            EXPECT_EQ(0, ptrViECapture->StopCapture(captureId));
            EXPECT_EQ(0, ptrViEFile->SetCaptureDeviceImage(
                captureId, captureDeviceImage.c_str()));

            ViETest::Log("you should see the capture device image now");
            AutoTestSleep(2 * RENDER_TIMEOUT);
            EXPECT_EQ(0, ptrViECapture->StartCapture(captureId));
            ViETest::Log("Done\n");
        }

        AutoTestSleep(TEST_SPACING);

        // Testing: SetCaptureDeviceImage
        {
            ViETest::Log("Testing SetCaptureDeviceImage(int, ViEPicture)");
            EXPECT_EQ(0, ptrViECapture->StopCapture(captureId));
            EXPECT_EQ(0, ptrViEFile->SetCaptureDeviceImage(
                captureId, capturePicture));

            ViETest::Log("you should see the capture device image now");
            AutoTestSleep(2 * RENDER_TIMEOUT);
            EXPECT_EQ(0, ptrViECapture->StartCapture(captureId));
            ViETest::Log("Done\n");
        }

        AutoTestSleep(TEST_SPACING);

        // testing SetRenderStartImage(videoChannel, renderStartImage);
        {
            ViETest::Log("Testing SetRenderStartImage(int, char*)");
            // set render image, then stop capture and stop render to display it
            ViETest::Log("Stoping renderer, setting start image, then "
                         "restarting");
            EXPECT_EQ(0, ptrViEFile->SetRenderStartImage(
                videoChannel, renderStartImage.c_str()));
            EXPECT_EQ(0, ptrViECapture->StopCapture(captureId));
            EXPECT_EQ(0, ptrViERender->StopRender(videoChannel));

            ViETest::Log("Render start image should be displayed.");
            AutoTestSleep(RENDER_TIMEOUT);

            // restarting capture and render
            EXPECT_EQ(0, ptrViECapture->StartCapture(captureId));
            EXPECT_EQ(0, ptrViERender->StartRender(videoChannel));
            ViETest::Log("Done\n");
        }

        AutoTestSleep(TEST_SPACING);

        // testing SetRenderStartImage(videoChannel, renderStartImage);
        {
            ViETest::Log("Testing SetRenderStartImage(int, ViEPicture)");
            // set render image, then stop capture and stop render to display it
            ViETest::Log("Stoping renderer, setting start image, then "
                         "restarting");
            EXPECT_EQ(0, ptrViEFile->SetRenderStartImage(
                videoChannel, capturePicture));
            EXPECT_EQ(0, ptrViECapture->StopCapture(captureId));
            EXPECT_EQ(0, ptrViERender->StopRender(videoChannel));

            ViETest::Log("Render start image should be displayed.");
            AutoTestSleep(RENDER_TIMEOUT);

            // restarting capture and render
            EXPECT_EQ(0, ptrViECapture->StartCapture(captureId));
            EXPECT_EQ(0, ptrViERender->StartRender(videoChannel));
            ViETest::Log("Done\n");
        }

        AutoTestSleep(TEST_SPACING);

        // testing SetRenderTimeoutImage(videoChannel, renderTimeoutFile,
        // RENDER_TIMEOUT);
        {
            ViETest::Log("Testing SetRenderTimeoutImage(int, char*)");
            ViETest::Log("Stopping capture device to induce timeout of %d ms",
                         RENDER_TIMEOUT);
            EXPECT_EQ(0, ptrViEFile->SetRenderTimeoutImage(
                videoChannel, renderTimeoutFile.c_str(), RENDER_TIMEOUT));

            // now stop sending frames to the remote renderer and wait for
            // timeout
            EXPECT_EQ(0, ptrViECapture->StopCapture(captureId));
            AutoTestSleep(RENDER_TIMEOUT);
            ViETest::Log("Timeout image should be displayed now for %d ms",
                         RENDER_TIMEOUT * 2);
            AutoTestSleep(RENDER_TIMEOUT * 2);

            // restart the capture device to undo the timeout
            EXPECT_EQ(0, ptrViECapture->StartCapture(captureId));
            ViETest::Log("Restarting capture device");
            AutoTestSleep(RENDER_TIMEOUT);
            ViETest::Log("Done\n");
        }

        AutoTestSleep(TEST_SPACING);

        // Need to create a ViEPicture object to pass into this function.
        // SetRenderTimeoutImage(videoChannel, renderTimeoutFile,
        // RENDER_TIMEOUT);
        {
            ViETest::Log("Testing SetRenderTimeoutImage(int, ViEPicture)");
            ViETest::Log("Stopping capture device to induce timeout of %d",
                         RENDER_TIMEOUT);
            EXPECT_EQ(0, ptrViEFile->SetRenderTimeoutImage(
                videoChannel, capturePicture, RENDER_TIMEOUT));

            // now stop sending frames to the remote renderer and wait for
            // timeout
            EXPECT_EQ(0, ptrViECapture->StopCapture(captureId));
            AutoTestSleep(RENDER_TIMEOUT);
            ViETest::Log("Timeout image should be displayed now for %d",
                         RENDER_TIMEOUT * 2);
            AutoTestSleep(RENDER_TIMEOUT * 2);

            // restart the capture device to undo the timeout
            EXPECT_EQ(0, ptrViECapture->StartCapture(captureId));
            ViETest::Log("Restarting capture device");
            ViETest::Log("Done\n");
        }

        // testing DeregisterObserver
        {
            ViETest::Log("Deregistering file observer");
            // Should fail since we don't observe this file.
            EXPECT_NE(0, ptrViEFile->DeregisterObserver(fileId, fileObserver));
        }

        //***************************************************************
        //	Testing finished. Tear down Video Engine
        //***************************************************************

        EXPECT_EQ(0, ptrViEBase->DisconnectAudioChannel(videoChannel));
        EXPECT_EQ(0, ptrViEBase->SetVoiceEngine(NULL));
        EXPECT_EQ(0, ptrVEBase->DeleteChannel(audioChannel));
        EXPECT_EQ(0, ptrVEBase->Release());
        EXPECT_EQ(0, ptrVECodec->Release());
        EXPECT_TRUE(webrtc::VoiceEngine::Delete(ptrVEEngine));

        EXPECT_EQ(0, ptrViEBase->StopReceive(videoChannel));
        EXPECT_EQ(0, ptrViEBase->StopSend(videoChannel));
        EXPECT_EQ(0, ptrViERender->StopRender(videoChannel));
        EXPECT_EQ(0, ptrViERender->RemoveRenderer(captureId));
        EXPECT_EQ(0, ptrViERender->RemoveRenderer(videoChannel));
        EXPECT_EQ(0, ptrViECapture->DisconnectCaptureDevice(videoChannel));
        EXPECT_EQ(0, ptrViEFile->FreePicture(capturePicture));
        EXPECT_EQ(0, ptrViEFile->FreePicture(renderPicture));
        EXPECT_EQ(0, ptrViEFile->FreePicture(renderTimeoutPicture));
        EXPECT_EQ(0, ptrViEBase->DeleteChannel(videoChannel));

        EXPECT_EQ(0, ptrViEFile->Release());
    }
#endif
}

void ViEAutoTest::ViEFileExtendedTest()
{
}

void ViEAutoTest::ViEFileAPITest()
{
}
