// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build windows

package windriver

import (
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"syscall"
	"unsafe"
)

func mkbitmap(size image.Point) (syscall.Handle, *byte, error) {
	bi := _BITMAPINFO{
		Header: _BITMAPINFOHEADER{
			Size:        uint32(unsafe.Sizeof(_BITMAPINFOHEADER{})),
			Width:       int32(size.X),
			Height:      -int32(size.Y), // negative height to force top-down drawing
			Planes:      1,
			BitCount:    32,
			Compression: _BI_RGB,
			SizeImage:   uint32(size.X * size.Y * 4),
		},
	}

	var ppvBits *byte
	bitmap, err := _CreateDIBSection(0, &bi, _DIB_RGB_COLORS, &ppvBits, 0, 0)
	if err != nil {
		return 0, nil, err
	}
	return bitmap, ppvBits, nil
}

var blendOverFunc = _BLENDFUNCTION{
	BlendOp:             _AC_SRC_OVER,
	BlendFlags:          0,
	SourceConstantAlpha: 255,           // only use per-pixel alphas
	AlphaFormat:         _AC_SRC_ALPHA, // premultiplied
}

func copyBitmapToDC(dc syscall.Handle, dr image.Rectangle, src syscall.Handle, sr image.Rectangle, op draw.Op) (retErr error) {
	memdc, err := _CreateCompatibleDC(dc)
	if err != nil {
		return err
	}
	defer _DeleteDC(memdc)

	prev, err := _SelectObject(memdc, src)
	if err != nil {
		return err
	}
	defer func() {
		_, err2 := _SelectObject(memdc, prev)
		if retErr == nil {
			retErr = err2
		}
	}()

	if _GetDeviceCaps(dc, _SHADEBLENDCAPS) == _SB_NONE {
		// This output device does not support blending capabilities,
		// so the subsequent output is incorrect, but is the best we
		// can do on systems that do not support AlphaBlend.
		op = draw.Src
	}

	switch op {
	case draw.Src:
		return _StretchBlt(dc, int32(dr.Min.X), int32(dr.Min.Y), int32(dr.Dx()), int32(dr.Dy()),
			memdc, int32(sr.Min.X), int32(sr.Min.Y), int32(sr.Dx()), int32(sr.Dy()), _SRCCOPY)
	case draw.Over:
		return _AlphaBlend(dc, int32(dr.Min.X), int32(dr.Min.Y), int32(dr.Dx()), int32(dr.Dy()),
			memdc, int32(sr.Min.X), int32(sr.Min.Y), int32(sr.Dx()), int32(sr.Dy()), blendOverFunc.ToUintptr())
	default:
		return fmt.Errorf("windriver: invalid draw operation %v", op)
	}
}

func fill(dc syscall.Handle, dr image.Rectangle, c color.Color, op draw.Op) error {
	r, g, b, a := c.RGBA()
	r >>= 8
	g >>= 8
	b >>= 8
	a >>= 8

	if op == draw.Src {
		color := _RGB(byte(r), byte(g), byte(b))
		brush, err := _CreateSolidBrush(color)
		if err != nil {
			return err
		}
		defer _DeleteObject(brush)

		rect := _RECT{
			Left:   int32(dr.Min.X),
			Top:    int32(dr.Min.Y),
			Right:  int32(dr.Max.X),
			Bottom: int32(dr.Max.Y),
		}
		return _FillRect(dc, &rect, brush)
	}

	// AlphaBlend will stretch the input image (using StretchBlt's
	// COLORONCOLOR mode) to fill the output rectangle. Testing
	// this shows that the result appears to be the same as if we had
	// used a MxN bitmap instead.
	sr := image.Rect(0, 0, 1, 1)
	bitmap, bitvalues, err := mkbitmap(sr.Size())
	if err != nil {
		return err
	}
	defer _DeleteObject(bitmap) // TODO handle error?

	color := _COLORREF((a << 24) | (r << 16) | (g << 8) | b)
	*(*_COLORREF)(unsafe.Pointer(bitvalues)) = color

	return copyBitmapToDC(dc, dr, bitmap, sr, draw.Over)
}
