//go:build ignore

package main

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

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

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

package dns

import "codeberg.org/miekg/dns/rdata"
import "codeberg.org/miekg/dns/internal/dnslex"
import "io"

`

const out = "zrdata.go"

func main() {
	flag.Parse()
	specs, err := generate.StructTypeSpecs("types.go")
	if err != nil {
		log.Fatalf("Failed to generate %s: %v", out, err)
	}
	b := &bytes.Buffer{}
	b.WriteString(hdr)
	b.WriteString(`// TypeToRDATA is a map of functions for each RR type to set it's rdata.
var TypeToRDATA = map[uint16]func(RR, RDATA){
`)

	for _, spec := range specs {
		rrname := spec.Name.Name
		switch rrname {
		case "OPT":
			fallthrough
		case "RFC3597":
			continue
		}

		strct := spec.Type.(*ast.StructType)

		if generate.IsEmbedded(strct) {
			fmt.Fprintf(b, "Type%[2]s: func (rr RR, rd RDATA) {  rr.(*%[2]s).%[1]s.%[1]s = rd.(rdata.%[1]s) },\n", strct.Fields.List[0].Type, rrname)
			continue
		}
		if len(strct.Fields.List) == 1 { // Only header, no rdata
			fmt.Fprintf(b, "Type%s: func (rr RR, rd RDATA) { },\n", rrname)
			continue
		}
		fmt.Fprintf(b, "Type%s: func (rr RR, rd RDATA) { rr.(*%[1]s).%[1]s = rd.(rdata.%[1]s) },\n", rrname)
	}
	b.WriteString("}\n\n")

	for _, spec := range specs {
		rrname := spec.Name.Name
		switch rrname {
		case "OPT":
			fallthrough
		case "RFC3597":
			continue
		}

		strct := spec.Type.(*ast.StructType)

		if generate.IsEmbedded(strct) {
			fmt.Fprintf(b, "func (rr *%[2]s) Data() RDATA { return rr.%[1]s.%[1]s }\n", strct.Fields.List[0].Type, rrname)
			continue
		}
		if len(strct.Fields.List) == 1 { // Only header, no rdata
			fmt.Fprintf(b, "func (rr %s) Data() RDATA { return nil }\n", rrname)
			continue
		}
		fmt.Fprintf(b, "func (rr *%[1]s) Data() RDATA { return rr.%[1]s }\n", rrname)
	}

	b.WriteString("func parseData(r io.Reader, rrtype uint16, o string) (RDATA, error) {\n")
	b.WriteString("c := dnslex.New(r, StringToType, StringToCode, StringToClass)\n\n")
	b.WriteString("switch rrtype {\n")
	for _, spec := range specs {
		rrname := spec.Name.Name
		switch rrname {
		case "OPT", "NULL", "TSIG", "RFC3597":
			continue
		}

		strct := spec.Type.(*ast.StructType)
		if !generate.IsEmbedded(strct) && len(strct.Fields.List) == 1 {
			// Only header, no rdata
			continue
		}

		fmt.Fprintf(b, "case Type%s:\n", rrname)

		if generate.IsEmbedded(strct) {
			fmt.Fprintf(b, "rd := rdata.%s{}\n", strct.Fields.List[0].Type)
			fmt.Fprintf(b, "pe := parse%s(&rd, c, o)\n", strct.Fields.List[0].Type)
		} else {
			fmt.Fprintf(b, "rd := rdata.%s{}\n", rrname)
			fmt.Fprintf(b, "pe := parse%s(&rd, c, o)\n", rrname)
		}
		fmt.Fprintf(b, `if pe != nil {
	return rd, pe
    }
return rd, nil

`)
	}
	b.WriteString(`}

if newFn, ok := TypeToRR[rrtype]; ok {
	rr := newFn()
	if _, ok := rr.(Parser); ok {
		if err := parse(rr, c, o); err != nil {
			return rr.Data(), err
		}
		return rr.Data(), nil
	}
}

rd := rdata.RFC3597{}
	pe := parseRFC3597(&rd, c, o)
	if pe != nil {
		return rd, pe
	}
	return rd, nil
}
`)
	generate.Write(b, out)
}
