// Copyright 2018-2020 opcua authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file

package main

import (
	"bytes"
	"encoding/csv"
	"flag"
	"go/format"
	"golang.org/x/exp/maps"
	"log"
	"os"
	"strings"
	"text/template"
)

func main() {
	log.SetFlags(0)

	in := flag.String("in", "schema/NodeIds.csv", "path to NodeIds.csv")
	out := flag.String("out", "id/id_*_gen.go", "path to generated file")
	flag.Parse()
	if !strings.ContainsRune(*out, '*') {
		log.Fatal("out must contain a wildcard")
	}

	if *in == "" {
		log.Fatal("-in is required")
	}
	if *out == "" {
		log.Fatal("-out is required")
	}

	f, err := os.Open(*in)
	if err != nil {
		log.Fatalf("Error reading %s: %v", *in, err)
	}
	defer f.Close()

	rows, err := csv.NewReader(f).ReadAll()
	if err != nil {
		log.Fatalf("Error parsing %s: %v", *in, err)
	}

	for i := range rows {
		rows[i][0] = goName(rows[i][0])
	}

	groupedRows := map[string][][]string{}
	for _, row := range rows {
		nodeClass := row[2]
		groupedRows[nodeClass] = append(groupedRows[nodeClass], row)
	}

	for nodeClass, rows := range groupedRows {
		out := strings.ReplaceAll(*out, "*", nodeClass)
		var b bytes.Buffer
		if err := idTmpl.Execute(&b, struct {
			NodeClass string
			Rows      [][]string
		}{nodeClass, rows}); err != nil {
			log.Fatalf("Error generating code: %v", err)
		}

		bfmt, err := format.Source(b.Bytes())
		if err != nil {
			log.Fatalf("Error formatting code: %v", err)
		}

		if err := os.WriteFile(out, bfmt, 0644); err != nil {
			log.Fatalf("Error writing %s: %v", out, err)
		}
		log.Printf("Wrote %s", out)
	}

	{
		out := strings.ReplaceAll(*out, "*", "names")
		var b bytes.Buffer
		if err := nameTmpl.Execute(&b, maps.Keys(groupedRows)); err != nil {
			log.Fatalf("Error generating code: %v", err)
		}

		bfmt, err := format.Source(b.Bytes())
		if err != nil {
			log.Fatalf("Error formatting code: %v", err)
		}

		if err := os.WriteFile(out, bfmt, 0644); err != nil {
			log.Fatalf("Error writing %s: %v", out, err)
		}
		log.Printf("Wrote %s", out)
	}
}

var idTmpl = template.Must(template.New("").Parse(`
// Copyright 2018-2024 opcua authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.

// Code generated by cmd/id. DO NOT EDIT!

package id

const (
	{{range .Rows}}{{index . 0}} = {{index . 1}}
	{{end}}
)

var name{{.NodeClass}} = map[uint32]string{
	{{- range .Rows}}
	{{index . 1}}: "{{index . 0}}",
	{{- end}}
}
`))

var nameTmpl = template.Must(template.New("").Parse(`
// Copyright 2018-2024 opcua authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.

// Code generated by cmd/id. DO NOT EDIT!

package id

import "strconv"

func Name(id uint32) string {
	{{- range .}}
	if s, ok := name{{.}}[id]; ok {
		return s
	}
	{{- end}}
	return strconv.FormatUint(uint64(id), 10)
}
`))

func goName(s string) string {
	r1 := strings.NewReplacer(
		"Guid", "GUID",
		"Id", "ID",
		"Json", "JSON",
		"QualityOfService", "QoS",
		"Uadp", "UADP",
		"Uri", "URI",
		"Url", "URL",
		"Xml", "XML",
	)
	r2 := strings.NewReplacer(
		"IDentity", "Identity",
	)
	return r2.Replace(r1.Replace(s))
}
