/*
 * uxtheme Double-buffered Drawing API
 *
 * Copyright (C) 2008 Reece H. Dunn
 * Copyright 2017 Nikolay Sivov for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"

#include <stdlib.h>
#include <stdarg.h>

#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "wingdi.h"
#include "vfwmsgs.h"
#include "uxtheme.h"

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(uxtheme);

struct paintbuffer
{
    HDC targetdc;
    HDC memorydc;
    RECT rect;
    void *bits;
};

static void free_paintbuffer(struct paintbuffer *buffer)
{
    DeleteDC(buffer->memorydc);
    HeapFree(GetProcessHeap(), 0, buffer);
}

static struct paintbuffer *get_buffer_obj(HPAINTBUFFER handle)
{
    if (!handle)
        return NULL;
    return handle;
}

/***********************************************************************
 *      BufferedPaintInit                                  (UXTHEME.@)
 */
HRESULT WINAPI BufferedPaintInit(VOID)
{
    FIXME("Stub ()\n");
    return S_OK;
}

/***********************************************************************
 *      BufferedPaintUnInit                                (UXTHEME.@)
 */
HRESULT WINAPI BufferedPaintUnInit(VOID)
{
    FIXME("Stub ()\n");
    return S_OK;
}

/***********************************************************************
 *      BeginBufferedPaint                                 (UXTHEME.@)
 */
HPAINTBUFFER WINAPI BeginBufferedPaint(HDC targetdc, const RECT *rect,
        BP_BUFFERFORMAT format, BP_PAINTPARAMS *params, HDC *retdc)
{
    char bmibuf[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
    BITMAPINFO *bmi = (BITMAPINFO *)bmibuf;
    struct paintbuffer *buffer;
    HBITMAP hbm;

    TRACE("(%p %s %d %p %p)\n", targetdc, wine_dbgstr_rect(rect), format,
          params, retdc);

    if (retdc)
        *retdc = NULL;

    if (!targetdc || IsRectEmpty(rect))
        return NULL;

    if (params)
        FIXME("painting parameters are ignored\n");

    buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(*buffer));
    buffer->targetdc = targetdc;
    buffer->rect = *rect;
    buffer->memorydc = CreateCompatibleDC(targetdc);

    switch (format)
    {
    case BPBF_COMPATIBLEBITMAP:
        hbm = CreateCompatibleBitmap(buffer->memorydc, rect->right - rect->left, rect->bottom - rect->top);
        buffer->bits = NULL;
        break;
    case BPBF_DIB:
    case BPBF_TOPDOWNDIB:
    case BPBF_TOPDOWNMONODIB:
        /* create DIB section */
        memset(bmi, 0, sizeof(bmibuf));
        bmi->bmiHeader.biSize = sizeof(bmi->bmiHeader);
        bmi->bmiHeader.biHeight = format == BPBF_DIB ? rect->bottom - rect->top :
                -(rect->bottom - rect->top);
        bmi->bmiHeader.biWidth = rect->right - rect->left;
        bmi->bmiHeader.biBitCount = format == BPBF_TOPDOWNMONODIB ? 1 : 32;
        bmi->bmiHeader.biPlanes = 1;
        bmi->bmiHeader.biCompression = BI_RGB;
        hbm = CreateDIBSection(buffer->memorydc, bmi, DIB_RGB_COLORS, &buffer->bits, NULL, 0);
        break;
    default:
        WARN("Unknown buffer format %d\n", format);
        free_paintbuffer(buffer);
        return NULL;
    }

    if (!hbm)
    {
        WARN("Failed to create buffer bitmap\n");
        free_paintbuffer(buffer);
        return NULL;
    }

    DeleteObject(SelectObject(buffer->memorydc, hbm));

    *retdc = buffer->memorydc;

    return (HPAINTBUFFER)buffer;
}

/***********************************************************************
 *      EndBufferedPaint                                   (UXTHEME.@)
 */
HRESULT WINAPI EndBufferedPaint(HPAINTBUFFER bufferhandle, BOOL update)
{
    struct paintbuffer *buffer = get_buffer_obj(bufferhandle);

    TRACE("(%p %d)\n", bufferhandle, update);

    if (!buffer)
        return E_INVALIDARG;

    if (update)
    {
        if (!BitBlt(buffer->targetdc, buffer->rect.left, buffer->rect.top,
                buffer->rect.right - buffer->rect.left, buffer->rect.bottom - buffer->rect.top,
                buffer->memorydc, 0, 0, SRCCOPY))
        {
            WARN("BitBlt() failed\n");
        }
    }

    free_paintbuffer(buffer);
    return S_OK;
}

/***********************************************************************
 *      BufferedPaintClear                                 (UXTHEME.@)
 */
HRESULT WINAPI BufferedPaintClear(HPAINTBUFFER hBufferedPaint, const RECT *prc)
{
    FIXME("Stub (%p %p)\n", hBufferedPaint, prc);
    return E_NOTIMPL;
}

/***********************************************************************
 *      BufferedPaintSetAlpha                              (UXTHEME.@)
 */
HRESULT WINAPI BufferedPaintSetAlpha(HPAINTBUFFER hBufferedPaint, const RECT *prc, BYTE alpha)
{
    FIXME("Stub (%p %p %u)\n", hBufferedPaint, prc, alpha);
    return E_NOTIMPL;
}

/***********************************************************************
 *      GetBufferedPaintBits                               (UXTHEME.@)
 */
HRESULT WINAPI GetBufferedPaintBits(HPAINTBUFFER bufferhandle, RGBQUAD **bits, int *width)
{
    struct paintbuffer *buffer = get_buffer_obj(bufferhandle);

    TRACE("(%p %p %p)\n", buffer, bits, width);

    if (!bits || !width)
        return E_POINTER;

    if (!buffer || !buffer->bits)
        return E_FAIL;

    *bits = buffer->bits;
    *width = buffer->rect.right - buffer->rect.left;

    return S_OK;
}

/***********************************************************************
 *      GetBufferedPaintDC                                 (UXTHEME.@)
 */
HDC WINAPI GetBufferedPaintDC(HPAINTBUFFER bufferhandle)
{
    struct paintbuffer *buffer = get_buffer_obj(bufferhandle);

    TRACE("(%p)\n", buffer);

    return buffer ? buffer->memorydc : NULL;
}

/***********************************************************************
 *      GetBufferedPaintTargetDC                           (UXTHEME.@)
 */
HDC WINAPI GetBufferedPaintTargetDC(HPAINTBUFFER bufferhandle)
{
    struct paintbuffer *buffer = get_buffer_obj(bufferhandle);

    TRACE("(%p)\n", buffer);

    return buffer ? buffer->targetdc : NULL;
}

/***********************************************************************
 *      GetBufferedPaintTargetRect                         (UXTHEME.@)
 */
HRESULT WINAPI GetBufferedPaintTargetRect(HPAINTBUFFER bufferhandle, RECT *rect)
{
    struct paintbuffer *buffer = get_buffer_obj(bufferhandle);

    TRACE("(%p %p)\n", buffer, rect);

    if (!rect)
        return E_POINTER;

    if (!buffer)
        return E_FAIL;

    *rect = buffer->rect;
    return S_OK;
}

/***********************************************************************
 *      BeginBufferedAnimation                             (UXTHEME.@)
 */
HANIMATIONBUFFER WINAPI BeginBufferedAnimation(HWND hwnd, HDC hdcTarget, const RECT *rcTarget,
                                               BP_BUFFERFORMAT dwFormat, BP_PAINTPARAMS *pPaintParams,
                                               BP_ANIMATIONPARAMS *pAnimationParams, HDC *phdcFrom,
                                               HDC *phdcTo)
{
    FIXME("Stub (%p %p %p %u %p %p %p %p)\n", hwnd, hdcTarget, rcTarget, dwFormat,
          pPaintParams, pAnimationParams, phdcFrom, phdcTo);

    return NULL;
}

/***********************************************************************
 *      BufferedPaintRenderAnimation                       (UXTHEME.@)
 */
BOOL WINAPI BufferedPaintRenderAnimation(HWND hwnd, HDC hdcTarget)
{
    FIXME("Stub (%p %p)\n", hwnd, hdcTarget);

    return FALSE;
}

/***********************************************************************
 *      BufferedPaintStopAllAnimations                     (UXTHEME.@)
 */
HRESULT WINAPI BufferedPaintStopAllAnimations(HWND hwnd)
{
    FIXME("Stub (%p)\n", hwnd);

    return E_NOTIMPL;
}

/***********************************************************************
 *      EndBufferedAnimation                               (UXTHEME.@)
 */
HRESULT WINAPI EndBufferedAnimation(HANIMATIONBUFFER hbpAnimation, BOOL fUpdateTarget)
{
    FIXME("Stub (%p %u)\n", hbpAnimation, fUpdateTarget);

    return E_NOTIMPL;
}