package roundimage

import (
	"math"

	"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
	"github.com/gotk3/gotk3/cairo"
	"github.com/gotk3/gotk3/gtk"
)

const (
	pi     = math.Pi
	circle = 2 * math.Pi
)

// Button implements a rounded button with a rounded image. This widget only
// supports a full circle for rounding.
type Button struct {
	*gtk.Button
	Image *Image
}

var roundButtonCSS = primitives.PrepareClassCSS("round-button", `
	.round-button {
		padding: 0;
		border-radius: 50%;
	}
`)

func NewButton() (*Button, error) {
	image, _ := NewImage(0)
	image.Show()

	b, _ := gtk.ButtonNew()
	b.SetImage(image)
	b.SetRelief(gtk.RELIEF_NONE)
	roundButtonCSS(b)

	return &Button{Button: b, Image: image}, nil
}

type Image struct {
	*gtk.Image
	Radius float64
}

// NewImage creates a new round image. If radius is 0, then it will be half the
// dimensions. If the radius is less than 0, then nothing is rounded.
func NewImage(radius float64) (*Image, error) {
	i, err := gtk.ImageNew()
	if err != nil {
		return nil, err
	}

	image := &Image{Image: i, Radius: radius}

	// Connect to the draw callback and clip the context.
	i.Connect("draw", image.drawer)

	return image, nil
}

func (i *Image) SetRadius(r float64) {
	i.Radius = r
}

func (i *Image) drawer(widget gtk.IWidget, cc *cairo.Context) bool {
	var w = float64(i.GetAllocatedWidth())
	var h = float64(i.GetAllocatedHeight())

	var min = w
	// Use the smallest side for radius calculation.
	if h < w {
		min = h
	}

	// Copy the variables in case we need to change them.
	var r = i.Radius

	switch {
	// If radius is less than 0, then don't round.
	case r < 0:
		return false

	// If radius is 0, then we have to calculate our own radius.:This only
	// works if the image is a square.
	case r == 0:
		// Calculate the radius by dividing a side by 2.
		r = (min / 2)

		// Draw an arc from 0deg to 360deg.
		cc.Arc(w/2, h/2, r, 0, circle)

		// We have to do this so the arc paint doesn't leave back a black
		// background instead of the usual alpha.
		cc.SetSourceRGBA(255, 255, 255, 0)

		// Clip the image with the arc we drew.
		cc.Clip()

	// If radius is more than 0, then we have to calculate the radius from
	// the edges.
	case r > 0:
		// StackOverflow is godly.
		// https://stackoverflow.com/a/6959843.

		// Radius should be largest a single side divided by 2.
		if max := min / 2; r > max {
			r = max
		}

		// Draw 4 arcs at 4 corners.
		cc.Arc(0+r, 0+r, r, 2*(pi/2), 3*(pi/2)) // top left
		cc.Arc(w-r, 0+r, r, 3*(pi/2), 4*(pi/2)) // top right
		cc.Arc(w-r, h-r, r, 0*(pi/2), 1*(pi/2)) // bottom right
		cc.Arc(0+r, h-r, r, 1*(pi/2), 2*(pi/2)) // bottom left

		// Close the created path.
		cc.ClosePath()
		cc.SetSourceRGBA(255, 255, 255, 0)

		// Clip the image with the arc we drew.
		cc.Clip()
	}

	// Paint the changes.
	cc.Paint()

	return false
}
