/*
 * Copyright (c) 2020-2021 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 "components/ui_qrcode.h"
#include "gfx_utils/graphic_log.h"
#include "securec.h"
#include "common/qrcodegen/qrcode_generator.h"
#include "engines/gfx/gfx_engine_manager.h"

namespace OHOS {
UIQrcode::UIQrcode()
    : width_(0), needDraw_(false), backgroundColor_(Color::White()), qrColor_(Color::Black()), qrcodeVal_(nullptr)
{
    style_ = &(StyleDefault::GetBackgroundTransparentStyle());
    imageInfo_ = {};
}

UIQrcode::~UIQrcode()
{
    if (qrcodeVal_ != nullptr) {
        UIFree(qrcodeVal_);
        qrcodeVal_ = nullptr;
    }

    if (imageInfo_.data != nullptr) {
        ImageCacheFree(imageInfo_);
        imageInfo_.data = nullptr;
    }

    if (qrImage_ != nullptr) {
        QrcodeImageFree(static_cast<QrcodeImage *>(qrImage_));
        qrImage_ = nullptr;
    }
}

void UIQrcode::SetQrcodeInfo(const char* val, ColorType backgroundColor, ColorType qrColor)
{
    if (val == nullptr) {
        GRAPHIC_LOGE("UIQrcode::SetQrcodeInfo val is null!\n");
        return;
    }
    uint32_t length = static_cast<uint32_t>(strlen(val));
    if ((length > QRCODE_VAL_MAX) || (length == 0)) {
        GRAPHIC_LOGE("UIQrcode::SetQrcodeInfo val length is equal 0 or greater than QRCODE_VAL_MAX!\n");
        return;
    }
    backgroundColor_ = backgroundColor;
    qrColor_ = qrColor;
    SetQrcodeVal(val, length);
    RefreshQrcode();
}

void UIQrcode::RefreshQrcode()
{
    Invalidate();
    if (!needDraw_) {
        needDraw_ = true;
    }
}

void UIQrcode::SetWidth(int16_t width)
{
    if (GetWidth() != width) {
        UIView::SetWidth(width);
        RefreshQrcode();
    }
}

void UIQrcode::SetHeight(int16_t height)
{
    if (GetHeight() != height) {
        UIView::SetHeight(height);
        RefreshQrcode();
    }
}

void UIQrcode::ReMeasure()
{
    if (!needDraw_) {
        return;
    }
    needDraw_ = false;
    if (qrcodeVal_ == nullptr) {
        GRAPHIC_LOGE("UIQrcode::ReMeasure qrcodeVal_ is null!\n");
        return;
    }
    if (qrImage_ != nullptr) {
        QrcodeImageFree(static_cast<QrcodeImage *>(qrImage_));
        qrImage_ = nullptr;
    }
    qrImage_ = QrcodeImageEncodeString(qrcodeVal_, QRCODE_ECC_MEDIUM);
    if (qrImage_ == nullptr) {
        GRAPHIC_LOGE("UIQrcode::ReMeasure qrImage_ is null!\n");
        return;
    }
    SetImageInfo();
    SetSrc(&imageInfo_);
}

void UIQrcode::SetQrcodeVal(const char* qrcodeVal, uint32_t length)
{
    if (qrcodeVal_ != nullptr) {
        UIFree(qrcodeVal_);
        qrcodeVal_ = nullptr;
    }

    uint32_t len = static_cast<uint32_t>(length + 1);
    qrcodeVal_ = static_cast<char*>(UIMalloc(len));
    if (qrcodeVal_ != nullptr) {
        if (memcpy_s(qrcodeVal_, len, qrcodeVal, len) != EOK) {
            UIFree(reinterpret_cast<void*>(qrcodeVal_));
            qrcodeVal_ = nullptr;
        }
    }
}

void UIQrcode::SetImageInfo()
{
    int16_t width = GetWidth();
    int16_t height = GetHeight();
    width_ = (width >= height) ? height : width;
    if (width_ < static_cast<QrcodeImage *>(qrImage_)->width) {
        GRAPHIC_LOGE("UIQrcode::SetImageInfo width is less than the minimum qrImage_ width!\n");
        return;
    }
    imageInfo_.header.width = width;
    imageInfo_.header.height = height;
    imageInfo_.header.colorMode = ARGB8888;
    width = BaseGfxEngine::GetInstance()->AlignImageWidth(width);
    imageInfo_.dataSize = width * imageInfo_.header.height * QRCODE_FACTOR_NUM;
    if (imageInfo_.data != nullptr) {
        ImageCacheFree(imageInfo_);
        imageInfo_.data = nullptr;
    }
    imageInfo_.data = reinterpret_cast<uint8_t*>(ImageCacheMalloc(imageInfo_));
    if (imageInfo_.data == nullptr) {
        GRAPHIC_LOGE("UIQrcode::SetImageInfo imageInfo_.data is null!\n");
        return;
    }
    GenerateQrCode();
}

void UIQrcode::GenerateQrCode()
{
    FillQrCodeBackgroundColor();

    FillQrCodeColor();
}

void UIQrcode::FillQrCodeColor()
{
    int32_t qrWidth = static_cast<QrcodeImage *>(qrImage_)->width;
    if (qrWidth <= 0) {
        GRAPHIC_LOGE("UIQrcode::FillQrCodeColor generated qrImage_ size is less or equal 0!\n");
        return;
    }
    int16_t width = imageInfo_.header.width;
    int16_t height = imageInfo_.header.height;
    uint16_t outFilePixelPrescaler = width_ / qrWidth;
    int32_t offsetX = (width - outFilePixelPrescaler * qrWidth) / 2;    // 2: half
    int32_t offsetY = (height - outFilePixelPrescaler * qrWidth) / 2;   // 2: half

    width = BaseGfxEngine::GetInstance()->AlignImageWidth(width);
    uint8_t* destData = nullptr;
    int64_t oneLinePixel = width * QRCODE_FACTOR_NUM * outFilePixelPrescaler;
    int64_t oneLineOffsetPixel = (offsetY * width * QRCODE_FACTOR_NUM) + (offsetX * QRCODE_FACTOR_NUM);
    uint8_t *sourceData = static_cast<QrcodeImage *>(qrImage_)->data;
    for (int32_t y = 0; y < qrWidth; ++y) {
        destData = const_cast<uint8_t*>(imageInfo_.data) + (oneLinePixel * y) + oneLineOffsetPixel;
        for (int32_t x = 0; x < qrWidth; ++x) {
            if ((*sourceData & 1) != 0) {
                GetDestData(destData, outFilePixelPrescaler);
            }
            destData += QRCODE_FACTOR_NUM * outFilePixelPrescaler;
            sourceData++;
        }
    }
}

void UIQrcode::FillQrCodeBackgroundColor()
{
    uint8_t* initColorData = const_cast<uint8_t*>(imageInfo_.data);
    *(initColorData + 0) = backgroundColor_.blue;  // 0: B channel
    *(initColorData + 1) = backgroundColor_.green; // 1: G channel
    *(initColorData + 2) = backgroundColor_.red;   // 2: R channel
    *(initColorData + 3) = OPA_OPAQUE;             // 3: Alpha channel

    uint32_t width = imageInfo_.header.width;
    width = BaseGfxEngine::GetInstance()->AlignImageWidth(width);

    uint8_t* tempColorData = initColorData;
    for (uint32_t col = 1; col < width; ++col) {
        initColorData += QRCODE_FACTOR_NUM;
        if (memcpy_s(initColorData, QRCODE_FACTOR_NUM, tempColorData, QRCODE_FACTOR_NUM) != EOK) {
            GRAPHIC_LOGE("UIQrcode::FillQrCodeBackgroundColor memcpy_s failed!\n");
            return;
        }
    }
    initColorData = tempColorData;
    int32_t deltaWidth = QRCODE_FACTOR_NUM * width;
    for (int16_t row = 1; row < imageInfo_.header.height; ++row) {
        initColorData += deltaWidth;
        if (memcpy_s(initColorData, deltaWidth, tempColorData, deltaWidth) != EOK) {
            GRAPHIC_LOGE("UIQrcode::FillQrCodeBackgroundColor memcpy_s failed!\n");
            return;
        }
    }
}

void UIQrcode::GetDestData(uint8_t* destData, int32_t outFilePixelPrescaler)
{
    uint32_t width = imageInfo_.header.width;
    width = BaseGfxEngine::GetInstance()->AlignImageWidth(width);

    for (int32_t x = 0; x < outFilePixelPrescaler; ++x) {
        uint8_t* tempData = destData + width * QRCODE_FACTOR_NUM * x;
        for (int32_t y = 0; y < outFilePixelPrescaler; ++y) {
            *(tempData + 0) = qrColor_.blue;  // 0: B channel
            *(tempData + 1) = qrColor_.green; // 1: G channel
            *(tempData + 2) = qrColor_.red;   // 2: R channel
            *(tempData + 3) = OPA_OPAQUE;     // 3: Alpha channel
            tempData += QRCODE_FACTOR_NUM;
        }
    }
}
} // namespace OHOS