/***************************************************************************
 *   crengine-ng                                                           *
 *   Copyright (C) 2007-2014 Vadim Lopatin <coolreader.org@gmail.com>      *
 *   Copyright (C) 2011 Konstantin Potapov <pkbo@users.sourceforge.net>    *
 *   Copyright (C) 2015,2016 Yifei(Frank) ZHU <fredyifei@gmail.com>        *
 *   Copyright (C) 2019,2021 poire-z <poire-z@users.noreply.github.com>    *
 *   Copyright (C) 2019,2021 NiLuJe <ninuje@gmail.com>                     *
 *   Copyright (C) 2021,2022 Aleksey Chernov <valexlin@gmail.com>          *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU General Public License           *
 *   as published by the Free Software Foundation; either version 2        *
 *   of the License, or (at your option) any later version.                *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the Free Software           *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,            *
 *   MA 02110-1301, USA.                                                   *
 ***************************************************************************/

/**
 * \file lvdrawbuf.h
 * \brief Abstract drawing buffer
 */

#ifndef __LVDRAWBUF_H_INCLUDED__
#define __LVDRAWBUF_H_INCLUDED__

#include <lvcacheableobject.h>
#include <lvimagesource.h>

#if !defined(__SYMBIAN32__) && defined(_WIN32) && !defined(QT_GL)
extern "C" {
#include <windows.h>
}
#elif __SYMBIAN32__
#include <e32base.h>
#include <w32std.h>
#endif

enum cr_rotate_angle_t
{
    CR_ROTATE_ANGLE_0 = 0,
    CR_ROTATE_ANGLE_90,
    CR_ROTATE_ANGLE_180,
    CR_ROTATE_ANGLE_270
};

enum DrawBufPixelFormat
{
    DRAW_BUF_1_BPP = 1,   /// 1 bpp, 8 pixels per byte packed
    DRAW_BUF_2_BPP = 2,   /// 2 bpp, 4 pixels per byte packed
    DRAW_BUF_3_BPP = 3,   /// 3 bpp, 1 pixel per byte, higher 3 bits are significant
    DRAW_BUF_4_BPP = 4,   /// 4 bpp, 1 pixel per byte, higher 4 bits are significant
    DRAW_BUF_8_BPP = 8,   /// 8 bpp, 1 pixel per byte, all 8 bits are significant
    DRAW_BUF_16_BPP = 16, /// color 16bit RGB 565
    DRAW_BUF_32_BPP = 32  /// color 32bit RGB 888
};

// Font bitmap format
enum FontBmpPixelFormat
{
    BMP_PIXEL_FORMAT_MONO,  /// 1 bpp w/b, 8 pixels per byte packed, MSB
    BMP_PIXEL_FORMAT_GRAY,  /// 8 bpp gray, 1 pixel per byte, all 8 bits are significant
    BMP_PIXEL_FORMAT_GRAY2, /// 2 bpp gray, 4 pixels per byte packed
    BMP_PIXEL_FORMAT_GRAY4, /// 4 bpp gray, 2 pixel per byte packed
    BMP_PIXEL_FORMAT_RGB,   /// color 24bit RGB 888
    BMP_PIXEL_FORMAT_BGR,   /// color 24bit BGR 888
    BMP_PIXEL_FORMAT_RGB_V, /// color 24bit RGB 888 (vertical order)
    BMP_PIXEL_FORMAT_BGR_V, /// color 24bit BGR 888 (vertical order)
    BMP_PIXEL_FORMAT_BGRA,  /// color 32bit BGRA 8888
};

class GLDrawBuf; // workaround for no-rtti builds

/// Abstract drawing buffer
class LVDrawBuf: public CacheableObject
{
public:
    // GL draw buffer support
    /// GL draw buffer compatibility - requires this call before any drawing
    virtual void beforeDrawing() { }
    /// GL draw buffer compatibility - requires this call after any drawing
    virtual void afterDrawing() { }

    // tiles support
    /// returns true if drawing buffer is tiled
    virtual bool isTiled() const {
        return false;
    }
    /// returns tile width (or just width if no tiles)
    virtual int tileWidth() const {
        return GetWidth();
    }
    /// returns tile height (or just height if no tiles)
    virtual int tileHeight() const {
        return GetHeight();
    }
    /// returns tile drawbuf for tiled image, returns this for non tiled draw buffer
    virtual LVDrawBuf* getTile(int x, int y) {
        CR_UNUSED2(x, y);
        return this;
    }
    /// returns number of tiles in row
    virtual int getXtiles() const {
        return 1;
    }
    /// returns number of tiles in column
    virtual int getYtiles() const {
        return 1;
    }

    /// returns tile rectangle
    virtual void getTileRect(lvRect& rc, int x, int y) const {
        CR_UNUSED2(x, y);
        rc.left = rc.top = 0;
        rc.right = GetWidth();
        rc.bottom = GetHeight();
    }

    /// rotates buffer contents by specified angle
    virtual void Rotate(cr_rotate_angle_t angle) = 0;
    /// returns white pixel value
    virtual lUInt32 GetWhiteColor() const = 0;
    /// returns black pixel value
    virtual lUInt32 GetBlackColor() const = 0;
    /// returns current background color
    virtual lUInt32 GetBackgroundColor() const = 0;
    /// sets current background color
    virtual void SetBackgroundColor(lUInt32 cl) = 0;
    /// returns current text color
    virtual lUInt32 GetTextColor() const = 0;
    /// sets current text color
    virtual void SetTextColor(lUInt32 cl) = 0;
    /// gets clip rect
    virtual void GetClipRect(lvRect* clipRect) const = 0;
    /// sets clip rect
    virtual void SetClipRect(const lvRect* clipRect) = 0;
    /// wants to be fed hidden content (only LVInkMeasurementDrawBuf may return true)
    virtual bool WantsHiddenContent() const {
        return false;
    }
    /// set to true for drawing in Paged mode, false for Scroll mode
    virtual void setHidePartialGlyphs(bool hide) = 0;
    /// set to true to invert images only (so they get inverted back to normal by nightmode)
    virtual void setInvertImages(bool invert) = 0;
    /// set to true to enforce dithering (only relevant for 8bpp Gray drawBuf)
    virtual void setDitherImages(bool dither) = 0;
    /// set to true to switch to a more costly smooth scaler instead of nearest neighbor
    virtual void setSmoothScalingImages(bool smooth) = 0;
    /// invert image
    virtual void Invert() = 0;
    /// get buffer width, pixels
    virtual int GetWidth() const = 0;
    /// get buffer height, pixels
    virtual int GetHeight() const = 0;
    /// get buffer bits per pixel
    virtual int GetBitsPerPixel() const = 0;
    /// get row size (bytes)
    virtual int GetRowSize() const = 0;
    /// fills buffer with specified color
    virtual void Clear(lUInt32 color) = 0;
    /// get pixel value
    virtual lUInt32 GetPixel(int x, int y) const = 0;
    /// get average pixel value for area (coordinates are fixed floating points *16)
    virtual lUInt32 GetAvgColor(lvRect& rc16) const = 0;
    /// get linearly interpolated pixel value (coordinates are fixed floating points *16)
    virtual lUInt32 GetInterpolatedColor(int x16, int y16) const = 0;
    /// draw gradient filled rectangle with colors for top-left, top-right, bottom-right, bottom-left
    virtual void GradientRect(int x0, int y0, int x1, int y1, lUInt32 color1, lUInt32 color2, lUInt32 color3, lUInt32 color4) {
        CR_UNUSED8(x0, x1, y0, y1, color1, color2, color3, color4);
    }
    /// fills rectangle with specified color
    virtual void FillRect(int x0, int y0, int x1, int y1, lUInt32 color) = 0;
    /// draw frame
    inline void DrawFrame(const lvRect& rc, lUInt32 color, int width = 1) {
        FillRect(rc.left, rc.top, rc.right, rc.top + width, color);
        FillRect(rc.left, rc.bottom - width, rc.right, rc.bottom, color);
        FillRect(rc.left, rc.top + width, rc.left + width, rc.bottom - width, color);
        FillRect(rc.right - width, rc.top + width, rc.right, rc.bottom - width, color);
    }
    /// fills rectangle with specified color
    inline void FillRect(const lvRect& rc, lUInt32 color) {
        FillRect(rc.left, rc.top, rc.right, rc.bottom, color);
    }
    /// draws rectangle with specified color
    inline void Rect(int x0, int y0, int x1, int y1, lUInt32 color) {
        FillRect(x0, y0, x1 - 1, y0 + 1, color);
        FillRect(x0, y0, x0 + 1, y1 - 1, color);
        FillRect(x1 - 1, y0, x1, y1, color);
        FillRect(x0, y1 - 1, x1, y1, color);
    }
    /// draws rectangle with specified width and color
    inline void Rect(int x0, int y0, int x1, int y1, int borderWidth, lUInt32 color) {
        FillRect(x0, y0, x1 - 1, y0 + borderWidth, color);
        FillRect(x0, y0, x0 + borderWidth, y1 - 1, color);
        FillRect(x1 - borderWidth, y0, x1, y1, color);
        FillRect(x0, y1 - borderWidth, x1, y1, color);
    }
    /// draws rounded rectangle with specified line width, rounding radius, and color
    void RoundRect(int x0, int y0, int x1, int y1, int borderWidth, int radius, lUInt32 color, int cornerFlags = 0x0F) {
        FillRect(x0 + ((cornerFlags & 1) ? radius : 0), y0, x1 - 1 - ((cornerFlags & 2) ? radius : 0), y0 + borderWidth, color);
        FillRect(x0, y0 + ((cornerFlags & 1) ? radius : 0), x0 + borderWidth, y1 - 1 - ((cornerFlags & 4) ? radius : 0), color);
        FillRect(x1 - borderWidth, y0 + ((cornerFlags & 2) ? radius : 0), x1, y1 - ((cornerFlags & 8) ? radius : 0), color);
        FillRect(x0 + ((cornerFlags & 4) ? radius : 0), y1 - borderWidth, x1 - ((cornerFlags & 8) ? radius : 0), y1, color);
        // TODO: draw rounded corners
    }
    /// draws rectangle with specified color
    inline void Rect(const lvRect& rc, lUInt32 color) {
        Rect(rc.left, rc.top, rc.right, rc.bottom, color);
    }
    /// draws rectangle with specified color
    inline void Rect(const lvRect& rc, int borderWidth, lUInt32 color) {
        Rect(rc.left, rc.top, rc.right, rc.bottom, borderWidth, color);
    }
    /// fills rectangle with pattern
    virtual void FillRectPattern(int x0, int y0, int x1, int y1, lUInt32 color0, lUInt32 color1, const lUInt8* pattern) = 0;
    /// inverts image in specified rectangle
    virtual void InvertRect(int x0, int y0, int x1, int y1) = 0;
    /// sets new size
    virtual void Resize(int dx, int dy) = 0;
    /// blend font bitmap using specified palette
    virtual void BlendBitmap(int x, int y, const lUInt8* bitmap, FontBmpPixelFormat bitmap_fmt, int width, int height, int bmp_pitch, const lUInt32* palette) = 0;
    /// draws image
    virtual void Draw(LVImageSourceRef img, int x, int y, int width, int height, bool dither = true) = 0;
    /// draws part of source image, possible rescaled
    virtual void Draw(LVImageSourceRef img, int x, int y, int width, int height, int srcx, int srcy, int srcwidth, int srcheight, bool dither = true) {
        CR_UNUSED10(img, x, y, width, height, srcx, srcy, srcwidth, srcheight, dither);
    }
    /// for GL buf only - rotated drawing
    virtual void DrawRotated(LVImageSourceRef img, int x, int y, int width, int height, int rotationAngle) {
        Draw(img, x, y, width, height);
        CR_UNUSED(rotationAngle);
    }
    /// draws buffer content to another buffer doing color conversion if necessary
    virtual void DrawTo(LVDrawBuf* buf, int x, int y, int options, const lUInt32* palette) = 0;
    // draws buffer on top of another buffer to implement background
    virtual void DrawOnTop(LVDrawBuf* buf, int x, int y) = 0;
    /// draws rescaled buffer content to another buffer doing color conversion if necessary
    virtual void DrawRescaled(LVDrawBuf* src, int x, int y, int dx, int dy, int options) = 0;
    /// draws rescaled buffer content to another buffer doing color conversion if necessary
    virtual void DrawFragment(LVDrawBuf* src, int srcx, int srcy, int srcdx, int srcdy, int x, int y, int dx, int dy, int options) {
        CR_UNUSED10(src, srcx, srcy, srcdx, srcdy, x, y, dx, dy, options);
    }
    /// draw lines
    virtual void DrawLine(int x0, int y0, int x1, int y1, lUInt32 color0, int length1, int length2, int direction) = 0;
#if !defined(__SYMBIAN32__) && defined(_WIN32) && !defined(QT_GL)
    /// draws buffer content to another buffer doing color conversion if necessary
    virtual void DrawTo(HDC dc, int x, int y, int options, const lUInt32* palette) = 0;
#endif
    /// returns scanline pointer
    virtual lUInt8* GetScanLine(int y) const = 0;

    virtual int getAlpha() const {
        return 0;
    }
    virtual void setAlpha(int alpha) {
        CR_UNUSED(alpha);
    }
    virtual lUInt32 applyAlpha(lUInt32 cl) {
        return cl;
    }

    /// virtual destructor
    virtual ~LVDrawBuf() { }
    virtual GLDrawBuf* asGLDrawBuf() {
        return NULL;
    }
};

typedef LVRef<LVDrawBuf> LVDrawBufRef;

#endif
