package svg

import 

const (
	maxSupportedExternalDoctypeBytes = 256
	maxExternalDoctypePrologBytes    = 16 << 10
)

var (
	svg10PublicID = []byte("-//W3C//DTD SVG 1.0//EN")
	svg10SystemID = []byte("http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd")
	svg11PublicID = []byte("-//W3C//DTD SVG 1.1//EN")
	svg11SystemID = []byte("http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd")
)

// IsSupportedExternalDoctype reports whether directive is the encoding/xml
// token for one canonical W3C SVG 1.0 or SVG 1.1 external DOCTYPE declaration
// in document's raw prolog. Requiring both inputs prevents encoding/xml's
// directive normalization from widening the accepted raw syntax. It accepts no
// internal subset and performs no DTD resolution. Callers remain responsible
// for enforcing that the declaration occurs at most once and before the root.
func (,  []byte) bool {
	if ,  := parseSupportedExternalDoctype(, false); ! {
		return false
	}
	return hasSupportedExternalDoctypeInProlog()
}

func hasSupportedExternalDoctypeInProlog( []byte) bool {
	 := len()
	if  > maxExternalDoctypePrologBytes {
		 = maxExternalDoctypePrologBytes
	}
	 := [:]
	 := 0
	if bytes.HasPrefix(, []byte{0xef, 0xbb, 0xbf}) {
		 = 3
	}
	for  < len() {
		for  < len() && isXMLSpace([]) {
			++
		}
		switch {
		case bytes.HasPrefix([:], []byte("<!--")):
			 := bytes.Index([+4:], []byte("-->"))
			if  < 0 {
				return false
			}
			 += 4 +  + 3
		case bytes.HasPrefix([:], []byte("<?")):
			 := bytes.Index([+2:], []byte("?>"))
			if  < 0 {
				return false
			}
			 += 2 +  + 2
		case bytes.HasPrefix([:], []byte("<!DOCTYPE")):
			,  := parseSupportedExternalDoctype([:], true)
			return 
		default:
			return false
		}
	}
	return false
}

func parseSupportedExternalDoctype( []byte,  bool) (int, bool) {
	if ! && len() > maxSupportedExternalDoctypeBytes {
		return 0, false
	}
	 := len()
	if  > maxSupportedExternalDoctypeBytes+1 {
		 = maxSupportedExternalDoctypeBytes + 1
	}
	 := externalDoctypeParser{input: [:]}
	if  && !.consume("<!") {
		return 0, false
	}
	if !.consume("DOCTYPE") || !.skipRequiredSpace() || !.consume("svg") ||
		!.skipRequiredSpace() || !.consume("PUBLIC") || !.skipRequiredSpace() {
		return 0, false
	}
	,  := .quoted()
	if ! || !.skipRequiredSpace() {
		return 0, false
	}
	,  := .quoted()
	if ! {
		return 0, false
	}
	.skipSpace()
	if  {
		if !.consume(">") || .offset > maxSupportedExternalDoctypeBytes {
			return 0, false
		}
	} else if .offset != len(.input) {
		return 0, false
	}
	 := bytes.Equal(, svg10PublicID) && bytes.Equal(, svg10SystemID) ||
		bytes.Equal(, svg11PublicID) && bytes.Equal(, svg11SystemID)
	return .offset, 
}

type externalDoctypeParser struct {
	input  []byte
	offset int
}

func ( *externalDoctypeParser) ( string) bool {
	if !bytes.HasPrefix(.input[.offset:], []byte()) {
		return false
	}
	.offset += len()
	return true
}

func ( *externalDoctypeParser) () bool {
	 := .offset
	.skipSpace()
	return .offset != 
}

func ( *externalDoctypeParser) () {
	for .offset < len(.input) && isXMLSpace(.input[.offset]) {
		.offset++
	}
}

func ( *externalDoctypeParser) () ([]byte, bool) {
	if .offset >= len(.input) || .input[.offset] != '\'' && .input[.offset] != '"' {
		return nil, false
	}
	 := .input[.offset]
	.offset++
	 := .offset
	for .offset < len(.input) && .input[.offset] !=  {
		.offset++
	}
	if .offset >= len(.input) {
		return nil, false
	}
	 := .input[:.offset]
	.offset++
	return , true
}

func isXMLSpace( byte) bool {
	switch  {
	case ' ', '\t', '\r', '\n':
		return true
	default:
		return false
	}
}