package main

import (
	_ "embed"
	"image/color"
	"strconv"
	"time"
	"unsafe"

	"tinygo.org/x/tinydraw"
	"tinygo.org/x/tinyfont"
	"tinygo.org/x/tinyfont/freesans"
	"tinygo.org/x/tinyfont/gophers"

	qrcode "github.com/skip2/go-qrcode"
)

const (
	logoDisplayTime = 10 * time.Second
)

var rainbow []color.RGBA
var quit bool
var selectedScreen uint8

//go:embed logo.bin
var badgeLogo string

func Badge() {
	quit = false
	selectedScreen = 0
	display.FillScreen(colorBlack)

	rainbow = make([]color.RGBA, 256)
	for i := 0; i < 256; i++ {
		rainbow[i] = getRainbowRGB(uint8(i))
	}

	for {
		switch selectedScreen {
		case 7: // last +1
			selectedScreen = 0
			fallthrough
		case 0:
			showLogoBin()
			if quit {
				break
			}
		case 1:
			if hardwareName == "GOPHERBADGE" {
				logopurpleHardware()
			} else {
				selectedScreen++
			}
			if quit {
				break
			}
		case 2:
			myNameIsRainbow(YourName)
			if quit {
				break
			}
		case 3:
			blinkyRainbow(YourTitleA1, YourTitleA2)
			if quit {
				break
			}
		case 4:
			scroll(YourMarqueeTop, YourMarqueeMiddle, YourMarqueeBottom)
			if quit {
				break
			}
		case 5:
			QR(YourQRText)
			if quit {
				break
			}
		case 255: // max
			selectedScreen = 6 // last one
			fallthrough
		case 6:
			blinkyRainbow(YourTitleB1, YourTitleB2)
			if quit {
				break
			}
		default:
			selectedScreen = 0
		}
		if quit {
			break
		}
	}
}

func myNameIs(name string) {
	display.FillScreen(colorWhite)

	// black corners detail
	display.FillRectangle(0, 0, cornerRadius, cornerRadius, colorBlack)
	display.FillRectangle(0, displayHeight-cornerRadius, cornerRadius, cornerRadius, colorBlack)
	display.FillRectangle(displayWidth-cornerRadius, 0, cornerRadius, cornerRadius, colorBlack)
	display.FillRectangle(displayWidth-cornerRadius, displayHeight-cornerRadius, cornerRadius, cornerRadius, colorBlack)

	// round corners
	tinydraw.FilledCircle(&display, cornerRadius, cornerRadius, cornerRadius, colorRed)
	tinydraw.FilledCircle(&display, displayWidth-cornerRadius-1, cornerRadius, cornerRadius, colorRed)
	tinydraw.FilledCircle(&display, cornerRadius, displayHeight-cornerRadius-1, cornerRadius, colorRed)
	tinydraw.FilledCircle(&display, displayWidth-cornerRadius-1, displayHeight-cornerRadius-1, cornerRadius, colorRed)

	// top band
	display.FillRectangle(cornerRadius, 0, displayWidth-2*cornerRadius-1, cornerRadius, colorRed)
	display.FillRectangle(0, cornerRadius, displayWidth, int16(topBandHeight), colorRed)

	// bottom band
	display.FillRectangle(cornerRadius, displayHeight-cornerRadius-1, displayWidth-2*cornerRadius-1, cornerRadius+1, colorRed)
	display.FillRectangle(0, displayHeight-int16(bottomBandHeight)-cornerRadius-1, displayWidth, int16(bottomBandHeight), colorRed)

	// top text
	if displayWidth >= 320 {
		// gopherbadge: HELLO + my name is
		w32, _ := tinyfont.LineWidth(&freesans.Bold18pt7b, "HELLO")
		tinyfont.WriteLine(&display, &freesans.Bold18pt7b, (displayWidth-int16(w32))/2, int16(helloY), "HELLO", colorWhite)
		w32, _ = tinyfont.LineWidth(&freesans.Oblique9pt7b, "my name is")
		tinyfont.WriteLine(&display, &freesans.Oblique9pt7b, (displayWidth-int16(w32))/2, int16(myNameIsY), "my name is", colorWhite)
	} else {
		// pybadge: my NAME is
		w32, _ := tinyfont.LineWidth(&freesans.Regular12pt7b, "my NAME is")
		tinyfont.WriteLine(&display, &freesans.Regular12pt7b, (displayWidth-int16(w32))/2, int16(myNameIsY), "my NAME is", colorWhite)
	}

	writeLine(name, colorBlack, displayWidth/2, 6*displayHeight/10, displayWidth, true)

	// gophers
	if displayWidth >= 320 {
		tinyfont.WriteLineColors(&display, &gophers.Regular58pt, displayWidth-int16(gophersX), int16(gophersY), "BE", []color.RGBA{getRainbowRGB(100), getRainbowRGB(200)})
	} else {
		tinyfont.WriteLineColors(&display, &gophers.Regular32pt, displayWidth-int16(gophersX), int16(gophersY), "BE", []color.RGBA{getRainbowRGB(100), getRainbowRGB(200)})
	}
}

func myNameIsRainbow(name string) {
	myNameIs(name)

	for i := 0; i < 230; i++ {
		writeLine(name, rainbow[i], displayWidth/2, 6*displayHeight/10, displayWidth, true)
		i += 2

		getInput()
		if goBack() {
			quit = true
			return
		}
	}

	selectedScreen++
}

func blinkyRainbow(topline, bottomline string) {
	display.FillScreen(colorWhite)

	// calculate the width of the text so we could center them later
	for i := int16(0); i < 20; i++ {

		writeLine(topline, getRainbowRGB(uint8(i*12)), displayWidth/2, 4*displayHeight/10, displayWidth, true)
		writeLine(bottomline, getRainbowRGB(uint8(i*12)), displayWidth/2, 8*displayHeight/10, displayWidth, true)

		getInput()
		if goBack() {
			quit = true
			return
		}
	}

	selectedScreen++
}

func scroll(topline, middleline, bottomline string) {
	display.FillScreen(colorWhite)

	writeLine(topline, getRainbowRGB(200), displayWidth/2, 3*displayHeight/10, displayWidth, true)
	writeLine(middleline, getRainbowRGB(80), displayWidth/2, 6*displayHeight/10, displayWidth, true)
	writeLine(bottomline, getRainbowRGB(120), displayWidth/2, 9*displayHeight/10, displayWidth, true)

	display.SetScrollArea(0, 0)
	defer func() {
		display.SetScroll(0)
		display.StopScroll()
	}()

	for k := 0; k < 4; k++ {
		for i := int16(displayWidth - 1); i >= 0; i-- {
			getInput()
			if goBack() {
				quit = true
				return
			}

			display.SetScroll(i)
			time.Sleep(10 * time.Millisecond)
		}
	}

	selectedScreen++
}

func logopurpleHardware() {
	bgColor := color.RGBA{109, 0, 140, 255}
	white := color.RGBA{255, 255, 255, 255}
	display.FillScreen(bgColor)

	if displayWidth >= 320 {
		// Full logo for gopherbadge
		display.FillRectangle(6, 166, 308, 21, white)

		tinydraw.FilledCircle(&display, 282, 130, 9, white)
		tinydraw.Line(&display, 259, 110, 298, 149, bgColor)
		tinydraw.Line(&display, 260, 110, 299, 149, bgColor)
		tinydraw.Line(&display, 261, 110, 300, 149, bgColor)
		tinydraw.Line(&display, 262, 110, 301, 149, bgColor)
		tinydraw.Line(&display, 263, 110, 302, 149, bgColor)
		tinydraw.Line(&display, 264, 110, 303, 149, bgColor)
		tinydraw.Line(&display, 265, 110, 304, 149, bgColor)

		display.FillRectangle(250, 98, 11, 63, white)
		display.FillRectangle(250, 98, 44, 11, white)

		display.FillRectangle(270, 150, 44, 11, white)
		display.FillRectangle(303, 98, 11, 63, white)

		tinyfont.WriteLine(&display, &freesans.Regular18pt7b, 6, 109, "purple", white)
		tinyfont.WriteLine(&display, &freesans.Regular18pt7b, 6, 153, "Hardware by", white)
	} else {
		// Simplified logo for pybadge
		tinyfont.WriteLine(&display, &freesans.Bold9pt7b, 10, 60, "purple", white)
		tinyfont.WriteLine(&display, &freesans.Bold9pt7b, 10, 80, "Hardware", white)
	}

	refreshIntervals := uint8(7)
	displayTime := logoDisplayTime / 3

	for i := uint8(0); i < refreshIntervals; i++ {
		time.Sleep(displayTime / time.Duration(refreshIntervals))
		getInput()

		if goBack() {
			quit = true
			return
		}
	}

	selectedScreen++
}

func getRainbowRGB(i uint8) color.RGBA {
	if i < 85 {
		return color.RGBA{i * 3, 255 - i*3, 0, 255}
	} else if i < 170 {
		i -= 85
		return color.RGBA{255 - i*3, 0, i * 3, 255}
	}
	i -= 170
	return color.RGBA{0, i * 3, 255 - i*3, 255}
}

func showLogoBin() {
	selectedScreen++
	row := make([]color.RGBA, displayWidth)
	unsafeBadgeLogo := unsafe.Slice(unsafe.StringData(badgeLogo), len(badgeLogo))

	if len(badgeLogo) < displayHeight*displayWidth {
		return
	}
	for i := 0; i < displayHeight; i++ {
		for j := 0; j < displayWidth; j++ {
			values, err := strconv.ParseUint(string(unsafeBadgeLogo[6*(displayWidth*i+j):6*(displayWidth*i+j+1)]), 16, 32)

			if err != nil {
				println(err)
			}

			row[j] = color.RGBA{
				R: uint8(values >> 16),
				G: uint8((values >> 8) & 0xFF),
				B: uint8(values & 0xFF),
				A: 255,
			}
		}
		display.FillRectangleWithBuffer(0, int16(i), displayWidth, 1, row)
	}

	refreshIntervals := uint8(20)

	for i := uint8(0); i < refreshIntervals; i++ {
		time.Sleep(logoDisplayTime / time.Duration(refreshIntervals))
		getInput()

		if goBack() {
			quit = true
			return
		}
	}
}

func QR(msg string) {
	qr, err := qrcode.New(msg, qrcode.Medium)
	if err != nil {
		println(err, 123)
	}

	qrbytes := qr.Bitmap()
	size := int16(len(qrbytes))

	factor := int16(displayHeight / len(qrbytes))

	bx := (displayWidth - size*factor) / 2
	by := (displayHeight - size*factor) / 2
	display.FillScreen(color.RGBA{109, 0, 140, 255})
	for y := int16(0); y < size; y++ {
		for x := int16(0); x < size; x++ {
			if qrbytes[y][x] {
				display.FillRectangle(bx+x*factor, by+y*factor, factor, factor, colorBlack)
			} else {
				display.FillRectangle(bx+x*factor, by+y*factor, factor, factor, colorWhite)
			}
		}
	}

	refreshIntervals := uint8(20)

	for i := uint8(0); i < refreshIntervals; i++ {
		time.Sleep(logoDisplayTime / time.Duration(refreshIntervals))
		getInput()

		if goBack() {
			quit = true
			return
		}
	}

	selectedScreen++
}

func getFontWidthSize(text string) (w32 uint32, size byte) {
	w32, _ = tinyfont.LineWidth(&freesans.Bold24pt7b, text)
	size = 24
	if w32 < uint32(FontWidthThreshold) {
		size = 24
	} else {
		w32, _ = tinyfont.LineWidth(&freesans.Bold18pt7b, text)
		if w32 < uint32(FontWidthThreshold) {
			size = 18
		} else {
			w32, _ = tinyfont.LineWidth(&freesans.Bold12pt7b, text)
			if w32 < uint32(FontWidthThreshold) {
				size = 12
			} else {
				w32, _ = tinyfont.LineWidth(&freesans.Bold9pt7b, text)
				size = 9
			}
		}
	}
	return
}
