package main

import (
	"fmt"
	"math"

	"github.com/fogleman/gg"
)

type fcoords struct {
	X float64
	Y float64
}

func main() {
	a4_dimensions := fcoords{210, 297}       //mm
	scale := 30.                             // px per mm
	card_dimensions := fcoords{85.60, 53.98} //mm
	stroke_width := 0.25                     //mm
	stitching_width := 5.                    //mm
	hole_spacing := 4.                       //mm
	hole_radius := 1.                        //mm
	initial_hole_spacing_from_center := 4.1  //mm
	hole_spacing_from_top := 3.              //mm
	leather_thickness := 2.                  //mm
	optimal_angle := 90.                     //deg
	rounding_radius := 4.                    //mm

	layer_offset := 12. //mm
	alternate_layer_offset := true

	page_margins := 10. //mm

	layer_count := 5

	hole_counts := []int{44, 32}

	font_path := "assets/iosevka-heavy.ttf"
	font_size := 8.

	ruler_count := []int{50, 4}
	ruler_square_size := 2. //mm

	// Code
	anchor := fcoords{page_margins, page_margins} // mm

	for layer := 0; layer < layer_count; layer++ {
		dc := gg.NewContext(int(math.Round(a4_dimensions.X*scale)), int(math.Round(a4_dimensions.Y*scale)))
		dc.SetRGB(1, 1, 1)
		dc.Clear()
		dc.SetRGB(0, 0, 0)
		dc.LoadFontFace(font_path, font_size*scale)
		dc.SetLineWidth(stroke_width * scale)

		stencil_dimensions := fcoords{card_dimensions.X*2 + stitching_width*2 + float64(layer_count-layer-1)*math.Pi*leather_thickness*optimal_angle/360*2, card_dimensions.Y + layer_offset + stitching_width} //mm

		dc.DrawRoundedRectangle(anchor.X*scale, anchor.Y*scale, stencil_dimensions.X*scale, stencil_dimensions.Y*scale, rounding_radius*scale)
		dc.Stroke()
		hole_spacing_from_center := initial_hole_spacing_from_center + math.Pi*leather_thickness*float64(layer_count-layer-1)*optimal_angle/360
		for i := 0; i < hole_counts[0]; i++ {
			dc.DrawCircle((anchor.X+stencil_dimensions.X/2+(hole_spacing_from_center+float64(i/2)*hole_spacing)*float64(i%2*2-1))*scale, (anchor.Y+stencil_dimensions.Y-stitching_width/2)*scale, hole_radius*scale)
			dc.Stroke()
		}

		for i := 0; i < hole_counts[1]; i++ {
			if i%2 == 0 {
				dc.DrawCircle((anchor.X+stitching_width/2)*scale, (anchor.Y+hole_spacing_from_top+float64(i/2)*hole_spacing)*scale, hole_radius*scale)
			} else {
				dc.DrawCircle((anchor.X+stencil_dimensions.X-stitching_width/2)*scale, (anchor.Y+hole_spacing_from_top+float64(i/2)*hole_spacing)*scale, hole_radius*scale)
			}
			dc.Stroke()
		}

		if layer != 0 {
			for i := 0; i < hole_counts[1]; i++ {
				dc.DrawCircle((anchor.X+stencil_dimensions.X/2+hole_spacing_from_center*float64(i%2*2-1))*scale, (anchor.Y+hole_spacing_from_top+float64(i/2)*hole_spacing)*scale, hole_radius*scale)
				dc.Stroke()
			}
		}

		dc.DrawLine((anchor.X)*scale, (anchor.Y+stencil_dimensions.Y-stitching_width)*scale, (anchor.X+stencil_dimensions.X)*scale, (anchor.Y+stencil_dimensions.Y-stitching_width)*scale)
		dc.Stroke()
		dc.DrawLine((anchor.X+stitching_width)*scale, (anchor.Y)*scale, (anchor.X+stitching_width)*scale, (anchor.Y+stencil_dimensions.Y)*scale)
		dc.Stroke()
		dc.DrawLine((anchor.X+stencil_dimensions.X-stitching_width)*scale, (anchor.Y)*scale, (anchor.X+stencil_dimensions.X-stitching_width)*scale, (anchor.Y+stencil_dimensions.Y)*scale)
		dc.Stroke()

		if layer != 0 {
			dc.DrawLine((anchor.X+stencil_dimensions.X/2+hole_spacing_from_center-stitching_width/2)*scale, (anchor.Y)*scale, (anchor.X+stencil_dimensions.X/2+hole_spacing_from_center-stitching_width/2)*scale, (anchor.Y+stencil_dimensions.Y)*scale)
			dc.Stroke()
			dc.DrawLine((anchor.X+stencil_dimensions.X/2-hole_spacing_from_center+stitching_width/2)*scale, (anchor.Y)*scale, (anchor.X+stencil_dimensions.X/2-hole_spacing_from_center+stitching_width/2)*scale, (anchor.Y+stencil_dimensions.Y)*scale)
			dc.Stroke()

			dc.DrawLine((anchor.X+stencil_dimensions.X/2+hole_spacing_from_center+stitching_width/2)*scale, (anchor.Y)*scale, (anchor.X+stencil_dimensions.X/2+hole_spacing_from_center+stitching_width/2)*scale, (anchor.Y+stencil_dimensions.Y)*scale)
			dc.Stroke()
			dc.DrawLine((anchor.X+stencil_dimensions.X/2-hole_spacing_from_center-stitching_width/2)*scale, (anchor.Y)*scale, (anchor.X+stencil_dimensions.X/2-hole_spacing_from_center-stitching_width/2)*scale, (anchor.Y+stencil_dimensions.Y)*scale)
			dc.Stroke()
		}

		if layer > 1 {
			effective_layer_index := layer - 1
			ay := anchor.Y + layer_offset*float64(effective_layer_index)
			by := anchor.Y + layer_offset*float64(effective_layer_index+1)
			if alternate_layer_offset {
				if layer%2 == 1 {
					cy := by
					by = ay
					ay = cy
				}
			}
			dc.DrawLine((anchor.X+stitching_width)*scale, ay*scale, (anchor.X+stencil_dimensions.X/2-hole_spacing_from_center-stitching_width/2)*scale, by*scale)
			dc.Stroke()
			dc.DrawLine((anchor.X+stencil_dimensions.X/2+hole_spacing_from_center+stitching_width/2)*scale, by*scale, (anchor.X+stencil_dimensions.X-stitching_width)*scale, ay*scale)
			dc.Stroke()
			dc.DrawLine(anchor.X*scale, ay*scale, (anchor.X+stitching_width)*scale, ay*scale)
			dc.Stroke()
			dc.DrawLine((anchor.X+stencil_dimensions.X/2-hole_spacing_from_center-stitching_width/2)*scale, by*scale, (anchor.X+stencil_dimensions.X/2+hole_spacing_from_center+stitching_width/2)*scale, by*scale)
			dc.Stroke()
			dc.DrawLine((anchor.X+stencil_dimensions.X-stitching_width)*scale, ay*scale, (anchor.X+stencil_dimensions.X)*scale, ay*scale)
			dc.Stroke()
			dc.DrawArc((anchor.X+rounding_radius)*scale, (ay+rounding_radius)*scale, rounding_radius*scale, 3*math.Pi/2, math.Pi)
			dc.Stroke()
			dc.DrawArc((anchor.X+stencil_dimensions.X-rounding_radius)*scale, (ay+rounding_radius)*scale, rounding_radius*scale, -math.Pi/2, 0)
			dc.Stroke()
		}

		dc.DrawStringAnchored(fmt.Sprintf("Layer %d", layer), (anchor.X)*scale, (anchor.Y+stencil_dimensions.Y+page_margins)*scale, 0, 0)
		for i := 0; i < ruler_count[0]; i++ {
			for j := 0; j < ruler_count[1]; j++ {
				dc.DrawRectangle((anchor.X+float64(i)*ruler_square_size)*scale, (anchor.Y+stencil_dimensions.Y+page_margins*2+font_size+float64(j)*ruler_square_size)*scale, ruler_square_size*scale, ruler_square_size*scale)
				dc.Stroke()
			}
		}
		dc.SavePNG(fmt.Sprintf("out-%04d.png", layer))
	}
}
