// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build ignore

// This is based on the implementation of src/cmd/internal/obj/stringer.go.
// This is a mini version of the stringer tool customized for the Cnames
// table in the architecture support for obj.

package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"os"
	"regexp"
	"strings"
)

var (
	input  = flag.String("i", "", "input file name")
	output = flag.String("o", "", "output file name")
	pkg    = flag.String("p", "", "package name")
)

var cnameExp = regexp.MustCompile(`^\tC_([A-Za-z0-9_]+)`)

func main() {
	flag.Parse()
	if *input == "" || *output == "" || *pkg == "" {
		flag.Usage()
		os.Exit(2)
	}

	start := ""
	switch *pkg {
	case "arm64":
		start = "var cnames7 = []string{\n\t\"\", // C_NONE starts from 1\n"
	case "loong64", "mips":
		start = "var cnames0 = []string{\n"
	case "ppc64":
		start = "var cnames9 = []string{\n"
	case "s390x":
		start = "var cnamesz = []string{\n"
	default:
		fmt.Printf("Only supports generating Cnames for arm64,loong64,mips,ppc64,s390x.")
		os.Exit(0)
	}

	in, err := os.Open(*input)
	if err != nil {
		log.Fatal(err)
	}
	fd, err := os.Create(*output)
	if err != nil {
		log.Fatal(err)
	}
	out := bufio.NewWriter(fd)
	closeOut := func() {
		if err = out.Flush(); err != nil {
			log.Fatal(err)
		}

		if err = fd.Close(); err != nil {
			log.Fatal(err)
		}
	}
	defer closeOut()

	on := false
	s := bufio.NewScanner(in)
	for s.Scan() {
		line := s.Text()
		if !on {
			// First relevant line contains "C_NONE = iota".
			// If we find it, delete the "=" so we don't stop immediately.
			const first = "C_NONE"
			if !strings.Contains(line, first) {
				continue
			}

			const suffix = "= iota"
			index := strings.Index(line, suffix)
			if index < 0 {
				continue
			}
			line = line[:index]

			// It's on. Start with the header.
			fmt.Fprintf(out, header, *input, *output, *pkg, *pkg)
			fmt.Fprintf(out, start)
			on = true
		}

		// Strip comments so their text won't defeat our heuristic.
		index := strings.Index(line, "//")
		if index > 0 {
			line = line[:index]
		}
		index = strings.Index(line, "/*")
		if index > 0 {
			comments := line[index:]
			if !strings.Contains(comments, "*/") {
				log.Fatalf("invalid comment: %s\n", comments)
			}
			line = line[:index]
		}

		// Termination condition: Any line with an = changes the sequence,
		// so stop there, and stop at a closing brace.
		if strings.HasPrefix(line, "}") || strings.ContainsRune(line, '=') {
			break
		}

		sub := cnameExp.FindStringSubmatch(line)
		if len(sub) < 2 {
			continue
		} else {
			fmt.Fprintf(out, "\t%q,\n", sub[1])
		}
	}
	fmt.Fprintln(out, "}")
	if s.Err() != nil {
		log.Fatal(err)
	}
}

const header = `// Code generated by mkcnames -i %s -o %s -p %s; DO NOT EDIT.

package %s

// This order should be strictly consistent to that in a.out.go.
`
