package rosa

import (
	"slices"
	"strings"
)

var _make = H("make")

// MakeHelper wraps the make program and its surrounding build system.
type MakeHelper struct {
	// Do not include default extras.
	OmitDefaults bool

	// Command to generate the build system.
	Generate string
	// Runs before make.
	ScriptMakeEarly string
	// Runs before check.
	ScriptCheckEarly string
	// Runs after install.
	Script string

	// Remain in current directory.
	InPlace bool

	// Whether to skip running the configure script.
	SkipConfigure bool
	// Alternative name for the configure script.
	ConfigureName string
	// Add autoconf arguments regardless of ConfigureName.
	ForceAutoconf bool
	// Flags passed to the configure script.
	Configure []KV
	// Host target triple, zero value is equivalent to the Rosa OS triple.
	Host string
	// Target triple, zero value is equivalent to the Rosa OS triple.
	Build string

	// Extra make targets.
	Make []string
	// Whether to skip the check target.
	SkipCheck bool
	// Whether to skip the check target during stage0.
	SkipCheckEarly bool
	// Name of the check target, zero value is equivalent to "check".
	Check []string
	// Replaces the default install command.
	Install string
}

var _ Helper = new(MakeHelper)

// extra returns make and other optional dependencies.
func (attr *MakeHelper) extra(flag int) P {
	extra := P{_make}
	if (attr == nil || !attr.OmitDefaults) && flag&TEarly == 0 {
		extra = append(extra,
			_awk,
			_coreutils,
		)
	}
	return extra
}

// wantsChmod returns whether the build system needs to be generated.
func (attr *MakeHelper) wantsChmod() bool {
	return attr != nil && attr.Generate != ""
}

// wantsWrite is equivalent to wantsChmod.
func (attr *MakeHelper) wantsWrite() bool { return attr.wantsChmod() }

// scriptEarly returns the optional build system generation segment.
func (attr *MakeHelper) scriptEarly() string {
	if attr == nil {
		return ""
	}

	generate := attr.Generate
	if len(generate) > 0 && generate[len(generate)-1] != '\n' {
		generate += "\n"
	}
	return generate
}

// wantsDir requests a new directory in TMPDIR, or omits the cd statement if InPlace.
func (attr *MakeHelper) wantsDir() (string, bool) {
	if attr != nil && attr.InPlace {
		return helperInPlace, false
	}
	return "/cure/", true
}

// script generates the cure script.
func (attr *MakeHelper) script(t Toolchain, name string) string {
	if attr == nil {
		attr = new(MakeHelper)
	}

	var configure string
	if !attr.SkipConfigure {
		configure = attr.ConfigureName
		if configure == "" {
			configure += `/usr/src/` + name + `/configure`
		}
		if attr.ForceAutoconf || attr.ConfigureName == "" {
			configure += ` \
	--prefix=/system`
			host := `"${ROSA_TRIPLE}"`
			if attr.Host != "" {
				host = attr.Host
			}
			if attr.Host != `""` {
				configure += ` \
	--host=` + host
			}

			build := `"${ROSA_TRIPLE}"`
			if attr.Build != "" {
				build = attr.Build
			}
			if attr.Build != `""` {
				configure += ` \
	--build=` + build
			}
		}

		if len(attr.Configure) > 0 {
			const sep = " \\\n\t"
			configure += sep + strings.Join(
				slices.Collect(func(yield func(string) bool) {
					for _, v := range attr.Configure {
						s := v[0]
						if v[0] != "" &&
							v[0][0] >= 'a' &&
							v[0][0] <= 'z' {
							s = "--" + s
						} else if len(v[0]) > 1 &&
							v[0][0] == 'D' &&
							v[0][1] >= 'a' &&
							v[0][1] <= 'z' {
							s = "-" + s
						}
						if v[1] != "" {
							if v[0] != "" {
								s += "="
							}
							s += v[1]
						}
						if !yield(s) {
							return
						}
					}
				}),
				sep,
			)
		}
	}

	scriptMake := `
make \
	` + jobsFlagE + ` \
	` + loadFlagE
	if len(attr.Make) > 0 {
		scriptMake += " \\\n\t" + strings.Join(attr.Make, " \\\n\t")
	}
	scriptMake += "\n"

	if !attr.SkipCheck && t.opts&OptSkipCheck == 0 &&
		(!attr.SkipCheckEarly || !t.stage.isStage0()) {
		scriptMake += attr.ScriptCheckEarly + `make \
	` + jobsFlagE + ` \
	` + loadFlagE + ` \
	`
		if len(attr.Check) > 0 {
			scriptMake += strings.Join(attr.Check, " \\\n\t")
		} else {
			scriptMake += "check"
		}
		scriptMake += "\n"
	}

	scriptInstall := attr.Install
	if scriptInstall == "" {
		scriptInstall = "make DESTDIR=/work install"
	}
	scriptInstall += "\n"

	return configure +
		attr.ScriptMakeEarly +
		scriptMake +
		scriptInstall +
		attr.Script
}
