package main

import (
	"embed"
	"fmt"
	"go/format"
	"log"
	"os"
	"path/filepath"
	"sort"
	"sync"
)

//go:embed resources
var protocolsFS embed.FS

func main() {

	var outDir string
	if len(os.Args) > 1 {
		outDir = os.Args[1]
	} else {
		log.Fatal(`Usage: protocols protocol_out_dir [helpers_out_dir protocols_package ...interfaces]

Generates protocol bindings into protocol_out_dir
Optionally, generate helper functions into helpers_out_dir
Output helper function into helpers_out_dir (usually want this be a different package than protocols)
So if so, also provide protocols_package as the package name for protocols package like github.com/mmulet/term.everything/wayland/protocols
If you omit this we'll assume the helpers are in the same package as protocols

If you provide interfaces, we'll only generate those those helpers. separated by space.
`)
	}
	var helpersOutDir string

	if len(os.Args) > 2 {
		helpersOutDir = os.Args[2]
	}

	var currentDirInPackage string
	if len(os.Args) > 3 {
		currentDirInPackage = os.Args[3]
	} else {
		currentDirInPackage = "wayland/protocols"
	}

	fmt.Println(currentDirInPackage)

	protocolsPackage := filepath.Join(currentDirInPackage, outDir)
	protocolsPackage = filepath.Clean(protocolsPackage)

	fmt.Println(protocolsPackage)

	var interfacesToGenHelpersFor []string
	if len(os.Args) > 4 {
		for i := 4; i < len(os.Args); i++ {
			interfacesToGenHelpersFor = append(interfacesToGenHelpersFor, os.Args[i])
		}
	}

	entries, err := protocolsFS.ReadDir("resources")
	if err != nil {
		log.Fatalf("read embedded dir resources: %v", err)
	}

	var files []string
	for _, e := range entries {
		if e.IsDir() {
			continue
		}
		files = append(files, e.Name())
	}
	sort.Strings(files)

	results := make([]BuildProtocolOut, len(files))
	var wg sync.WaitGroup
	errOnce := make(chan error, 1)
	for i, f := range files {
		wg.Add(1)
		go func(idx int, file string) {
			defer wg.Done()
			out, err := buildProtocol(protocolsFS, file, protocolsPackage, interfacesToGenHelpersFor)
			if err != nil {
				select {
				case errOnce <- err:
				default:
				}
				return
			}
			results[idx] = out
		}(i, f)
	}
	wg.Wait()
	select {
	case e := <-errOnce:
		log.Fatal(e)
	default:
	}

	writeOutputFiles := func(outDir string, results []BuildProtocolOut, helper bool) {
		pkg := filepath.Base(outDir)

		var additionalImport string
		if helper {
			additionalImport = fmt.Sprintf(`import "%s"`, protocolsPackage)
		} else {
			additionalImport = `import "fmt"`
		}

		outFile := fmt.Sprintf(`// Code generated by `+"`cmd/protocols`"+`; DO NOT EDIT.

	package %s

	`, pkg)

		if err := os.MkdirAll(outDir, 0755); err != nil {
			log.Fatalf("create dir %s: %v", outDir, err)
		}

		for i, s := range results {
			var out string
			var dest string
			if helper {
				if s.HelperFile == "" {
					out = outFile
				} else {
					out = outFile + additionalImport + "\n" + s.HelperFile
				}

				dest = filepath.Join(outDir, filepath.Base(files[i])+".helper.go")

			} else {
				out = outFile + additionalImport + "\n" + s.ProtocolFile
				dest = filepath.Join(outDir, filepath.Base(files[i])+".go")

			}
			formatted, err := format.Source([]byte(out))
			if err != nil {
				log.Fatalf("format %s: %v", dest, err)
			}

			if err := os.WriteFile(dest, formatted, 0o644); err != nil {
				log.Fatalf("write %s: %v", dest, err)
			}

			fmt.Println("wrote", dest)

		}
	}

	writeOutputFiles(outDir, results, false)

	if helpersOutDir == "" {
		return
	}
	helpersOutDir, err = filepath.Abs(helpersOutDir)
	if err != nil {
		log.Fatalf("abs path for helpersOutDir: %v", err)
	}
	print("Generating helpers into ", helpersOutDir, "\n")

	writeOutputFiles(helpersOutDir, results, true)

}
