// Package icon provides embedded AgentTeam application and tray icons.
package icon

import (
	"bytes"
	"image"
	"image/color"
	"image/draw"
	"image/png"
	"sync"

	_ "embed"
)

const (
	dockIconAntialiasSamples = 4
	// dockIconContentRatio controls how much of the canvas the icon fills.
	// Apple HIG recommends ~10% padding on each side, so content ≈ 80% of canvas.
	dockIconContentRatio = 0.80

	defaultDockCornerRadius = 56.0 / 256.0
	macos26DockCornerRadius = 75.0 / 256.0
)

// dockCornerRadius is set by platform-specific init to the appropriate
// corner radius ratio for the running OS version.
var dockCornerRadius float64

var (
	//go:embed assets/icon-black.png
	appBlackPNG []byte
	//go:embed assets/icon-white.png
	appWhitePNG []byte
	//go:embed assets/tray-black.png
	trayBlackPNG []byte
	//go:embed assets/tray-white.png
	trayWhitePNG []byte

	roundedAppPNGOnce sync.Once
	roundedAppPNG     []byte
)

// AppPNG returns the branded application icon as a PNG image.
func AppPNG() []byte {
	return appBlackPNG
}

// RoundedAppPNG returns the branded application icon with rounded transparent
// corners for native app switcher surfaces such as macOS Dock and Windows
// taskbar/Alt-Tab.
func RoundedAppPNG() []byte {
	roundedAppPNGOnce.Do(func() {
		rounded, err := RoundPNGCorners(appBlackPNG)
		if err != nil {
			roundedAppPNG = appBlackPNG
			return
		}
		roundedAppPNG = rounded
	})
	return roundedAppPNG
}

// RoundPNGCorners applies the dock-style padding (~10% transparent margin on
// each side) and rounded transparent corners to an arbitrary PNG. Exported so
// external tools such as genicon can produce dock-ready PNG assets without
// duplicating the rounding logic.
func RoundPNGCorners(data []byte) ([]byte, error) {
	return roundPNGCorners(data)
}

// DockPNG returns the rounded branded application icon used for macOS Dock
// presentation.
func DockPNG() []byte {
	return RoundedAppPNG()
}

// AppWhitePNG returns the alternate branded application icon as a PNG image.
func AppWhitePNG() []byte {
	return appWhitePNG
}

// TrayBlackPNG returns the dark tray icon as a PNG image.
func TrayBlackPNG() []byte {
	return trayBlackPNG
}

// TrayWhitePNG returns the light tray icon as a PNG image.
func TrayWhitePNG() []byte {
	return trayWhitePNG
}

// TrayPNG returns the system tray icon as a PNG image.
func TrayPNG() []byte {
	return TrayBlackPNG()
}

func roundPNGCorners(data []byte) ([]byte, error) {
	src, _, err := image.Decode(bytes.NewReader(data))
	if err != nil {
		return nil, err
	}

	srcBounds := src.Bounds()
	sw, sh := srcBounds.Dx(), srcBounds.Dy()
	if sw <= 0 || sh <= 0 {
		return nil, image.ErrFormat
	}

	// Destination canvas has the same dimensions as the source.
	dstW, dstH := sw, sh

	// Scale the content down so transparent padding surrounds it.
	contentW := int(float64(dstW) * dockIconContentRatio)
	contentH := int(float64(dstH) * dockIconContentRatio)
	offsetX := (dstW - contentW) / 2
	offsetY := (dstH - contentH) / 2

	// Draw the scaled source onto the padded canvas.
	scaled := scaleNRGBA(src, srcBounds, contentW, contentH)

	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
	draw.Draw(dst, image.Rect(offsetX, offsetY, offsetX+contentW, offsetY+contentH), scaled, image.Point{}, draw.Over)

	// Apply rounded corners to the content area.
	radius := roundedIconRadius(contentW, contentH)
	for y := 0; y < dstH; y++ {
		for x := 0; x < dstW; x++ {
			// Only pixels within the content rect may be non-transparent.
			cx, cy := x-offsetX, y-offsetY
			if cx < 0 || cy < 0 || cx >= contentW || cy >= contentH {
				continue
			}
			col := dst.NRGBAAt(x, y)
			coverage := roundedRectCoverage(cx, cy, contentW, contentH, radius)
			if coverage <= 0 {
				col.A = 0
			} else if coverage < 255 {
				col.A = uint8((uint16(col.A)*uint16(coverage) + 127) / 255)
			}
			dst.SetNRGBA(x, y, col)
		}
	}

	var out bytes.Buffer
	if err := png.Encode(&out, dst); err != nil {
		return nil, err
	}
	return out.Bytes(), nil
}

func scaleNRGBA(src image.Image, srcBounds image.Rectangle, width, height int) *image.NRGBA {
	dst := image.NewNRGBA(image.Rect(0, 0, width, height))
	srcW, srcH := srcBounds.Dx(), srcBounds.Dy()
	if width <= 0 || height <= 0 || srcW <= 0 || srcH <= 0 {
		return dst
	}

	for y := 0; y < height; y++ {
		sy := scaleCoordinate(y, height, srcH)
		y0 := int(sy)
		fy := sy - float64(y0)
		if y0 >= srcH-1 {
			y0 = srcH - 1
			fy = 0
		}
		y1 := y0 + 1
		if y1 >= srcH {
			y1 = y0
		}

		for x := 0; x < width; x++ {
			sx := scaleCoordinate(x, width, srcW)
			x0 := int(sx)
			fx := sx - float64(x0)
			if x0 >= srcW-1 {
				x0 = srcW - 1
				fx = 0
			}
			x1 := x0 + 1
			if x1 >= srcW {
				x1 = x0
			}

			c00 := sampleNRGBA(src, srcBounds.Min.X+x0, srcBounds.Min.Y+y0)
			c10 := sampleNRGBA(src, srcBounds.Min.X+x1, srcBounds.Min.Y+y0)
			c01 := sampleNRGBA(src, srcBounds.Min.X+x0, srcBounds.Min.Y+y1)
			c11 := sampleNRGBA(src, srcBounds.Min.X+x1, srcBounds.Min.Y+y1)
			dst.SetNRGBA(x, y, bilinearNRGBA(c00, c10, c01, c11, fx, fy))
		}
	}

	return dst
}

func scaleCoordinate(dstIndex, dstSize, srcSize int) float64 {
	if dstSize <= 1 {
		return float64(srcSize-1) / 2
	}
	pos := (float64(dstIndex)+0.5)*float64(srcSize)/float64(dstSize) - 0.5
	if pos < 0 {
		return 0
	}
	maxPos := float64(srcSize - 1)
	if pos > maxPos {
		return maxPos
	}
	return pos
}

func sampleNRGBA(img image.Image, x, y int) color.NRGBA {
	return color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA)
}

func bilinearNRGBA(c00, c10, c01, c11 color.NRGBA, fx, fy float64) color.NRGBA {
	return color.NRGBA{
		R: bilinearChannel(c00.R, c10.R, c01.R, c11.R, fx, fy),
		G: bilinearChannel(c00.G, c10.G, c01.G, c11.G, fx, fy),
		B: bilinearChannel(c00.B, c10.B, c01.B, c11.B, fx, fy),
		A: bilinearChannel(c00.A, c10.A, c01.A, c11.A, fx, fy),
	}
}

func bilinearChannel(c00, c10, c01, c11 uint8, fx, fy float64) uint8 {
	top := float64(c00)*(1-fx) + float64(c10)*fx
	bottom := float64(c01)*(1-fx) + float64(c11)*fx
	value := top*(1-fy) + bottom*fy
	if value <= 0 {
		return 0
	}
	if value >= 255 {
		return 255
	}
	return uint8(value + 0.5)
}

func roundedIconRadius(width, height int) float64 {
	shortSide := width
	if height < shortSide {
		shortSide = height
	}
	return float64(shortSide) * dockCornerRadius
}

func roundedRectCoverage(x, y, width, height int, radius float64) uint8 {
	if radius <= 0 {
		return 255
	}

	insideSamples := 0
	totalSamples := dockIconAntialiasSamples * dockIconAntialiasSamples
	for sy := 0; sy < dockIconAntialiasSamples; sy++ {
		py := float64(y) + (float64(sy)+0.5)/dockIconAntialiasSamples
		for sx := 0; sx < dockIconAntialiasSamples; sx++ {
			px := float64(x) + (float64(sx)+0.5)/dockIconAntialiasSamples
			if pointInRoundedRect(px, py, float64(width), float64(height), radius) {
				insideSamples++
			}
		}
	}
	return uint8((insideSamples*255 + totalSamples/2) / totalSamples)
}

func pointInRoundedRect(x, y, width, height, radius float64) bool {
	if x >= radius && x <= width-radius {
		return true
	}
	if y >= radius && y <= height-radius {
		return true
	}

	cx := radius
	if x > width-radius {
		cx = width - radius
	}
	cy := radius
	if y > height-radius {
		cy = height - radius
	}
	dx := x - cx
	dy := y - cy
	return dx*dx+dy*dy <= radius*radius
}
