// Package conversion contains functions to import ROS definition into Golang.
package conversion

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"
)

var tplPackage = template.Must(template.New("").Parse(
	`// Package {{ .PkgName }} contains message definitions.
//
//autogenerated:yes
//nolint:revive
package {{ .PkgName }}
`))

func writeTemplate(fpath string, tpl *template.Template, args map[string]interface{}) error {
	f, err := os.Create(fpath)
	if err != nil {
		return err
	}
	defer f.Close()

	return tpl.Execute(f, args)
}

// ImportPackage generates Go files for all ROS definitions under the directory.
func ImportPackage(name string, rosDir string, goDir string) error {
	os.Mkdir(goDir, 0o755)
	err := writeTemplate(filepath.Join(goDir, "package.go"), tplPackage,
		map[string]interface{}{"PkgName": name})
	if err != nil {
		return err
	}

	return filepath.Walk(rosDir, func(filePath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		switch path.Ext(info.Name()) {
		case ".msg":
			outpath := filepath.Join(goDir, "msg_"+strings.TrimSuffix(strings.ToLower(info.Name()), ".msg")+".go")
			f, err := os.Create(outpath)
			if err != nil {
				return err
			}
			defer f.Close()
			if err := ImportMessage(filePath, name, name, f); err != nil {
				os.Remove(outpath)
				return err
			}

		case ".srv":
			outpath := filepath.Join(goDir, "srv_"+strings.TrimSuffix(strings.ToLower(info.Name()), ".srv")+".go")
			f, err := os.Create(outpath)
			if err != nil {
				return err
			}
			defer f.Close()
			if err := ImportService(filePath, name, name, f); err != nil {
				os.Remove(outpath)
				return err
			}

		case ".action":
			outpath := filepath.Join(goDir, "action_"+strings.TrimSuffix(strings.ToLower(info.Name()), ".action")+".go")
			f, err := os.Create(outpath)
			if err != nil {
				return err
			}
			defer f.Close()
			if err := ImportAction(filePath, name, name, f); err != nil {
				os.Remove(outpath)
				return err
			}
		}

		return nil
	})
}

// ImportPackageRecursive generates Go files for all ROS definitions under the directory if it's a ROS package.
func ImportPackageRecursive(dir string) error {
	// find folders which contain a "msg", "srv" or "action" subfolder
	paths := make(map[string]struct{})
	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() &&
			(info.Name() == "msg" || info.Name() == "srv" || info.Name() == "action") {
			paths[filepath.Dir(path)] = struct{}{}
			return nil
		}
		return nil
	})
	if err != nil {
		return err
	}

	if len(paths) == 0 {
		return fmt.Errorf("no ROS packages found in '%s'."+
			" A ROS package must contain at least a subfolder named 'msg', 'srv' or 'action'", dir)
	}

	for path := range paths {
		pkgName := filepath.Base(path)
		err := ImportPackage(pkgName, path, pkgName)
		if err != nil {
			return err
		}
	}

	return nil
}
