/*
 * Copyright (c) 2024 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 "drawing_demo.h"

#include <sstream>

#include "display_manager.h"
#include "test_case_factory.h"
#include "test_case/test_common.h"

using namespace OHOS::Rosen;

namespace OHOS {
namespace Rosen {
DrawingDemo::DrawingDemo(int argc, char* argv[])
{
    argc_ = argc;
    for (int i = 0; i < argc_; i++) {
        std::string str = argv[i];
        argv_.emplace_back(str);
    }
};

DrawingDemo::~DrawingDemo()
{
    if (window_) {
        window_->Hide();
        window_->Destroy();
    }
};

int DrawingDemo::Test(TestDisplayCanvas* canvas)
{
    TestCommon::Log("eg: drawing_demo function [cpu | gpu] {caseName?} {displayTime?}");
    TestCommon::Log("eg: drawing_demo performance [cpu | gpu] caseName count {displayTime?}");
    if (argc_ <= INDEX_DRAWING_TYPE) {
        return RET_PARAM_ERR;
    }

    testType_ = argv_[INDEX_TEST_TYPE];
    drawingType_ = argv_[INDEX_DRAWING_TYPE];
    if (testType_ == "function") {
        if (drawingType_ == "cpu") {
            return TestFunction(FUNCTION_CPU);
        } else if (drawingType_ == "gpu") {
            return TestFunction(FUNCTION_GPU_UPSCREEN);
        }
    } else if (testType_ == "performance") {
        if (drawingType_ == "cpu") {
            return TestPerformance(canvas, PERFORMANCE_CPU);
        } else if (drawingType_ == "gpu") {
            return TestPerformance(canvas, PERFORMANCE_GPU_UPSCREEN);
        }
    }
    return RET_PARAM_ERR;
}

int DrawingDemo::InitWindow()
{
    rsUiDirector_ = RSUIDirector::Create();
    if (rsUiDirector_ == nullptr) {
        TestCommon::Log("Failed to create rsUiDirector_");
        return RET_FAILED;
    }
    rsUiDirector_->Init();
    RSTransaction::FlushImplicitTransaction();
    sleep(1);
    auto surfaceNode = window_->GetSurfaceNode();
    rsUiDirector_->SetRSSurfaceNode(surfaceNode);

    rootNode_ = RSRootNode::Create();
    if (rootNode_ == nullptr) {
        TestCommon::Log("Failed to create rootNode");
        return RET_FAILED;
    }
    rootNode_->SetBounds(0, 0, rect_.width_, rect_.height_);
    rootNode_->SetFrame(0, 0, rect_.width_, rect_.height_);
    rootNode_->SetBackgroundColor(Drawing::Color::COLOR_WHITE);
    rsUiDirector_->SetRoot(rootNode_->GetId());

    canvasNode_ = RSCanvasNode::Create();
    if (canvasNode_ == nullptr) {
        TestCommon::Log("Failed to create canvasNode");
        return RET_FAILED;
    }
    canvasNode_->SetFrame(0, 0, rect_.width_, rect_.height_);
    rootNode_->AddChild(canvasNode_, -1);
    rsUiDirector_->SendMessages();
    sleep(1);
    return RET_OK;
}

int DrawingDemo::CreateWindow()
{
    TestCommon::Log("create window start");
    sptr<Display> display = DisplayManager::GetInstance().GetDefaultDisplay();
    if (display == nullptr) {
        TestCommon::Log("Failed to get display!");
        return RET_FAILED;
    }
    int32_t defaultWidth = display->GetWidth();
    int32_t defaultHeight = display->GetHeight();
    std::ostringstream stream;
    stream << "display: " << defaultWidth << "*" << defaultHeight;
    TestCommon::Log(stream.str());

    std::string demoName = "drawing_demo";
    RSSystemProperties::GetUniRenderEnabled();
    sptr<WindowOption> option = new WindowOption();
    option->SetWindowType(WindowType::WINDOW_TYPE_FLOAT);
    option->SetWindowMode(WindowMode::WINDOW_MODE_FLOATING);
    option->SetWindowRect({ 0, 0, defaultWidth, defaultHeight });

    int count = 0;
    do {
        if (window_ != nullptr) {
            window_->Hide();
            window_->Destroy();
        }
        window_ = Window::Create(demoName, option);
        if (window_ == nullptr) {
            TestCommon::Log("Failed to create window");
            return RET_FAILED;
        }

        window_->Show();
        usleep(SLEEP_TIME);
        rect_ = window_->GetRect();
        count++;
    } while (rect_.width_ == 0 && rect_.height_ == 0 && count < MAX_TRY_NUMBER);

    if (rect_.width_ == 0 || rect_.height_ == 0) {
        TestCommon::Log("Failed to create window, rect is 0!");
        return RET_FAILED;
    }
    
    int ret = InitWindow();
    if (ret != RET_OK) {
        return ret;
    }
    stream.str("");
    stream << "create window success: " << rect_.width_ << " * " << rect_.height_;
    TestCommon::Log(stream.str());
    return RET_OK;
}

int DrawingDemo::GetFunctionalParam(std::unordered_map<std::string, std::function<std::shared_ptr<TestBase>()>>& map)
{
    // drawing_demo functional {cpu | gpu} {caseName?} {displayTime?}
    if (argc_ <= INDEX_CASE_NAME) {
        caseName_ = ALL_TAST_CASE;
        return RET_OK;
    }

    caseName_ = argv_[INDEX_CASE_NAME];
    if (map.find(caseName_) == map.end()) {
        TestCommon::Log("TestCase not exist, please try again. All testcase:");
        for (auto iter : map) {
            TestCommon::Log(iter.first);
        }
        return RET_PARAM_ERR;
    }

    if (argc_ > INDEX_FUNCTION_TIME) {
        std::string displayTimeStr = argv_[INDEX_FUNCTION_TIME];
        displayTime_ = std::stoi(displayTimeStr);
    }
    return RET_OK;
}

int DrawingDemo::TestFunction(int type)
{
    auto map = TestCaseFactory::GetFunctionCase();
    int ret = GetFunctionalParam(map);
    if (ret != RET_OK) {
        return ret;
    }

    TestCommon::Log("TestFunction start!");
    ret = CreateWindow();
    if (ret != RET_OK) {
        return ret;
    }
    for (auto iter : map) {
        if ((caseName_ != ALL_TAST_CASE) && (caseName_ != iter.first)) {
            continue;
        }

        auto canvas = canvasNode_->BeginRecording(rect_.width_, rect_.height_);
        auto testCase = iter.second();
        if (testCase == nullptr) {
            std::ostringstream stream;
            stream << "Failed to create testcase:" << iter.first;
            TestCommon::Log(stream.str());
            continue;
        }

        testCase->SetCanvas((TestDisplayCanvas*)(canvas));
        if (type == FUNCTION_CPU) {
            testCase->SetFileName(iter.first);
            testCase->TestFunctionCpu();
        } else if (type == FUNCTION_GPU_UPSCREEN) {
            testCase->TestFunctionGpuUpScreen();
        }
        canvasNode_->FinishRecording();
        rsUiDirector_->SendMessages();
        if (type == FUNCTION_GPU_UPSCREEN) {
            sleep(2); // Wait for 2 seconds to make the screen display normal
            (void)TestCommon::PackingPixmap(window_->Snapshot(), iter.first);
        }
    }
    
    int time = (caseName_ != ALL_TAST_CASE ? displayTime_ : 1);
    std::ostringstream stream;
    stream << "wait: " << time << "s";
    TestCommon::Log(stream.str());
    sleep(time);
    TestCommon::Log("TestFunction end!");
    return RET_OK;
}

int DrawingDemo::GetPerformanceParam(std::shared_ptr<TestBase>& testCase)
{
    // drawing_demo performance {cpu | gpu} caseName count {displaytime?}
    if (argc_ <= INDEX_COUNT) {
        return RET_PARAM_ERR;
    }

    caseName_ = argv_[INDEX_CASE_NAME];
    auto map = TestCaseFactory::GetPerformanceCase();
    auto iter = map.find(caseName_);
    if (iter == map.end()) {
        TestCommon::Log("TestCase not exist, please try again. All testcase:");
        for (auto iter : map) {
            TestCommon::Log(iter.first);
        }
        return RET_PARAM_ERR;
    }
    testCase = iter->second();
    if (testCase == nullptr) {
        TestCommon::Log("Failed to create testcase");
        return RET_FAILED;
    }

    std::string testCountStr = argv_[INDEX_COUNT];
    testCount_ = std::stoi(testCountStr);
    
    if (argc_ > INDEX_PERFORMANCE_TIME) {
        std::string displayTimeStr = argv_[INDEX_PERFORMANCE_TIME];
        displayTime_ = std::stoi(displayTimeStr);
    }
    return RET_OK;
}

int DrawingDemo::TestPerformance(TestDisplayCanvas* canvasExt, int type)
{
    std::shared_ptr<TestBase> testCase = nullptr;
    int ret = GetPerformanceParam(testCase);
    if (ret != RET_OK || testCase == nullptr) {
        return ret;
    }

    TestCommon::Log("TestPerformance start!");
    TestDisplayCanvas* canvas = nullptr;
    if (canvasExt == nullptr) {
        ret = CreateWindow();
        if (ret != RET_OK) {
            return ret;
        }
        canvas = reinterpret_cast<TestDisplayCanvas*>(canvasNode_->BeginRecording(rect_.width_, rect_.height_));
        if (canvas == nullptr) {
            TestCommon::Log("Failed to get canvas");
            return RET_FAILED;
        }
    } else {
        canvas = canvasExt;
    }

    testCase->SetCanvas((TestDisplayCanvas*)(canvas));
    testCase->SetTestCount(testCount_);

    if (type == PERFORMANCE_CPU) {
        testCase->TestPerformanceCpu();
    } else if (type == PERFORMANCE_GPU_UPSCREEN) {
        testCase->TestPerformanceGpuUpScreen();
    }

    if (canvasExt == nullptr) {
        canvasNode_->FinishRecording();
        rsUiDirector_->SendMessages();

        std::ostringstream stream;
        stream << "wait: " << displayTime_ << "s";
        TestCommon::Log(stream.str());
        sleep(displayTime_);
    }

    TestCommon::Log("TestPerformance end!");
    return RET_OK;
}
} // namespace Rosen
} // namespace OHOS

int main(int argc, char* argv[])
{
    std::shared_ptr<DrawingDemo> drawingDemo = std::make_shared<DrawingDemo>(argc, argv);
    if (drawingDemo == nullptr) {
        TestCommon::Log("Failed to create DrawingDemo");
        return 0;
    }
    int ret = drawingDemo->Test(nullptr);
    if (ret == RET_PARAM_ERR) {
        TestCommon::Log("Invalid parameter, please confirm");
    }
    return ret;
}

#ifdef __cplusplus
extern "C" {
#endif
int DrawingTest(void* canvas, int argc, char* argv[])
{
    if (canvas == nullptr) {
        TestCommon::Log("canvas is nullptr");
        return 0;
    }
    std::shared_ptr<DrawingDemo> drawingDemo = std::make_shared<DrawingDemo>(argc, argv);
    if (drawingDemo == nullptr) {
        TestCommon::Log("Failed to create DrawingDemo");
        return 0;
    }
    int ret = drawingDemo->Test((TestDisplayCanvas*)(canvas));
    if (ret == RET_PARAM_ERR) {
        TestCommon::Log("Invalid parameter, please confirm");
    }
    return ret;
}
#ifdef __cplusplus
}
#endif