package main

import (
	"fmt"
	"math"
	"os"
)

func WriteSVG(filename string, gf *GerberFile, bounds *Bounds) error {
	b := *bounds

	widthMM := b.MaxX - b.MinX
	heightMM := b.MaxY - b.MinY

	f, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	// Use mm directly for SVG
	fmt.Fprintf(f, `<svg xmlns="http://www.w3.org/2000/svg" width="%fmm" height="%fmm" viewBox="0 0 %f %f">`,
		widthMM, heightMM, widthMM, heightMM)
	fmt.Fprintf(f, "\n<g fill=\"black\" stroke=\"black\">\n")

	// Note: SVG Y-axis points down. We need to invert Y: (heightMM - (y - b.MinY))
	toSVGX := func(x float64) float64 { return x - b.MinX }
	toSVGY := func(y float64) float64 { return heightMM - (y - b.MinY) }

	curX, curY := 0.0, 0.0
	curDCode := 0
	interpolationMode := "G01" // Default linear
	inRegion := false
	var regionVertices [][2]float64

	for _, cmd := range gf.Commands {
		if cmd.Type == "APERTURE" {
			curDCode = *cmd.D
			continue
		}
		if cmd.Type == "G01" || cmd.Type == "G02" || cmd.Type == "G03" {
			interpolationMode = cmd.Type
			continue
		}
		if cmd.Type == "G36" {
			inRegion = true
			regionVertices = nil
			continue
		}
		if cmd.Type == "G37" {
			if len(regionVertices) >= 3 {
				fmt.Fprintf(f, `<polygon points="`)
				for _, v := range regionVertices {
					fmt.Fprintf(f, "%f,%f ", toSVGX(v[0]), toSVGY(v[1]))
				}
				fmt.Fprintf(f, "\" fill=\"black\" stroke=\"none\"/>\n")
			}
			inRegion = false
			regionVertices = nil
			continue
		}

		prevX, prevY := curX, curY
		if cmd.X != nil {
			curX = *cmd.X
		}
		if cmd.Y != nil {
			curY = *cmd.Y
		}

		if inRegion {
			if cmd.Type == "MOVE" || (cmd.Type == "DRAW" && interpolationMode == "G01") {
				regionVertices = append(regionVertices, [2]float64{curX, curY})
			} else if cmd.Type == "DRAW" && (interpolationMode == "G02" || interpolationMode == "G03") {
				iVal, jVal := 0.0, 0.0
				if cmd.I != nil {
					iVal = *cmd.I
				}
				if cmd.J != nil {
					jVal = *cmd.J
				}
				arcPts := approximateArc(prevX, prevY, curX, curY, iVal, jVal, interpolationMode)
				for _, pt := range arcPts {
					regionVertices = append(regionVertices, pt)
				}
			}
			continue
		}

		if cmd.Type == "FLASH" {
			ap, ok := gf.State.Apertures[curDCode]
			if ok {
				writeSVGAperture(f, toSVGX(curX), toSVGY(curY), ap, false)
			}
		} else if cmd.Type == "DRAW" {
			ap, ok := gf.State.Apertures[curDCode]
			if ok {
				// Basic stroke representation for lines
				w := 0.1 // default
				if len(ap.Modifiers) > 0 {
					w = ap.Modifiers[0]
				}

				if interpolationMode == "G01" {
					fmt.Fprintf(f, `<line x1="%f" y1="%f" x2="%f" y2="%f" stroke-width="%f" stroke-linecap="round"/>`+"\n",
						toSVGX(prevX), toSVGY(prevY), toSVGX(curX), toSVGY(curY), w)
				} else {
					iVal, jVal := 0.0, 0.0
					if cmd.I != nil {
						iVal = *cmd.I
					}
					if cmd.J != nil {
						jVal = *cmd.J
					}

										// SVG path arc (Y-axis inverted: G02 CW -> CCW in SVG, G03 CCW -> CW in SVG)
					r := math.Sqrt(iVal*iVal + jVal*jVal)
					acx, acy := prevX+iVal, prevY+jVal
					sa := math.Atan2(prevY-acy, prevX-acx)
					ea := math.Atan2(curY-acy, curX-acx)
					var arcSpan float64
					if interpolationMode == "G03" {
						if ea <= sa { ea += 2 * math.Pi }
						arcSpan = ea - sa
					} else {
						if sa <= ea { sa += 2 * math.Pi }
						arcSpan = sa - ea
					}
					largeArc := 0
					if arcSpan > math.Pi { largeArc = 1 }
					sweep := 1 // G03 CCW Gerber -> CW SVG
					if interpolationMode == "G02" { sweep = 0 }

					fmt.Fprintf(f, `<path d="M %f %f A %f %f 0 %d %d %f %f" stroke-width="%f" fill="none" stroke-linecap="round"/>` + "\n",
						toSVGX(prevX), toSVGY(prevY), r, r, largeArc, sweep, toSVGX(curX), toSVGY(curY), w)
				}
			}
		}
	}
	fmt.Fprintf(f, "</g>\n</svg>\n")
	return nil
}

func writeSVGAperture(f *os.File, cx, cy float64, ap Aperture, isMacro bool) {
	switch ap.Type {
	case "C":
		if len(ap.Modifiers) > 0 {
			r := ap.Modifiers[0] / 2
			fmt.Fprintf(f, `<circle cx="%f" cy="%f" r="%f" />`+"\n", cx, cy, r)
		}
	case "R":
		if len(ap.Modifiers) >= 2 {
			w, h := ap.Modifiers[0], ap.Modifiers[1]
			fmt.Fprintf(f, `<rect x="%f" y="%f" width="%f" height="%f" />`+"\n", cx-w/2, cy-h/2, w, h)
		}
	case "O":
		if len(ap.Modifiers) >= 2 {
			w, h := ap.Modifiers[0], ap.Modifiers[1]
			r := w / 2
			if h < w {
				r = h / 2
			}
			fmt.Fprintf(f, `<rect x="%f" y="%f" width="%f" height="%f" rx="%f" ry="%f" />`+"\n", cx-w/2, cy-h/2, w, h, r, r)
		}
	case "P":
		if len(ap.Modifiers) >= 2 {
			dia, numV := ap.Modifiers[0], int(ap.Modifiers[1])
			r := dia / 2
			rot := 0.0
			if len(ap.Modifiers) >= 3 {
				rot = ap.Modifiers[2]
			}
			fmt.Fprintf(f, `<polygon points="`)
			for i := 0; i < numV; i++ {
				a := (rot + float64(i)*360.0/float64(numV)) * math.Pi / 180.0
				// SVG inverted Y means we might need minus for Sin
				fmt.Fprintf(f, "%f,%f ", cx+r*math.Cos(a), cy-r*math.Sin(a))
			}
			fmt.Fprintf(f, `" />`+"\n")
		}
	}
}
