//go:build ignore

// scan_generate generates zscan_rdata.go which houses all the parseXXX methods.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"log"
	"slices"
	"strings"

	"codeberg.org/miekg/dns/internal/generate"
)

var skip = []string{"TSIG", "NULL", "TXT", "DELEG", "SVCB", "HIP", "LOC",
	"CSYNC", "RRSIG", "NSEC", "NSEC3", "GPOS", "RFC3597", "DSYNC", "NSEC3PARAM",
	"TKEY", "URI", "NINFO", "EUI48", "CAA", "HINFO", "ISDN", "NAPTR", "UINFO", "EUI64",
}

var hdr = `
// Code generated by "go run clone_generate.go"; DO NOT EDIT.

package dns

import (
	"net/netip"

    "codeberg.org/miekg/dns/rdata"
    "codeberg.org/miekg/dns/internal/dnsstring"
    "codeberg.org/miekg/dns/internal/dnslex"
)

`

const out = "zscan_rdata.go"

func main() {
	flag.Parse()
	specs, err := generate.StructTypeSpecs("rdata/rdata.go")
	if err != nil {
		log.Fatalf("Failed to generate %s: %v", out, err)
	}

	b := &bytes.Buffer{}
	b.WriteString(hdr)

Specs:
	for _, spec := range specs {
		rrname := spec.Name.Name
		if slices.Contains(skip, rrname) {
			continue
		}

		fmt.Fprintf(b, "func parse%[1]s(rd *rdata.%[1]s, c *dnslex.Lexer, o string) (err error) {\n", rrname)
		strct := spec.Type.(*ast.StructType)

		blank := func() {}
		next := func() { fmt.Fprintf(b, "l, _ := c.Next()\n") }
		for _, field := range strct.Fields.List {
			fieldname := field.Names[0].String()
			fieldtype := ""
			if id, ok := field.Type.(*ast.Ident); ok {
				fieldtype = id.Name
			}
			tag := ""
			if field.Tag != nil {
				tag = strings.Trim(field.Tag.Value, "`")
			}

			blank()

			if tag != `dns:"base64"` && tag != `dns:"hex"` {
				next()
			}

			next = func() { fmt.Fprintf(b, "l, _ = c.Next()\n") }

			switch tag {
			case `dns:"-"`:
				continue
			case "":
				switch fieldtype {
				case "string":
					fmt.Fprintf(b, "rd.%s = l.Token\n", fieldname)
					fmt.Fprintf(b, `if l.Value == dnslex.Error || rd.%s == "" {`+"\n", fieldname)
				case "uint8":
					fmt.Fprintf(b, "rd.%s, err = dnsstring.AtoiUint8(l.Token)\n", fieldname)
					fmt.Fprintf(b, "if l.Value == dnslex.Error || err != nil {\n")
					if fieldname == "Algorithm" {
						fmt.Fprintf(b, "var ok bool\n")
						fmt.Fprintf(b, "rd.%s, ok = upperLookup(l.Token, StringToAlgorithm)\n", fieldname)
						fmt.Fprintf(b, "if !ok {\n")
						fmt.Fprintf(b, `return &ParseError{err: "bad %s Algorithm", lex: l}`+"\n", rrname)
						fmt.Fprintf(b, "}\n")
					}
				case "uint16":
					fmt.Fprintf(b, "rd.%s, err = dnsstring.AtoiUint16(l.Token)\n", fieldname)
					fmt.Fprintf(b, "if l.Value == dnslex.Error || err != nil {\n")
				case "uint32":
					fmt.Fprintf(b, "rd.%s, err = dnsstring.AtoiUint32(l.Token)\n", fieldname)
					fmt.Fprintf(b, "if l.Value == dnslex.Error || err != nil {\n")
				case "uint64":
					fmt.Fprintf(b, "rd.%s, err = dnsstring.AtoiUint64(l.Token)\n", fieldname)
					fmt.Fprintf(b, "if l.Value == dnslex.Error || err != nil {\n")
				}

			case `dns:"ttl"`:
				fmt.Fprintf(b, "rd.%s, err = dnsstring.AtoiUint32(l.Token)\n", fieldname)
				fmt.Fprintf(b, "if l.Value == dnslex.Error || err != nil {\n")
				{
					fmt.Fprintf(b, "var ok bool\n")
					fmt.Fprintf(b, "rd.%s, ok = stringToTTL(l.Token)\n", fieldname)
					fmt.Fprintf(b, "if !ok {\n")
					fmt.Fprintf(b, `return &ParseError{err: "bad %s %s", lex: l}`+"\n", rrname, fieldname)
				}
				fmt.Fprintf(b, "}\n")
				fmt.Fprintf(b, "}\n")

				continue

			case `dns:"node"`:
				fmt.Fprintf(b, "rd.%s, err = dnsstring.ToNodeID(l.Token)\n", fieldname)
				fmt.Fprintf(b, "if l.Value == dnslex.Error || err != nil {\n")

			case `dns:"base64"`:
				fallthrough
			case `dns:"hex"`:
				// should be the last, no error handling, return the error
				fmt.Fprintf(b, `rd.%s, err = remainder(c, "bad %s %s")`+"\n", fieldname, rrname, fieldname)
				fmt.Fprintf(b, "return err\n")
				fmt.Fprintf(b, "}\n\n")

				continue Specs

			case `dns:"a"`:
				fmt.Fprintf(b, "rd.%s, err = netip.ParseAddr(l.Token)\n", fieldname)
				fmt.Fprintf(b, "if l.Value == dnslex.Error || err != nil || !rd.%s.Is4(){\n", fieldname)
			case `dns:"aaaa"`:
				fmt.Fprintf(b, "rd.%s, err = netip.ParseAddr(l.Token)\n", fieldname)
				fmt.Fprintf(b, "if l.Value == dnslex.Error || err != nil || !rd.%s.Is6(){\n", fieldname)

			case `dns:"mname"`:
				fallthrough
			case `dns:"cname"`:
				fallthrough
			case `dns:"name"`:
				fmt.Fprintf(b, "rd.%s = dnsutilAbsolute(l.Token, o)\n", fieldname)
				fmt.Fprintf(b, `if l.Value == dnslex.Error || rd.%s == "" {`+"\n", fieldname)
				// rest of the error handling is done below
			}
			fmt.Fprintf(b, `return &ParseError{err: "bad %s %s", lex: l}`+"\n", rrname, fieldname)
			fmt.Fprintf(b, "}\n")

			blank = func() { fmt.Fprintln(b, "\nc.Next() // dnslex.Blank\n") }
		}
		fmt.Fprintln(b, "return toParseError(dnslex.Discard(c))")
		fmt.Fprint(b, "}\n\n")
	}

	generate.Write(b, out)
}
