/*
 * Copyright (c) 2020-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.
 */

#ifndef GRAPHIC_LITE_DRAW_UTILS_H
#define GRAPHIC_LITE_DRAW_UTILS_H

#include "common/text.h"
#include "font/ui_font_header.h"
#include "gfx_utils/color.h"
#include "gfx_utils/geometry2d.h"
#include "gfx_utils/graphic_buffer.h"
#include "gfx_utils/graphic_types.h"
#include "gfx_utils/list.h"
#include "gfx_utils/style.h"
#include "gfx_utils/transform.h"

namespace OHOS {
#define SWAP_INT16(x, y)    \
    do {                    \
        int16_t temp = (x); \
        (x) = (y);          \
        (y) = temp;         \
    } while (0)

#define SWAP_POINTS(x1, x2, y1, y2) \
    SWAP_INT16(x1, x2);             \
    SWAP_INT16(y1, y2);

// FixedPointed Related definition.
#define FIXED_NUM_1 1048576
#define FIXED_Q_NUM 20
#define FO_TRANS_FLOAT_TO_FIXED(f) (static_cast<int64_t>((f) * FIXED_NUM_1))
#define FO_TRANS_INTEGER_TO_FIXED(f) ((static_cast<int64_t>(f)) << FIXED_Q_NUM)
#define FO_DIV(n1, n2) ((static_cast<int64_t>(n1) << FIXED_Q_NUM) / (n2))
#define FO_TO_INTEGER(n) ((n) >= 0 ? ((n) >> FIXED_Q_NUM) : (((n) >> FIXED_Q_NUM) + 1))
#define FO_DECIMAL(n) ((n) >= 0 ? ((n) & (FIXED_NUM_1 - 1)) : ((n) | (-FIXED_NUM_1)))
#define FO_MUL(n1, n2) ((static_cast<int64_t>(n1) * (n2)) >> FIXED_Q_NUM)

struct EdgeSides {
    int16_t left;
    int16_t right;
    int16_t top;
    int16_t bottom;
};

struct LabelLineInfo {
    Point& pos;
    Point& offset;
    const Rect& mask;
    int16_t lineHeight;
    uint16_t lineLength;
    uint8_t shapingId;
    uint8_t opaScale;
    const Style& style;
    const char* text;
    uint16_t length;
    uint16_t start;
    uint16_t fontId;
    uint8_t fontSize;
    uint8_t txtFlag;
    UITextLanguageDirect direct;
    uint32_t* codePoints;
    bool baseLine;
#if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE
    TextStyle* textStyles;
#endif
    List<BackgroundColor>*  backgroundColor;
    List<ForegroundColor>*  foregroundColor;
    List<LineBackgroundColor>*  linebackgroundColor;
    SpannableString* spannableString;
    uint16_t ellipsisOssetY;
};

struct LabelLetterInfo {
    const Point& pos;
    Rect mask;
    const ColorType& color;
    OpacityType opa;
    int8_t offsetX;
    int8_t offsetY;

    const uint32_t& letter;
    UITextLanguageDirect direct;
    uint16_t fontId;
    uint8_t shapingId;
    uint8_t fontSize;
#if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE
    TextStyle textStyle;
#endif
    bool baseLine;
    int16_t letterSpace_;
    int16_t lineSpace_;
    bool havebackgroundColor;
    ColorType& backgroundColor;
    bool haveLineBackgroundColor;
    ColorType& lineBackgroundColor;
};

struct TransformInitState {
#if defined(ENABLE_FIXED_POINT) && ENABLE_FIXED_POINT
    // parameters below are Q15 fixed-point number
    int64_t verticalU;
    int64_t verticalV;
    int64_t duHorizon;
    int64_t dvHorizon;
    int64_t duVertical;
    int64_t dvVertical;
    // parameters above are Q15 fixed-point number
#else
    float verticalU;
    float verticalV;
    float duHorizon;
    float dvHorizon;
    float duVertical;
    float dvVertical;
#endif
};

struct TriangleEdge {
    TriangleEdge() {}
    TriangleEdge(int16_t x1, int16_t y1, int16_t duInt, int16_t dvInt);
    ~TriangleEdge();
#if defined(ENABLE_FIXED_POINT) && ENABLE_FIXED_POINT
    // parameters below are Q15 fixed-point number
    int64_t curX = 0;
    int64_t curY = 0;
    int64_t du = 0;
    int64_t dv = 0;
    // parameters above are Q15 fixed-point number
#else
    float curX = 0.0f;
    float curY = 0.0f;
    float du = 0.0f;
    float dv = 0.0f;
#endif
};

struct TriangleTransformDataInfo {
    const TransformDataInfo& info;
    Point p1;
    Point p2;
    Point p3;
    bool isRightPart;
    bool ignoreJunctionPoint;
};

struct TriangleScanInfo {
    int16_t yMin;
    int16_t yMax;
    TriangleEdge& edge1;
    TriangleEdge& edge2;
    uint8_t* screenBuffer;
    uint8_t bufferPxSize;
    const ColorType& color;
    const OpacityType opaScale;
    TransformInitState& init;
    uint16_t screenBufferWidth;
    uint8_t pixelSize;
    const int32_t srcLineWidth;
    const TransformDataInfo& info;
    const Rect& mask;
    bool isRightPart;
    bool ignoreJunctionPoint;
    Matrix3<float> matrix;
};

struct TrianglePartInfo {
    const Rect& mask;
    const TransformMap& transMap;
    const Point& position;
    TriangleEdge& edge1;
    TriangleEdge& edge2;
    int16_t yMin;
    int16_t yMax;
    const TransformDataInfo& info;
    const ColorType& color;
    const OpacityType opaScale;
    bool isRightPart;
    bool ignoreJunctionPoint;
};

enum {
    IMG_SRC_VARIABLE,
    IMG_SRC_FILE,
    IMG_SRC_UNKNOWN,
};

class DrawUtils : public HeapBase {
public:
    static DrawUtils* GetInstance();

    void DrawColorArea(BufferInfo& gfxDstBuffer, const Rect& area, const Rect& mask,
                       const ColorType& color, OpacityType opa) const;

    void DrawColorAreaBySides(BufferInfo& gfxDstBuffer, const Rect& mask, const ColorType& color,
                              OpacityType opa, const EdgeSides& sides) const;

    void DrawPixel(BufferInfo& gfxDstBuffer, int16_t x, int16_t y, const Rect& mask,
                   const ColorType& color, OpacityType opa) const;

    void DrawColorLetter(BufferInfo& gfxDstBuffer,
                         const LabelLetterInfo& letterInfo,
                         uint8_t* fontMap,
                         GlyphNode node,
                         int16_t lineHeight) const;
    void DrawNormalLetter(BufferInfo& gfxDstBuffer,
                          const LabelLetterInfo& letterInfo,
                          uint8_t* fontMap,
                          GlyphNode node,
                          uint8_t maxLetterSize) const;

    void DrawLetter(BufferInfo& gfxDstBuffer,
                    const uint8_t* fontMap,
                    const Rect& fontRect,
                    const Rect& subRect,
                    const uint8_t fontWeight,
                    const ColorType& color,
                    const OpacityType opa) const;

    void DrawLetter(BufferInfo& gfxDstBuffer, const Rect& subRect, LetterDataInfo& dataInfo) const;

    void DrawImage(BufferInfo& gfxDstBuffer, const Rect& area, const Rect& mask,
                   const uint8_t* image, OpacityType opa, uint8_t pxBitSize, ColorMode colorMode) const;

    static void
        GetXAxisErrForJunctionLine(bool ignoreJunctionPoint, bool isRightPart, int16_t& xMinErr, int16_t& xMaxErr);

    static void GetTransformInitState(const TransformMap& transMap,
                                      const Point& position,
                                      const Rect& trans,
                                      TransformInitState& init);

    static void DrawTriangleTransform(BufferInfo& gfxDstBuffer,
                                      const Rect& mask,
                                      const Point& position,
                                      const ColorType& color,
                                      OpacityType opaScale,
                                      const TransformMap& transMap,
                                      const TriangleTransformDataInfo& dataInfo);

    void DrawTransform(BufferInfo& gfxDstBuffer,
                       const Rect& mask,
                       const Point& position,
                       const ColorType& color,
                       OpacityType opaScale,
                       const TransformMap& transMap,
                       const TransformDataInfo& dataInfo) const;

    void DrawTranspantArea(BufferInfo& gfxDstBuffer, const Rect& rect, const Rect& mask);

    void DrawWithBuffer(BufferInfo& gfxDstBuffer, const Rect& rect, const Rect& mask, const ColorType* colorBuf);

    static uint8_t GetPxSizeByColorMode(uint8_t colorMode);

    static uint8_t GetByteSizeByColorMode(uint8_t colorMode);

    static OpacityType GetMixOpacity(OpacityType opa1, OpacityType opa2)
    {
        // 8: Shift right 8 bits
        OpacityType opaMix = (opa1 == OPA_OPAQUE) ? opa2 : ((static_cast<uint16_t>(opa1) * opa2) >> 8);
        return opaMix;
    }

    void DrawAdjPixelInLine(BufferInfo& gfxDstBuffer,
                            int16_t x1,
                            int16_t y1,
                            int16_t x2,
                            int16_t y2,
                            const Rect& mask,
                            const ColorType& color,
                            OpacityType opa,
                            uint16_t w) const;

    void DrawPixelInLine(BufferInfo& gfxDstBuffer, int16_t x, int16_t y, const Rect& mask,
                         const ColorType& color, OpacityType opa, uint16_t w) const;

    void DrawVerPixelInLine(BufferInfo& gfxDstBuffer,
                            int16_t x,
                            int16_t y,
                            int8_t dir,
                            const Rect& mask,
                            const ColorType& color,
                            OpacityType opa,
                            uint16_t weight) const;

    void DrawHorPixelInLine(BufferInfo& gfxDstBuffer,
                            int16_t x,
                            int16_t y,
                            int8_t dir,
                            const Rect& mask,
                            const ColorType& color,
                            OpacityType opa,
                            uint16_t weight) const;

    void BlendWithSoftWare(const uint8_t* src1,
                           const Rect& srcRect,
                           uint32_t srcStride,
                           uint32_t srcLineNumber,
                           ColorMode srcMode,
                           uint32_t color,
                           OpacityType opa,
                           uint8_t* dst,
                           uint32_t destStride,
                           ColorMode destMode,
                           uint32_t x,
                           uint32_t y) const;

    void FillAreaWithSoftWare(BufferInfo& gfxDstBuffer,
                              const Rect& fillArea,
                              const ColorType& color,
                              const OpacityType& opa) const;
#ifdef ARM_NEON_OPT

    void BlendLerpPix(uint8_t* color, uint8_t red, uint8_t green, uint8_t blue,
                      uint8_t alpha, uint8_t cover);
    void BlendLerpPix(uint8_t* color, uint8_t red, uint8_t green, uint8_t blue,
                      uint8_t alpha);
    void BlendLerpPix(uint8_t* dstColors, uint8_t* srcColors, uint8_t srcCover);
    void BlendLerpPix(uint8_t* dstColors, uint8_t* srcColors, uint8_t* srcCovers);
    void BlendLerpPix(uint8_t* color, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha, uint8_t* covers);
    void BlendPreLerpPix(uint8_t* color, uint8_t red, uint8_t green, uint8_t blue,
                         uint8_t alpha, uint8_t cover);
    void BlendPreLerpPix(uint8_t* color, uint8_t red, uint8_t green, uint8_t blue,
                         uint8_t alpha);
    void BlendPreLerpPix(uint8_t* dstColors, uint8_t* srcColors, uint8_t srcCover);
    void BlendPreLerpPix(uint8_t* dstColors, uint8_t* srcColors, uint8_t* srcCovers);
    void BlendPreLerpPix(uint8_t* color, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha, uint8_t* covers);
#endif
private:
    using DrawTriangleTransformFuc = void (*)(const TriangleScanInfo& triangle, const ColorMode bufferMode);

    static void DrawTriangleTrueColorNearest(const TriangleScanInfo& triangle, const ColorMode bufferMode);

    static void DrawTriangleAlphaBilinear(const TriangleScanInfo& triangle, const ColorMode bufferMode);

    static void DrawTriangleTrueColorBilinear565(const TriangleScanInfo& triangle, const ColorMode bufferMode);

    static void DrawTriangleTrueColorBilinear888(const TriangleScanInfo& triangle, const ColorMode bufferMode);

    static void Draw3DTriangleTrueColorBilinear8888(const TriangleScanInfo& triangle, const ColorMode bufferMode);

    static void DrawTriangleTrueColorBilinear8888(const TriangleScanInfo& triangle, const ColorMode bufferMode);

    inline static void StepToNextLine(TriangleEdge& edg1, TriangleEdge& edg2);

    static void DrawTriangleTransformPart(BufferInfo& gfxDstBuffer, const TrianglePartInfo& part);

    static OpacityType GetPxAlphaForAlphaImg(const TransformDataInfo& dataInfo, const Point& point);

    static void AddBorderToImageData(TransformDataInfo& newDataInfo, ImageInfo& imageinfo);

    static void UpdateTransMap(int16_t width, int16_t height, TransformMap& transMap);

    void FillArea(BufferInfo& gfxDstBuffer, const Rect& rect, const Rect& mask,
                  bool isTransparent, const ColorType* colorBuf);

#ifdef ARM_NEON_OPT
    void SetDestAndSrc(ColorMode& srcMode, ColorMode& destMode, uint32_t height, uint8_t* src,
                       uint32_t width, OpacityType opa, uint8_t* dest, uint32_t destStride,
                       uint32_t srcStride, uint8_t destByteSize, uint8_t srcByteSize) const;
#endif

    void SetFucInfo(BufferInfo& gfxDstBuffer, const TrianglePartInfo& part,
                    uint8_t* screenBuffer, TransformInitState& init);
    void SetPartEdge(BufferInfo& gfxDstBuffer, const TriangleTransformDataInfo& triangleInfo,
                     TriangleEdge& edge1, TriangleEdge& edge2, bool p3IsInRight,
                     const Rect& mask, uint8_t yErr, TrianglePartInfo& part) const;
};
} // namespace OHOS
#endif // GRAPHIC_LITE_DRAW_UTILS_H