/*
* -------------------------------------------------------------------------
*  This file is part of the Vision SDK project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* Vision SDK is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
*           http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
 * Description: Opencv Basic Drawing Unit.
 * Author: MindX SDK
 * Create: 2021
 * History: NA
 */

#include "OpencvOsdCpuKernel.h"
#include "cpu_types.h"

namespace {
const char *OPENCV_OSD = "OpencvOsd";
const int MAX_THICKNESS = 32767;
const int XY_SHIFT = 16;
}

namespace aicpu {
uint32_t OpencvOsdCpuKernel::OpencvCircle(cv::Mat &mat, const CpuKernelContext &ctx)
{
    Tensor *inputTensor4 = ctx.Input(0x4);
    auto inputData4 = inputTensor4->GetData();
    auto shape4 = inputTensor4->GetTensorShape();
    auto circleSize = shape4->GetDimSize(0);
    for (int i = 0; i < circleSize; i++) {
        auto color = cv::Scalar(((int32_t *)inputData4)[0xA * i + 0], ((int32_t *)inputData4)[0xA * i + 0x1],
            ((int32_t *)inputData4)[0xA * i + 0x2]);
        auto thickness = ((int32_t *)inputData4)[0xA * i + 0x4];
        if (thickness > MAX_THICKNESS) {
            return INT_MAX;
        }
        auto lineType = ((int32_t *)inputData4)[0xA * i + 0x5];
        if ((lineType != 0) && (lineType != cv::LINE_4) && (lineType != cv::LINE_8) && (lineType != cv::LINE_AA)) {
            return INT_MAX;
        }
        auto shift = ((int32_t *)inputData4)[0xA * i + 0x6];
        if (shift < 0 || shift > XY_SHIFT) {
            return INT_MAX;
        }
        auto radius = ((int32_t *)inputData4)[0xA * i + 0x7];
        if (radius < 0) {
            return INT_MAX;
        }
        auto center =
            cv::Point(((int32_t *)inputData4)[i * 0xA + 0x8], ((int32_t *)inputData4)[i * 0xA + 0x9]);
        cv::circle(mat, center, radius, color, thickness, lineType, shift);
    }
    return 0;
}

uint32_t OpencvOsdCpuKernel::OpencvRectangle(cv::Mat &mat, const CpuKernelContext &ctx)
{
    Tensor *inputTensor2 = ctx.Input(0x2);
    auto inputData2 = inputTensor2->GetData();
    auto shape2 = inputTensor2->GetTensorShape();
    auto rectSize = shape2->GetDimSize(0);
    for (int i = 0; i < rectSize; i++) {
        auto color = cv::Scalar(((int32_t *)inputData2)[0xB * i + 0], ((int32_t *)inputData2)[0xB * i + 0x1],
            ((int32_t *)inputData2)[0xB * i + 0x2]);
        auto thickness = ((int32_t *)inputData2)[0xB * i + 0x4];
        if (thickness > MAX_THICKNESS) {
            return INT_MAX;
        }
        auto lineType = ((int32_t *)inputData2)[0xB * i + 0x5];
        if ((lineType != 0) && (lineType != cv::LINE_4) && (lineType != cv::LINE_8) && (lineType != cv::LINE_AA)) {
            return INT_MAX;
        }
        auto shift = ((int32_t *)inputData2)[0xB * i + 0x6];
        if (shift < 0 || shift > XY_SHIFT) {
            return INT_MAX;
        }
        auto lefttop =
            cv::Point(((int32_t *)inputData2)[0xB * i + 0x7], ((int32_t *)inputData2)[0xB * i + 0x8]);
        auto rightbottom =
            cv::Point(((int32_t *)inputData2)[0xB * i + 0x9], ((int32_t *)inputData2)[0xB * i + 0xA]);
        cv::rectangle(mat, lefttop, rightbottom, color, thickness, lineType, shift);
    }
    return 0;
}

uint32_t OpencvOsdCpuKernel::OpencvPutText(cv::Mat &mat, const CpuKernelContext &ctx)
{
    Tensor *inputTensor3 = ctx.Input(0x3);
    auto inputData3 = inputTensor3->GetData();
    auto shape3 = inputTensor3->GetTensorShape();
    auto txtSize = shape3->GetDimSize(0);
    for (int i = 0; i < txtSize; i++) {
        auto txtColor = cv::Scalar(((int32_t *)inputData3)[i * 0xE + 0],
            ((int32_t *)inputData3)[i * 0xE + 0x1], ((int32_t *)inputData3)[i * 0xE + 0x2]);
        auto fontScale = ((double *)inputData3)[i * 0x7 + 0x2];
        auto txtThickness = ((int32_t *)inputData3)[i * 0xE + 0x8];
        if (txtThickness <= 0 || txtThickness > MAX_THICKNESS) {
            return INT_MAX;
        }
        auto lineType = ((int32_t *)inputData3)[i * 0xE + 0x9];
        if ((lineType != 0) && (lineType != cv::LINE_4) && (lineType != cv::LINE_8) && (lineType != cv::LINE_AA)) {
            return INT_MAX;
        }
        auto fontFace = ((int32_t *)inputData3)[i * 0xE + 0xA];
        auto bottomLeftOrigin = ((int32_t *)inputData3)[i * 0xE + 0xB];
        bool flag = (bottomLeftOrigin == 0) ? false : true;
        if ((char *)(((uint64_t *)inputData3)[i * 0x7 + 0x3]) != nullptr) {
            std::string txt((char *)(((uint64_t *)inputData3)[i * 0x7 + 0x3]));
            auto point =
                cv::Point(((int32_t *)inputData3)[i * 0xE + 0xC], ((int32_t *)inputData3)[i * 0xE + 0xD]);
            cv::putText(mat, txt, point, fontFace, fontScale, txtColor, txtThickness, lineType, flag);
        }
    }
    return 0;
}

uint32_t OpencvOsdCpuKernel::OpencvLine(cv::Mat &mat, const CpuKernelContext &ctx)
{
    Tensor *inputTensor5 = ctx.Input(0x5);
    auto inputData5 = inputTensor5->GetData();
    auto shape5 = inputTensor5->GetTensorShape();
    auto lineSize = shape5->GetDimSize(0);
    for (int i = 0; i < lineSize; i++) {
        auto color = cv::Scalar(((int32_t *)inputData5)[0xB * i + 0], ((int32_t *)inputData5)[0xB * i + 0x1],
            ((int32_t *)inputData5)[0xB * i + 0x2]);
        auto thickness = ((int32_t *)inputData5)[0xB * i + 0x4];
        if (thickness <= 0 || thickness > MAX_THICKNESS) {
            return INT_MAX;
        }
        auto lineType = ((int32_t *)inputData5)[0xB * i + 0x5];
        if ((lineType != 0) && (lineType != cv::LINE_4) && (lineType != cv::LINE_8) && (lineType != cv::LINE_AA)) {
            return INT_MAX;
        }
        auto shift = ((int32_t *)inputData5)[0xB * i + 0x6];
        if (shift < 0 || shift > XY_SHIFT) {
            return INT_MAX;
        }
        auto point1 =
            cv::Point(((int32_t *)inputData5)[i * 0xB + 0x7], ((int32_t *)inputData5)[i * 0xB + 0x8]);
        auto point2 =
            cv::Point(((int32_t *)inputData5)[i * 0xB + 0x9], ((int32_t *)inputData5)[i * 0xB + 0xA]);
        cv::line(mat, point1, point2, color, thickness, lineType, shift);
    }
    return 0;
}

uint32_t OpencvOsdCpuKernel::SetOutputData(cv::Mat &mat, CpuKernelContext &ctx)
{
    Tensor *outputTensor = ctx.Output(0);
    if (outputTensor == nullptr) {
        return INT_MAX;
    }
    auto outputData = outputTensor->GetData();
    if (outputData == nullptr) {
        return INT_MAX;
    }
    mat.data = (uchar*)outputData;
    return 0;
}

bool OpencvOsdCpuKernel::CheckOutputSize(const CpuKernelContext &ctx)
{
    Tensor *outputTensor = ctx.Output(0);
    if (outputTensor == nullptr) {
        return false;
    }
    auto outShape = outputTensor->GetTensorShape();
    if (outShape == nullptr) {
        return false;
    }
    auto outputSize = outShape->GetDimSize(0);

    Tensor *inputTensor = ctx.Input(0);
    auto inputShape = inputTensor->GetTensorShape();
    auto inputSize = inputShape->GetDimSize(0);

    Tensor *inputTensor1 = ctx.Input(0x1);
    auto inputData1 = inputTensor1->GetData();
    if ((((uint32_t *)inputData1)[0x1] * ((uint32_t *)inputData1)[0] * 0x3 != outputSize) ||
        (outputSize != inputSize * 0x2)) {
        return false;
    }
    return true;
}

bool OpencvOsdCpuKernel::CheckParamsNull(const CpuKernelContext &ctx)
{
    for (uint32_t i = 0x0; i <= 0x5; i++) {
        Tensor *inputTensor = ctx.Input(i);
        if (inputTensor == nullptr) {
            return false;
        }
        auto inputData = inputTensor->GetData();
        if (inputData == nullptr) {
            return false;
        }
        auto shape = inputTensor->GetTensorShape();
        if (shape == nullptr) {
            return false;
        }
    }
    return true;
}

uint32_t OpencvOsdCpuKernel::Compute(CpuKernelContext &ctx)
{
    if (!CheckParamsNull(ctx)) {
        return INT_MAX;
    }
    if (!CheckOutputSize(ctx)) {
        return INT_MAX;
    }
    Tensor *inputTensor = ctx.Input(0);
    auto inputData = inputTensor->GetData();
    Tensor *inputTensor1 = ctx.Input(0x1);
    auto inputData1 = inputTensor1->GetData();
    cv::Mat srcNV12Mat(((uint32_t *)inputData1)[0x1] * 0x3 / 0x2, ((uint32_t *)inputData1)[0], CV_8UC1,
                       inputData);
    cv::Mat mat(((uint32_t *)inputData1)[0x1], ((uint32_t *)inputData1)[0], CV_8UC3);

    uint32_t ret = SetOutputData(mat, ctx);
    if (ret != 0) {
        return ret;
    }
    cv::cvtColor(srcNV12Mat, mat, cv::COLOR_YUV2BGR_NV12);

    ret = OpencvRectangle(mat, ctx);
    if (ret != 0) {
        return ret;
    }
    ret = OpencvCircle(mat, ctx);
    if (ret != 0) {
        return ret;
    }
    ret = OpencvLine(mat, ctx);
    if (ret != 0) {
        return ret;
    }
    ret = OpencvPutText(mat, ctx);
    if (ret != 0) {
        return ret;
    }
    return 0;
}

REGISTER_CPU_KERNEL(OPENCV_OSD, OpencvOsdCpuKernel);
} // namespace aicpu