/***************************************************************************
 *   crwx-ng                                                               *
 *   Copyright (C) 2008 Vadim Lopatin <coolreader.org@gmail.com>           *
 *   Copyright (C) 2023 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.                                                   *
 ***************************************************************************/

#include "crwxscreen.h"

void CRWxScreen::update(const lvRect& rc, bool full) {
    int dx = _canvas->GetWidth();
    int dy = _canvas->GetHeight();
    wxImage img(dx, dy, true);
    // wxImage is always 24-bit RGB data with pitch = width * 3.
    lUInt8* bits = (lUInt8*)img.GetData();
    int pitch = dx * 3;
    int bpp = _canvas->GetBitsPerPixel();
    switch (bpp) {
        case 1:
            convert1bppData_impl(bits, dx, dy, pitch);
            break;
        case 2:
            convert2bppData_impl(bits, dx, dy, pitch);
            break;
        case 3:
            convert3bppData_impl(bits, dx, dy, pitch);
            break;
        case 4:
            convert4bppData_impl(bits, dx, dy, pitch);
            break;
        case 8:
            convert8bppData_impl(bits, dx, dy, pitch);
            break;
        case 32:
            convert32bppData_impl(bits, dx, dy, pitch);
            break;
        default:
            CRLog::error("CRWxScreen: unsupported bpp %d", bpp);
    }
    // copy to bitmap
    wxBitmap bmp(img);
    _wxbitmap = bmp;
}

void CRWxScreen::convert1bppData_impl(lUInt8* dstData, int width, int height, int dstPitch) {
    // clang-format off
    static const lUInt8 palette[2][3] = {
        { 0x00, 0x00, 0x00 },
        { 0xff, 0xff, 0xff }
    };
    // clang-format on
    int dyy = _canvas->GetHeight();
    int dxx = _canvas->GetWidth();
    for (int y = 0; y < height && y < dyy; y++) {
        const lUInt8* srcLine = (const lUInt8*)_canvas->GetScanLine(y);
        lUInt8* dstLine = dstData + y * dstPitch;
        for (int x = 0; x < width && x < dxx; x++) {
            lUInt8 c = ((srcLine[x >> 3] >> ((7 - (x & 7))))) & 1;
            *dstLine++ = palette[c][0];
            *dstLine++ = palette[c][1];
            *dstLine++ = palette[c][2];
        }
    }
}

void CRWxScreen::convert2bppData_impl(lUInt8* dstData, int width, int height, int dstPitch) {
    // clang-format off
    static const lUInt8 palette[4][3] = {
        { 0x00, 0x00, 0x00 },
        { 0x55, 0x55, 0x55 },
        { 0xaa, 0xaa, 0xaa },
        { 0xff, 0xff, 0xff }
    };
    // clang-format on
    int dyy = _canvas->GetHeight();
    int dxx = _canvas->GetWidth();
    for (int y = 0; y < height && y < dyy; y++) {
        const lUInt8* srcLine = (const lUInt8*)_canvas->GetScanLine(y);
        lUInt8* dstLine = dstData + y * dstPitch;
        for (int x = 0; x < width && x < dxx; x++) {
            lUInt8 c = ((srcLine[x >> 2] >> ((3 - (x & 3)) << 1))) & 3;
            *dstLine++ = palette[c][0];
            *dstLine++ = palette[c][1];
            *dstLine++ = palette[c][2];
        }
    }
}

void CRWxScreen::convert3bppData_impl(lUInt8* dstData, int width, int height, int dstPitch) {
    int dyy = _canvas->GetHeight();
    int dxx = _canvas->GetWidth();
    // In crengine in a gray buffer with a 3-bit color depth,
    //  there is one pixel per byte
    for (int y = 0; y < height && y < dyy; y++) {
        const lUInt8* srcLine = (const lUInt8*)_canvas->GetScanLine(y);
        lUInt8* dstLine = dstData + y * dstPitch;
        for (int x = 0; x < width && x < dxx; x++) {
            lUInt8 c = (srcLine[x] >> 5) & 0x07;
            c *= 36;
            *dstLine++ = c;
            *dstLine++ = c;
            *dstLine++ = c;
        }
    }
}

void CRWxScreen::convert4bppData_impl(lUInt8* dstData, int width, int height, int dstPitch) {
    int dyy = _canvas->GetHeight();
    int dxx = _canvas->GetWidth();
    // In crengine in a gray buffer with a 4-bit color depth,
    //  there is one pixel per byte
    for (int y = 0; y < height && y < dyy; y++) {
        const lUInt8* srcLine = (const lUInt8*)_canvas->GetScanLine(y);
        lUInt8* dstLine = dstData + y * dstPitch;
        for (int x = 0; x < width && x < dxx; x++) {
            lUInt8 c = (srcLine[x] >> 4) & 0x0F;
            c *= 17;
            *dstLine++ = c;
            *dstLine++ = c;
            *dstLine++ = c;
        }
    }
}

void CRWxScreen::convert8bppData_impl(lUInt8* dstData, int width, int height, int dstPitch) {
    int dyy = _canvas->GetHeight();
    int dxx = _canvas->GetWidth();
    // Gray buffer: one pixel per byte
    for (int y = 0; y < height && y < dyy; y++) {
        const lUInt8* srcLine = (const lUInt8*)_canvas->GetScanLine(y);
        lUInt8* dstLine = dstData + y * dstPitch;
        for (int x = 0; x < width && x < dxx; x++) {
            lUInt8 c = srcLine[x];
            *dstLine++ = c;
            *dstLine++ = c;
            *dstLine++ = c;
        }
    }
}

void CRWxScreen::convert32bppData_impl(lUInt8* dstData, int width, int height, int dstPitch) {
    int dyy = _canvas->GetHeight();
    int dxx = _canvas->GetWidth();
    for (int y = 0; y < height && y < dyy; y++) {
        const lUInt32* srcLine = (const lUInt32*)_canvas->GetScanLine(y);
        unsigned char* dstLine = dstData + y * dstPitch;
        for (int x = 0; x < width && x < dxx; x++) {
            lUInt32 c = *srcLine++;
            *dstLine++ = (c >> 16) & 255;
            *dstLine++ = (c >> 8) & 255;
            *dstLine++ = (c >> 0) & 255;
        }
    }
}
