package dns

import (
	
	
	
	
	
)

// Parse the $GENERATE statement as used in BIND9 zones.
// See http://www.zytrax.com/books/dns/ch8/generate.html for instance.
// We are called after '$GENERATE '. After which we expect:
// * the range (12-24/2)
// * lhs (ownername)
// * [[ttl][class]]
// * type
// * rhs (rdata)
// But we are lazy here, only the range is parsed *all* occurrences
// of $ after that are interpreted.
func ( *ZoneParser) ( lex) (RR, bool) {
	 := .token
	 := int64(1)
	if  := strings.IndexByte(, '/');  >= 0 {
		if +1 == len() {
			return .setParseError("bad step in $GENERATE range", )
		}

		,  := strconv.ParseInt([+1:], 10, 64)
		if  != nil ||  <= 0 {
			return .setParseError("bad step in $GENERATE range", )
		}

		 = 
		 = [:]
	}

	, ,  := strings.Cut(, "-")
	if ! {
		return .setParseError("bad start-stop in $GENERATE range", )
	}

	,  := strconv.ParseInt(, 10, 64)
	if  != nil {
		return .setParseError("bad start in $GENERATE range", )
	}

	,  := strconv.ParseInt(, 10, 64)
	if  != nil {
		return .setParseError("bad stop in $GENERATE range", )
	}
	if  < 0 ||  < 0 ||  <  || (-)/ > 65535 {
		return .setParseError("bad range in $GENERATE range", )
	}

	// _BLANK
	,  = .c.Next()
	if ! || .value != zBlank {
		return .setParseError("garbage after $GENERATE range", )
	}

	// Create a complete new string, which we then parse again.
	var  string
	for ,  := .c.Next(); ; ,  = .c.Next() {
		if .err {
			return .setParseError("bad data in $GENERATE directive", )
		}
		if .value == zNewline {
			break
		}

		 += .token
	}

	 := &generateReader{
		s: ,

		cur:   ,
		start: ,
		end:   ,
		step:  ,

		file: .file,
		lex:  &,
	}
	.sub = NewZoneParser(, .origin, .file)
	.sub.includeDepth, .sub.includeAllowed = .includeDepth, .includeAllowed
	.sub.generateDisallowed = true
	.sub.SetDefaultTTL(defaultTtl)
	return .subNext()
}

type generateReader struct {
	s  string
	si int

	cur   int64
	start int64
	end   int64
	step  int64

	mod bytes.Buffer

	escape bool

	eof bool

	file string
	lex  *lex
}

func ( *generateReader) ( string,  int) *ParseError {
	.eof = true // Make errors sticky.

	 := *.lex
	.token = .s[.si-1 : ]
	.column += .si // l.column starts one zBLANK before r.s

	return &ParseError{file: .file, err: , lex: }
}

func ( *generateReader) ( []byte) (int, error) {
	// NewZLexer, through NewZoneParser, should use ReadByte and
	// not end up here.

	panic("not implemented")
}

func ( *generateReader) () (byte, error) {
	if .eof {
		return 0, io.EOF
	}
	if .mod.Len() > 0 {
		return .mod.ReadByte()
	}

	if .si >= len(.s) {
		.si = 0
		.cur += .step

		.eof = .cur > .end || .cur < 0
		return '\n', nil
	}

	 := .si
	.si++

	switch .s[] {
	case '\\':
		if .escape {
			.escape = false
			return '\\', nil
		}

		.escape = true
		return .()
	case '$':
		if .escape {
			.escape = false
			return '$', nil
		}

		 := "%d"

		if  >= len(.s)-1 {
			// End of the string
			fmt.Fprintf(&.mod, , .cur)
			return .mod.ReadByte()
		}

		if .s[+1] == '$' {
			.si++
			return '$', nil
		}

		var  int64

		// Search for { and }
		if .s[+1] == '{' {
			// Modifier block
			 := strings.Index(.s[+2:], "}")
			if  < 0 {
				return 0, .parseError("bad modifier in $GENERATE", len(.s))
			}

			var  string
			, ,  = modToPrintf(.s[+2 : +2+])
			if  != "" {
				return 0, .parseError(, +3+)
			}
			if .start+ < 0 || .end+ > 1<<31-1 {
				return 0, .parseError("bad offset in $GENERATE", +3+)
			}

			.si += 2 +  // Jump to it
		}

		fmt.Fprintf(&.mod, , .cur+)
		return .mod.ReadByte()
	default:
		if .escape { // Pretty useless here
			.escape = false
			return .()
		}

		return .s[], nil
	}
}

// Convert a $GENERATE modifier 0,0,d to something Printf can deal with.
func modToPrintf( string) (string, int64, string) {
	// Modifier is { offset [ ,width [ ,base ] ] } - provide default
	// values for optional width and type, if necessary.
	, ,  := strings.Cut(, ",")
	, ,  := strings.Cut(, ",")
	, ,  := strings.Cut(, ",")
	if ! {
		 = "0"
	}
	if ! {
		 = "d"
	}
	if  {
		return "", 0, "bad modifier in $GENERATE"
	}

	switch  {
	case "o", "d", "x", "X":
	default:
		return "", 0, "bad base in $GENERATE"
	}

	,  := strconv.ParseInt(, 10, 64)
	if  != nil {
		return "", 0, "bad offset in $GENERATE"
	}

	,  := strconv.ParseUint(, 10, 8)
	if  != nil {
		return "", 0, "bad width in $GENERATE"
	}

	if  == 0 {
		return "%" + , , ""
	}

	return "%0" +  + , , ""
}