/*
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * 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.
 */

#include "test_common.h"
#include <cinttypes>
#include <cstdio>
#include <fcntl.h>
#include <memory>
#include <securec.h>
#include <sys/time.h>
#include <unistd.h>
#include "camera_output_capability.h"
#include "camera_util.h"
#include "camera_log.h"
#include "picture_proxy.h"
namespace OHOS {
namespace CameraStandard {

std::shared_ptr<PictureIntf> GetPictureIntfInstance()
{
    auto pictureProxy = PictureProxy::CreatePictureProxy();
    CHECK_PRINT_ELOG(pictureProxy == nullptr || pictureProxy.use_count() != 1, "pictureProxy use count is not 1");
    return pictureProxy;
}

camera_format_t TestUtils::GetCameraMetadataFormat(CameraFormat format)
{
    camera_format_t metaFormat = OHOS_CAMERA_FORMAT_YCRCB_420_SP;
    const std::unordered_map<CameraFormat, camera_format_t> mapToMetadataFormat = {
        {CAMERA_FORMAT_YUV_420_SP, OHOS_CAMERA_FORMAT_YCRCB_420_SP},
        {CAMERA_FORMAT_JPEG, OHOS_CAMERA_FORMAT_JPEG},
        {CAMERA_FORMAT_RGBA_8888, OHOS_CAMERA_FORMAT_RGBA_8888},
        {CAMERA_FORMAT_YCBCR_420_888, OHOS_CAMERA_FORMAT_YCBCR_420_888}
    };
    auto itr = mapToMetadataFormat.find(format);
    if (itr != mapToMetadataFormat.end()) {
        metaFormat = itr->second;
    }
    return metaFormat;
}
uint64_t TestUtils::GetCurrentLocalTimeStamp()
{
    std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> tp =
        std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
    auto tmp = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch());
    return tmp.count();
}

int32_t TestUtils::SaveYUV(const char* buffer, int32_t size, SurfaceType type)
{
    char path[PATH_MAX] = {0};
    int32_t retVal;
    CHECK_RETURN_RET_ELOG((buffer == nullptr) || (size == 0), -1, "buffer is null or size is 0");
    MEDIA_DEBUG_LOG("TestUtils::SaveYUV(), type: %{public}d", type);
    if (type == SurfaceType::PREVIEW) {
        (void)system("mkdir -p /data/media/preview");
        retVal = sprintf_s(path, sizeof(path) / sizeof(path[0]), "/data/media/preview/%s_%lld.yuv", "preview",
                           GetCurrentLocalTimeStamp());
        CHECK_RETURN_RET_ELOG(retVal < 0, -1, "Path Assignment failed");
    } else if (type == SurfaceType::PHOTO) {
        (void)system("mkdir -p /data/media/photo");
        retVal = sprintf_s(path, sizeof(path) / sizeof(path[0]), "/data/media/photo/%s_%lld.jpg", "photo",
                           GetCurrentLocalTimeStamp());
        CHECK_RETURN_RET_ELOG(retVal < 0, -1, "Path Assignment failed");
    } else if (type == SurfaceType::SECOND_PREVIEW) {
        (void)system("mkdir -p /data/media/preview2");
        retVal = sprintf_s(path, sizeof(path) / sizeof(path[0]), "/data/media/preview2/%s_%lld.yuv", "preview2",
                           GetCurrentLocalTimeStamp());
        CHECK_RETURN_RET_ELOG(retVal < 0, -1, "Path Assignment failed");
    } else {
        MEDIA_ERR_LOG("Unexpected flow!");
        return -1;
    }

    MEDIA_DEBUG_LOG("TestUtils::SaveYUV saving file to %{private}s", path);
    int imgFd = open(path, O_RDWR | O_CREAT, FILE_PERMISSIONS_FLAG);
    CHECK_RETURN_RET_ELOG(imgFd == -1, -1, "TestUtils::SaveYUV open file failed, errno = %{public}s.", strerror(errno));
    fdsan_exchange_owner_tag(imgFd, 0, LOG_DOMAIN);
    int ret = write(imgFd, buffer, size);
    if (ret == -1) {
        MEDIA_ERR_LOG("%s, write file failed, error = %{public}s", __FUNCTION__, strerror(errno));
        fdsan_close_with_tag(imgFd, LOG_DOMAIN);
        return -1;
    }
    fdsan_close_with_tag(imgFd, LOG_DOMAIN);
    return 0;
}

bool TestUtils::IsNumber(const char number[])
{
    for (int i = 0; number[i] != 0; i++) {
        if (!std::isdigit(number[i])) {
            return false;
        }
    }
    return true;
}

int32_t TestUtils::SaveVideoFile(const char* buffer, int32_t size, VideoSaveMode operationMode, int32_t &fd)
{
    int32_t retVal = 0;

    if (operationMode == VideoSaveMode::CREATE) {
        char path[255] = {0};

        (void)system("mkdir -p /data/media/video");
        retVal = sprintf_s(path, sizeof(path) / sizeof(path[0]),
                           "/data/media/video/%s_%lld.h264", "video", GetCurrentLocalTimeStamp());
        CHECK_RETURN_RET_ELOG(retVal < 0, -1, "Failed to create video file name");
        MEDIA_DEBUG_LOG("%{public}s, save video to file %{private}s", __FUNCTION__, path);
        fd = open(path, O_RDWR | O_CREAT, FILE_PERMISSIONS_FLAG);
        if (fd == -1) {
            std::cout << "open file failed, errno = " << strerror(errno) << std::endl;
            return -1;
        }
        fdsan_exchange_owner_tag(fd, 0, LOG_DOMAIN);
    } else if (operationMode == VideoSaveMode::APPEND && fd != -1) {
        int32_t ret = write(fd, buffer, size);
        if (ret == -1) {
            std::cout << "write file failed, error = " << strerror(errno) << std::endl;
            fdsan_close_with_tag(fd, LOG_DOMAIN);
            fd = -1;
            return fd;
        }
    } else { // VideoSaveMode::CLOSE
        if (fd != -1) {
            fdsan_close_with_tag(fd, LOG_DOMAIN);
            fd = -1;
        }
    }
    return 0;
}

TestCameraMngerCallback::TestCameraMngerCallback(const char* testName) : testName_(testName) {
}

void TestCameraMngerCallback::OnCameraStatusChanged(const CameraStatusInfo &cameraStatusInfo) const
{
    MEDIA_DEBUG_LOG("OnCameraStatusChanged()");
    return;
}

void TestCameraMngerCallback::OnFlashlightStatusChanged(const std::string &cameraID,
                                                        const FlashStatus flashStatus) const
{
    MEDIA_DEBUG_LOG("OnFlashlightStatusChanged(), testName_: %{public}s, cameraID: %{public}s, flashStatus: %{public}d",
                    testName_, cameraID.c_str(), flashStatus);
    return;
}

TestDeviceCallback::TestDeviceCallback(const char* testName) : testName_(testName) {
}

void TestDeviceCallback::OnError(const int32_t errorType, const int32_t errorMsg) const
{
    MEDIA_DEBUG_LOG("TestDeviceCallback::OnError(), testName_: %{public}s, errorType: %{public}d, errorMsg: %{public}d",
                    testName_, errorType, errorMsg);
    return;
}

TestOnResultCallback::TestOnResultCallback(const char* testName) : testName_(testName) {
}

void TestOnResultCallback::OnResult(const uint64_t timestamp,
                                    const std::shared_ptr<Camera::CameraMetadata> &result) const
{
    MEDIA_DEBUG_LOG("TestOnResultCallback::OnResult(), testName_: %{public}s",
                    testName_);
    return;
}


TestPhotoOutputCallback::TestPhotoOutputCallback(const char* testName) : testName_(testName) {
}

void TestPhotoOutputCallback::OnCaptureStarted(const int32_t captureID) const
{
    MEDIA_INFO_LOG("PhotoOutputCallback:OnCaptureStarted(), testName_: %{public}s, captureID: %{public}d",
                   testName_, captureID);
}

void TestPhotoOutputCallback::OnCaptureStarted(const int32_t captureID, uint32_t exposureTime) const
{
    MEDIA_INFO_LOG("PhotoOutputCallback:OnCaptureStarted(), testName_: %{public}s, captureID: %{public}d",
                   testName_, captureID);
}

void TestPhotoOutputCallback::OnCaptureEnded(const int32_t captureID, const int32_t frameCount) const
{
    MEDIA_INFO_LOG("TestPhotoOutputCallback:OnCaptureEnded(), testName_: %{public}s, captureID: %{public}d,"
                   " frameCount: %{public}d", testName_, captureID, frameCount);
}

void TestPhotoOutputCallback::OnFrameShutter(const int32_t captureId, const uint64_t timestamp) const
{
    MEDIA_INFO_LOG("OnFrameShutter(), testName_: %{public}s, captureID: %{public}d", testName_, captureId);
}

void TestPhotoOutputCallback::OnFrameShutterEnd(const int32_t captureId, const uint64_t timestamp) const
{
    MEDIA_INFO_LOG("OnFrameShutterEnd(), testName_: %{public}s, captureID: %{public}d", testName_, captureId);
}

void TestPhotoOutputCallback::OnCaptureReady(const int32_t captureId, const uint64_t timestamp) const
{
    MEDIA_INFO_LOG("OnCaptureReady(), testName_: %{public}s, captureID: %{public}d", testName_, captureId);
}

void TestPhotoOutputCallback::OnEstimatedCaptureDuration(const int32_t duration) const
{
    MEDIA_INFO_LOG("OnEstimatedCaptureDuration(), duration: %{public}d", duration);
}

void TestPhotoOutputCallback::OnConstellationDrawingState(const int32_t drawingState) const
{
    MEDIA_INFO_LOG("OnConstellationDrawingState(), drawingState: %{public}d", drawingState);
}

void TestPhotoOutputCallback::OnOfflineDeliveryFinished(const int32_t captureId) const
{
    MEDIA_INFO_LOG("OnOfflineDeliveryFinished(), captureId: %{public}d", captureId);
}

void TestPhotoOutputCallback::OnCaptureError(const int32_t captureId, const int32_t errorCode) const
{
    MEDIA_INFO_LOG("OnCaptureError(), testName_: %{public}s, captureID: %{public}d, errorCode: %{public}d",
                   testName_, captureId, errorCode);
}

TestPreviewOutputCallback::TestPreviewOutputCallback(const char* testName) : testName_(testName) {
}

void TestPreviewOutputCallback::OnFrameStarted() const
{
    MEDIA_INFO_LOG("TestPreviewOutputCallback:OnFrameStarted(), testName_: %{public}s", testName_);
}

void TestPreviewOutputCallback::OnFrameEnded(const int32_t frameCount) const
{
    MEDIA_INFO_LOG("TestPreviewOutputCallback:OnFrameEnded(), testName_: %{public}s, frameCount: %{public}d",
                   testName_, frameCount);
}

void TestPreviewOutputCallback::OnError(const int32_t errorCode) const
{
    MEDIA_INFO_LOG("TestPreviewOutputCallback:OnError(), testName_: %{public}s, errorCode: %{public}d",
                   testName_, errorCode);
}

void TestPreviewOutputCallback::OnSketchStatusDataChanged(const SketchStatusData& statusData) const
{
    MEDIA_DEBUG_LOG("TestPreviewOutputCallback::OnSketchStatusDataChanged(), testName_: %{public}s", testName_);
    return;
}

void TestPreviewOutputCallback::OnFramePaused() const
{
    MEDIA_INFO_LOG("TestPreviewOutputCallback:OnFramePaused()");
}

void TestPreviewOutputCallback::OnFrameResumed() const
{
    MEDIA_INFO_LOG("TestPreviewOutputCallback:OnFrameResumed()");
}

TestVideoOutputCallback::TestVideoOutputCallback(const char* testName) : testName_(testName) {
}

void TestVideoOutputCallback::OnFrameStarted() const
{
    MEDIA_INFO_LOG("TestVideoOutputCallback:OnFrameStarted(), testName_: %{public}s", testName_);
}

void TestVideoOutputCallback::OnFrameEnded(const int32_t frameCount) const
{
    MEDIA_INFO_LOG("TestVideoOutputCallback:OnFrameEnded(), testName_: %{public}s, frameCount: %{public}d",
                   testName_, frameCount);
}

void TestVideoOutputCallback::OnError(const int32_t errorCode) const
{
    MEDIA_INFO_LOG("TestVideoOutputCallback:OnError(), testName_: %{public}s, errorCode: %{public}d",
                   testName_, errorCode);
}

void TestVideoOutputCallback::OnDeferredVideoEnhancementInfo(const CaptureEndedInfoExt info) const
{
    MEDIA_INFO_LOG("TestVideoOutputCallback:OnDeferredVideoEnhancementInfo()");
}

TestMetadataOutputObjectCallback::TestMetadataOutputObjectCallback(const char* testName) : testName_(testName) {
}

void TestMetadataOutputObjectCallback::OnMetadataObjectsAvailable(std::vector<sptr<MetadataObject>> metaObjects) const
{
    MEDIA_INFO_LOG("TestMetadataOutputObjectCallback:OnMetadataObjectsAvailable(), testName_: %{public}s, "
                   "metaObjects size: %{public}zu", testName_, metaObjects.size());
    for (size_t i = 0; i < metaObjects.size(); i++) {
        MEDIA_INFO_LOG("TestMetadataOutputObjectCallback::OnMetadataObjectsAvailable "
                       "metaObjInfo: Type(%{public}d), Rect{x(%{pulic}f),y(%{pulic}f),w(%{pulic}f),d(%{pulic}f)} "
                       "Timestamp: %{public}" PRId64,
                       metaObjects[i]->GetType(),
                       metaObjects[i]->GetBoundingBox().topLeftX, metaObjects[i]->GetBoundingBox().topLeftY,
                       metaObjects[i]->GetBoundingBox().width, metaObjects[i]->GetBoundingBox().height,
                       metaObjects[i]->GetTimestamp());
    }
}

void TestDeferredPhotoProcSessionCallback::OnProcessImageDone(const std::string& imageId,
                                                              const uint8_t* addr,
                                                              const long bytes, uint32_t cloudImageEnhanceFlag)
{
    MEDIA_INFO_LOG("TestDeferredPhotoProcSessionCallback OnProcessImageDone.");
}

void TestDeferredPhotoProcSessionCallback::OnProcessImageDone(const std::string &imageId,
    std::shared_ptr<PictureIntf> picture, const DpsMetadata& dpsMetadata)
{
    MEDIA_INFO_LOG("TestDeferredPhotoProcSessionCallback OnProcessImageDone Picture.");
}

void TestDeferredPhotoProcSessionCallback::OnProcessImageDone(const std::string& imageId,
    const std::vector<CameraStandard::ImageFd>& imageFds,
    std::shared_ptr<CameraStandard::PictureIntf> lcdImage, const DpsMetadata& metadata)
{
    MEDIA_INFO_LOG("TestDeferredPhotoProcSessionCallback OnProcessImageDone with lcd.");
}

void TestDeferredPhotoProcSessionCallback::OnDeliveryLowQualityImage(const std::string &imageId,
    std::shared_ptr<PictureIntf> picture)
{
    MEDIA_INFO_LOG("TestDeferredPhotoProcSessionCallback OnDeliveryLowQualityImage.");
}

void TestDeferredPhotoProcSessionCallback::OnDeliveryLowQualityLcd(const std::string &imageId,
    std::shared_ptr<PictureIntf> picture)
{
    MEDIA_INFO_LOG("TestDeferredPhotoProcSessionCallback OnDeliveryLowQualityLcd.");
}

void TestDeferredPhotoProcSessionCallback::OnError(const std::string& imageId, const DpsErrorCode errorCode)
{
    MEDIA_INFO_LOG("TestDeferredPhotoProcSessionCallback OnError.");
}

void TestDeferredPhotoProcSessionCallback::OnStateChanged(const DpsStatusCode status)
{
    MEDIA_INFO_LOG("TestDeferredPhotoProcSessionCallback OnStateChanged.");
}

void TestDeferredVideoProcSessionCallback::OnProcessVideoDone(const std::string& videoId,
    const sptr<IPCFileDescriptor> ipcFd)
{
    MEDIA_INFO_LOG("TestDeferredVideoProcSessionCallback OnProcessVideoDone.");
}

void TestDeferredVideoProcSessionCallback::OnProcessVideoDone(const std::string& videoId)
{
    MEDIA_INFO_LOG("TestDeferredVideoProcSessionCallback OnProcessVideoDone.");
}

void TestDeferredVideoProcSessionCallback::OnError(const std::string& videoId, const DpsErrorCode errorCode)
{
    MEDIA_INFO_LOG("TestDeferredVideoProcSessionCallback OnError.");
}

void TestDeferredVideoProcSessionCallback::OnStateChanged(const DpsStatusCode status)
{
    MEDIA_INFO_LOG("TestDeferredVideoProcSessionCallback OnStateChanged.");
}

SurfaceListener::SurfaceListener(const char* testName, SurfaceType type, int32_t &fd, sptr<IConsumerSurface> surface)
    : testName_(testName), surfaceType_(type), fd_(fd), surface_(surface) {
}

void SurfaceListener::OnBufferAvailable()
{
    int32_t flushFence = 0;
    int64_t timestamp = 0;
    OHOS::Rect damage;
    MEDIA_DEBUG_LOG("SurfaceListener::OnBufferAvailable(), testName_: %{public}s, surfaceType_: %{public}d",
                    testName_, surfaceType_);
    OHOS::sptr<OHOS::SurfaceBuffer> buffer = nullptr;
    if (surface_ == nullptr) {
        MEDIA_ERR_LOG("OnBufferAvailable:surface_ is null");
        return;
    }
    surface_->AcquireBuffer(buffer, flushFence, timestamp, damage);
    if (buffer != nullptr) {
        char* addr = static_cast<char *>(buffer->GetVirAddr());
        int32_t size = buffer->GetSize();

        switch (surfaceType_) {
            case SurfaceType::PREVIEW:
                if (previewIndex_ % TestUtils::PREVIEW_SKIP_FRAMES == 0
                    && TestUtils::SaveYUV(addr, size, surfaceType_) != CAMERA_OK) {
                    MEDIA_ERR_LOG("Failed to save buffer");
                    previewIndex_ = 0;
                }
                previewIndex_++;
                break;

            case SurfaceType::SECOND_PREVIEW:
                if (secondPreviewIndex_ % TestUtils::PREVIEW_SKIP_FRAMES == 0
                    && TestUtils::SaveYUV(addr, size, surfaceType_) != CAMERA_OK) {
                    MEDIA_ERR_LOG("Failed to save buffer");
                    secondPreviewIndex_ = 0;
                }
                secondPreviewIndex_++;
                break;

            case SurfaceType::PHOTO:
                if (TestUtils::SaveYUV(addr, size, surfaceType_) != CAMERA_OK) {
                    MEDIA_ERR_LOG("Failed to save buffer");
                }
                break;

            case SurfaceType::VIDEO:
                if (fd_ == -1 && (TestUtils::SaveVideoFile(nullptr, 0, VideoSaveMode::CREATE, fd_) != CAMERA_OK)) {
                    MEDIA_ERR_LOG("Failed to Create video file");
                }
                if (TestUtils::SaveVideoFile(addr, size, VideoSaveMode::APPEND, fd_) != CAMERA_OK) {
                    MEDIA_ERR_LOG("Failed to save buffer");
                }
                break;

            default:
                MEDIA_ERR_LOG("Unexpected type");
                break;
        }
        surface_->ReleaseBuffer(buffer, -1);
    } else {
        MEDIA_ERR_LOG("AcquireBuffer failed!");
    }
}
} // namespace CameraStandard
} // namespace OHOS