package optimizer

import "strings"

// Optimize applies all enabled peephole passes to the generated ASM lines.
// Each pass runs sequentially on a parsed representation of the lines.
// @@OPT markers are stripped from output.
// Returns optimized lines and the set of dissolved REGISTER variable names
// (variables that had all their stores/loads eliminated).
func Optimize(lines []string, cfg *Config) ([]string, map[string]bool) {
	dissolved := map[string]bool{}

	if cfg == nil || !cfg.Any() {
		return lines, dissolved
	}

	parsed := parseLines(lines)
	original := parsed

	if cfg.EnableStoreLoad {
		parsed = passStoreReload(parsed, cfg)
	}
	if cfg.EnableLoad {
		parsed = passLoadElimination(parsed, cfg)
	}
	if cfg.EnableImm {
		parsed = passImmElimination(parsed)
	}
	if cfg.EnableJmp {
		parsed = passJmpNext(parsed)
	}
	if cfg.EnableSelf {
		parsed = passSelfAssignment(parsed)
	}
	if cfg.EnableRegisterVars && len(cfg.RegisterVars) > 0 {
		parsed = passRegDead(parsed, cfg.RegisterVars)
	}

	parsed = stripOptMarkers(parsed)
	if cfg.Debug {
		original = stripOptMarkers(original)
		parsed = debugDiff(original, parsed)
	}

	resultLines := linesToString(parsed)

	if len(cfg.RegisterVars) > 0 {
		dissolved = computeDissolved(resultLines, cfg.RegisterVars)
	}

	return resultLines, dissolved
}

// computeDissolved finds REGISTER variables that have no remaining references
// in the output and can have their memory allocation elided.
func computeDissolved(outputLines []string, registerVars map[string]bool) map[string]bool {
	dissolved := map[string]bool{}
	for name := range registerVars {
		dissolved[name] = true
	}
	for _, line := range outputLines {
		for name := range dissolved {
			if strings.Contains(line, name) {
				delete(dissolved, name)
			}
		}
		if len(dissolved) == 0 {
			break
		}
	}
	return dissolved
}
