/*
 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.

 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef VDRAWHELPER_H
#define VDRAWHELPER_H

#include <memory>
#include <array>
#include "assert.h"
#include "vbitmap.h"
#include "vbrush.h"
#include "vrect.h"
#include "vrle.h"

V_USE_NAMESPACE

struct VSpanData;
struct Operator;

struct RenderFunc
{
    using Color = void (*)(uint32_t *dest, int length, uint32_t color, uint32_t alpha);
    using Src   = void (*)(uint32_t *dest, int length, const uint32_t *src, uint32_t alpha);
    enum class Type {
        Invalid,
        Color,
        Src,
    };
    RenderFunc() = default;
    RenderFunc(Type t, Color f):type_(t), color_(f){assert(t == Type::Color);}
    RenderFunc(Type t, Src f):type_(t), src_(f){ assert(t == Type::Src);}

    Type   type_{Type::Invalid};
    union {
        Color color_;
        Src   src_;
    };
};

class RenderFuncTable
{
public:
    RenderFuncTable();
    RenderFunc::Color color(BlendMode mode) const
    {
        return colorTable[uint32_t(mode)].color_;
    }
    RenderFunc::Src   src(BlendMode mode) const
    {
        return srcTable[uint32_t(mode)].src_;
    }
private:
    void neon();
    void sse();
    void updateColor(BlendMode mode, RenderFunc::Color f)
    {
        colorTable[uint32_t(mode)] = {RenderFunc::Type::Color, f};
    }
    void updateSrc(BlendMode mode, RenderFunc::Src f)
    {
        srcTable[uint32_t(mode)] = {RenderFunc::Type::Src, f};
    }
private:
    std::array<RenderFunc, uint32_t(BlendMode::Last)> colorTable;
    std::array<RenderFunc, uint32_t(BlendMode::Last)> srcTable;
};

typedef void (*SourceFetchProc)(uint32_t *buffer, const Operator *o,
                                const VSpanData *data, int y, int x,
                                int length);
typedef void (*ProcessRleSpan)(size_t count, const VRle::Span *spans,
                               void *userData);

extern void memfill32(uint32_t *dest, uint32_t value, int count);

struct LinearGradientValues {
    float dx;
    float dy;
    float l;
    float off;
};

struct RadialGradientValues {
    float dx;
    float dy;
    float dr;
    float sqrfr;
    float a;
    float inv2a;
    bool  extended;
};

struct Operator {
    BlendMode                mode;
    SourceFetchProc          srcFetch;
    RenderFunc::Color        funcSolid;
    RenderFunc::Src          func;
    union {
        LinearGradientValues linear;
        RadialGradientValues radial;
    };
};

class VRasterBuffer {
public:
    VBitmap::Format prepare(const VBitmap *image);
    void            clear();

    void resetBuffer(int val = 0);

    inline uint8_t *scanLine(int y)
    {
        assert(y >= 0);
        assert(size_t(y) < mHeight);
        return mBuffer + y * mBytesPerLine;
    }
    uint32_t *pixelRef(int x, int y) const
    {
        return (uint32_t *)(mBuffer + y * mBytesPerLine + x * mBytesPerPixel);
    }

    size_t          width() const { return mWidth; }
    size_t          height() const { return mHeight; }
    size_t          bytesPerLine() const { return mBytesPerLine; }
    size_t          bytesPerPixel() const { return mBytesPerPixel; }
    VBitmap::Format format() const { return mFormat; }

private:
    VBitmap::Format mFormat{VBitmap::Format::ARGB32_Premultiplied};
    size_t          mWidth{0};
    size_t          mHeight{0};
    size_t          mBytesPerLine{0};
    size_t          mBytesPerPixel{0};
    mutable uint8_t *mBuffer{nullptr};
};

struct VGradientData {
    VGradient::Spread mSpread;
    struct Linear {
        float x1, y1, x2, y2;
    };
    struct Radial {
        float cx, cy, fx, fy, cradius, fradius;
    };
    union {
        Linear linear;
        Radial radial;
    };
    const uint32_t *mColorTable;
    bool            mColorTableAlpha;
};

struct VTextureData : public VRasterBuffer {
    uint32_t pixel(int x, int y) const { return *pixelRef(x, y); };
    uint8_t  alpha() const { return mAlpha; }
    void     setAlpha(uint8_t alpha) { mAlpha = alpha; }
    void     setClip(const VRect &clip);
    // clip rect
    int   left;
    int   right;
    int   top;
    int   bottom;
    bool  hasAlpha;
    uint8_t mAlpha;
};

struct VColorTable {
    uint32_t buffer32[VGradient::colorTableSize];
    bool     alpha{true};
};

struct VSpanData {
    enum class Type { None, Solid, LinearGradient, RadialGradient, Texture };

    void updateSpanFunc();
    void init(VRasterBuffer *image);
    void setup(const VBrush &brush, BlendMode mode = BlendMode::SrcOver,
               int alpha = 255);
    void setupMatrix(const VMatrix &matrix);

    VRect clipRect() const
    {
        return VRect(0, 0, mDrawableSize.width(), mDrawableSize.height());
    }

    void setDrawRegion(const VRect &region)
    {
        mOffset = VPoint(region.left(), region.top());
        mDrawableSize = VSize(region.width(), region.height());
    }

    uint32_t *buffer(int x, int y) const
    {
        return mRasterBuffer->pixelRef(x + mOffset.x(), y + mOffset.y());
    }
    void initTexture(const VBitmap *image, int alpha, const VRect &sourceRect);
    const VTextureData &texture() const { return mTexture; }

    BlendMode                          mBlendMode{BlendMode::SrcOver};
    VRasterBuffer *                    mRasterBuffer;
    ProcessRleSpan                     mBlendFunc;
    ProcessRleSpan                     mUnclippedBlendFunc;
    VSpanData::Type                    mType;
    std::shared_ptr<const VColorTable> mColorTable{nullptr};
    VPoint                             mOffset;  // offset to the subsurface
    VSize                              mDrawableSize;  // suburface size
    uint32_t                           mSolid;
    VGradientData                      mGradient;
    VTextureData                       mTexture;

    float m11, m12, m13, m21, m22, m23, m33, dx, dy;  // inverse xform matrix
    bool  fast_matrix{true};
    VMatrix::MatrixType transformType{VMatrix::MatrixType::None};
};

#define BYTE_MUL(c, a)                                  \
    ((((((c) >> 8) & 0x00ff00ff) * (a)) & 0xff00ff00) + \
     (((((c)&0x00ff00ff) * (a)) >> 8) & 0x00ff00ff))

inline constexpr int vRed(uint32_t c)
{
    return ((c >> 16) & 0xff);
}

inline constexpr int vGreen(uint32_t c)
{
    return ((c >> 8) & 0xff);
}

inline constexpr int vBlue(uint32_t c)
{
    return (c & 0xff);
}

inline constexpr int vAlpha(uint32_t c)
{
    return c >> 24;
}

static inline uint32_t interpolate_pixel(uint32_t x, uint32_t a, uint32_t y,
                                         uint32_t b)
{
    uint32_t t = (x & 0xff00ff) * a + (y & 0xff00ff) * b;
    t >>= 8;
    t &= 0xff00ff;
    x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b;
    x &= 0xff00ff00;
    x |= t;
    return x;
}

#endif  // QDRAWHELPER_P_H