module util

import strings

// Surrounder is an utility to help you manage a stack of additions, that
// should be done *both* _before_ and _after_ a given piece of generated
// code, in a synchronised way. It does so by forcing you to put the
// creation/destruction samples next to each other, so that it is easier to
// keep them in sync and spot errors.
// Note: Surrounder writes the creation samples (`befores`) in the _same_ order
// they were added, and the destruction samples (`afters`) in _reverse_ order.
//
// Usage:
// ```v
// mut sr := new_surrounder(2) // just a rough initial guess; it can grow
// sr.add('string tmp1 = ...;', 'string_free(&tmp1);')
// sr.add('string tmp2 = ...;', 'string_free(&tmp2);')
// ..
// sr.builder_write_befores(mut some_string_builder)
// some_string_builder.writeln('MIDDLE_that_uses_tmp1_and_tmp2')
// sr.builder_write_afters(mut some_string_builder)
// ```
// ... will produce this in `some_string_builder`:
// ```
// string tmp1 = ...;
// string tmp2 = ...;
// MIDDLE_that_uses_tmp1_and_tmp2
// string_free(&tmp2);
// string_free(&tmp1);
// ```

@[noinit]
pub struct Surrounder {
mut:
	befores []string
	afters  []string
}

// new_surrounder creates a new Surrounder instance.
// The expected_length is a hint for the initial capacity for the
// befores/afters stacks.
pub fn new_surrounder(expected_length int) Surrounder {
	return Surrounder{
		befores: []string{cap: expected_length}
		afters: []string{cap: expected_length}
	}
}

// add appends a `before`/`after` pair to the surrounder
// When you call .after() or .builder_write_afters(),
// all `before` parts will be in order, while all `after`
// parts, will be in reverse order.
pub fn (mut s Surrounder) add(before string, after string) {
	s.befores << before
	s.afters << after
}

// before returns all the `before` parts that were accumulated so far
@[manualfree]
pub fn (s &Surrounder) before() string {
	len := s.befores.len
	if len > 0 {
		mut res := strings.new_builder(len * 100)
		defer {
			unsafe { res.free() }
		}
		for i := 0; i < len; i++ {
			x := s.befores[i]
			if x.len > 0 {
				res.writeln(x)
			}
		}
		ret := res.str()
		return ret
	}
	return ''
}

// after returns all the `after` parts that were accumulated so far,
// in reverse order of their addition.
@[manualfree]
pub fn (s &Surrounder) after() string {
	len := s.afters.len
	if len > 0 {
		mut res := strings.new_builder(len * 100)
		defer {
			unsafe { res.free() }
		}
		for i := len - 1; i >= 0; i-- {
			x := s.afters[i]
			if x.len > 0 {
				res.writeln(x)
			}
		}
		ret := res.str()
		return ret
	}
	return ''
}

// builder_write_befores writeln-es all the `before` parts into the given
// string builder `sb`.
pub fn (s &Surrounder) builder_write_befores(mut sb strings.Builder) {
	len := s.befores.len
	if len > 0 {
		for i := 0; i < len; i++ {
			x := s.befores[i]
			if x.len > 0 {
				sb.writeln(x)
			}
		}
	}
}

// builder_write_afters writeln-es all the `after` parts into the given
// string builder `sb`. They will be written there in reverse order, compared
// to how/when they were added.
pub fn (s &Surrounder) builder_write_afters(mut sb strings.Builder) {
	len := s.afters.len
	if len > 0 {
		for i := len - 1; i >= 0; i-- {
			x := s.afters[i]
			if x.len > 0 {
				sb.writeln(x)
			}
		}
	}
}

// free frees the private resources associated with the surrounder instance
// Called automatically by `-autofree`, or in `[manualfree]` tagged functions.
@[unsafe]
pub fn (mut s Surrounder) free() {
	unsafe {
		s.befores.free()
		s.afters.free()
	}
}
