package optimizer

import "strings"

// passStoreReload removes redundant reloads immediately after stores.
// Pattern: sta X; ...(non-A-modifying)...; lda X  → remove lda
func passStoreReload(lines []asmLine, cfg *Config) []asmLine {
	var result []asmLine

	for i := 0; i < len(lines); i++ {
		line := lines[i]

		if line.isCode && line.opcode == "sta" && isSafeStldOperand(line.operand, cfg) {
			res := findMatchingLoad(lines, i+1, line.operand, cfg)
			if res.found {
				result = append(result, line)
				for j := i + 1; j < res.idx; j++ {
					result = append(result, lines[j])
				}
				i = res.idx // skip the lda on next iteration
				continue
			}
		}

		result = append(result, line)
	}

	return result
}

type matchResult struct {
	idx   int
	found bool
}

// findMatchingLoad scans forward from `start` for `lda operand`.
// Only passes through comments and non-A-modifying code.
func findMatchingLoad(lines []asmLine, start int, operand string, cfg *Config) matchResult {
	for i := start; i < len(lines); i++ {
		l := lines[i]

		if l.optMarker || l.isComment {
			continue
		}
		if l.isLabel {
			return matchResult{}
		}
		if l.isCode && l.opcode == "lda" && l.operand == operand && isSafeStldOperand(l.operand, cfg) {
			return matchResult{i, true}
		}
		if modifiesA(l) {
			return matchResult{}
		}
	}
	return matchResult{}
}

// modifiesA returns true if the instruction puts a new value into A.
func modifiesA(line asmLine) bool {
	if !line.isCode {
		return false
	}
	switch line.opcode {
	case "lda", "adc", "sbc", "and", "ora", "eor", "pla", "txa", "tya":
		return true
	case "asl", "lsr", "rol", "ror":
		return line.operand == "" || line.operand == "a"
	case "inc", "dec":
		return false
	default:
		return false
	}
}

// isSafeStldOperand returns true if the operand is safe for store-then-reload optimization.
// Unsafe: I/O addresses (in cfg.IOMap), self-modifying code pattern (+N), indexed/indirect.
func isSafeStldOperand(operand string, cfg *Config) bool {
	if strings.ContainsAny(operand, "(,+") {
		return false
	}

	// Check if operand is a direct hex address in an I/O region
	if strings.HasPrefix(operand, "$") {
		addr := parseHexOrDec(operand)
		if addr >= 0 && addr < 65536 && cfg.IOMap[addr] {
			return false
		}
	}

	return true
}
