package asm

import "slices"

// patcher maintains patches to a collection of bytes.
type patcher struct {
	code         []byte
	labels       map[string]int
	earlyPatches []*patch
	latePatches  []*patch
}

// ApplyPatches applies the given patches to the code.
func (p *patcher) ApplyPatches(patches []*patch) {
restart:
	for i, patch := range patches {
		start := patch.start
		end := patch.end
		oldSize := end - start
		new := patch.apply(p.code[start:end])
		newSize := len(new)
		shift := newSize - oldSize

		if shift == 0 {
			continue
		}

		patch.end += shift

		for label, address := range p.labels {
			if address >= end {
				p.labels[label] += shift
			}
		}

		for _, following := range patches[i+1:] {
			if following.start >= end {
				following.start += shift
				following.end += shift
			}
		}

		for _, following := range p.latePatches {
			if following.start >= end {
				following.start += shift
				following.end += shift
			}
		}

		left := p.code[:start]
		right := p.code[end:]
		p.code = slices.Concat(left, new, right)
		goto restart
	}
}

// PatchLast4Bytes creates a late patch for the last 4 bytes.
func (p *patcher) PatchLast4Bytes() *patch {
	patch := &patch{
		start: len(p.code) - 4,
		end:   len(p.code),
	}

	p.latePatches = append(p.latePatches, patch)
	return patch
}