package main

import (
	"fmt"
    "math"
    "math/rand"

	"github.com/ryomak/p5go"
)

func main() {
	p5go.Run("#canvas-detail",
		p5go.Setup(setup),
		p5go.Draw(draw),
	)
	select {}
}

var faces []*face

func setup(p *p5go.Canvas) {
    canvas := 300
	p.CreateCanvas(canvas, canvas)
	p.ColorMode(p5go.HSB)
	width := 100
	for x := 0; x < canvas; x += width {
		for y := 0; y < canvas; y += width {
			color := fmt.Sprintf("hsb(%d, 100%%, 100%%)", rand.Intn(360))

			f := &face{
				width:  float64(width),
				height: float64(width),
				color:  color,
				x:      float64(x),
				y:      float64(y),
			}
			faces = append(faces, f)
		}
	}
}

func draw(p *p5go.Canvas) {
	for _, f := range faces {
		f.draw(p)
	}
}

type face struct {
	width  float64
	height float64
	color  string

	x, y float64
}

// eye は目を描画する
func (f *face) eye(p *p5go.Canvas, x, y float64) {
	angle := p.Atan2(p.MouseY()-y, p.MouseX()-x)
	p.NoStroke()

	p.Push()
	p.Translate(x, y)
	p.Fill("white")                             // 白目
	p.Ellipse(0, 0, 0.25*f.width, 0.25*f.width) // 白目部分を描画

	p.Rotate(angle)
	p.Fill(f.color)                                            // 黒目
	p.Ellipse(0.0625*f.width, 0, 0.125*f.width, 0.125*f.width) // 黒目部分を描画
	p.Pop()
}

// mouth は口を描画する
func (f *face) mouth(p *p5go.Canvas) {
	distance := math.Hypot(p.MouseX()-f.x-f.width/2, p.MouseY()-f.y-f.height/2)

	// 距離を 0 から 200 の範囲に補正し、滑らかな変化をつける
	clampedDistance := math.Min(math.Max(distance, 0), 200)
	ratio := 1 - clampedDistance/200 // 0 に近いほど楕円、1 に近いほど三日月

	p.Push()
	p.NoStroke()
	p.Translate(f.x+f.width/2, f.y+0.75*f.height)

	p.Fill("white")
	startAngle := 0.0
	endAngle := math.Pi
	p.Arc(0, 0, 0.5*f.width, 0.25*f.height*ratio, startAngle, endAngle)
	p.Pop()
}

func (f *face) draw(p *p5go.Canvas) {
	p.Fill(f.color)
	p.Rect(f.x, f.y, f.width, f.height)

	// left eye
	f.eye(p, 0.375*f.width+f.x, 0.5*f.height+f.y)

	// right eye
	f.eye(p, 0.625*f.width+f.x, 0.5*f.height+f.y)

	f.mouth(p)
}