package xml

import (
	
	
	
)

// NodeDecoder is a XML decoder wrapper that is responsible to decoding
// a single XML Node element and it's nested member elements. This wrapper decoder
// takes in the start element of the top level node being decoded.
type NodeDecoder struct {
	Decoder *xml.Decoder
	StartEl xml.StartElement
}

// WrapNodeDecoder returns an initialized XMLNodeDecoder
func ( *xml.Decoder,  xml.StartElement) NodeDecoder {
	return NodeDecoder{
		Decoder: ,
		StartEl: ,
	}
}

// Token on a Node Decoder returns a xml StartElement. It returns a boolean that indicates the
// a token is the node decoder's end node token; and an error which indicates any error
// that occurred while retrieving the start element
func ( NodeDecoder) () ( xml.StartElement,  bool,  error) {
	for {
		,  := .Decoder.Token()
		if  != nil {
			return , , 
		}

		// check if we reach end of the node being decoded
		if ,  := .(xml.EndElement);  {
			return ,  == .StartEl.End(), 
		}

		if ,  := .(xml.StartElement);  {
			return restoreAttrNamespaces(), false, 
		}

		// skip token if it is a comment or preamble or empty space value due to indentation
		// or if it's a value and is not expected
	}
}

// restoreAttrNamespaces update XML attributes to restore the short namespaces found within
// the raw XML document.
func ( xml.StartElement) xml.StartElement {
	if len(.Attr) == 0 {
		return 
	}

	// Generate a mapping of XML namespace values to their short names.
	 := map[string]string{}
	for ,  := range .Attr {
		if .Name.Space == "xmlns" {
			[.Value] = .Name.Local
			break
		}
	}

	for ,  := range .Attr {
		if .Name.Space == "xmlns" {
			continue
		}
		// By default, xml.Decoder will fully resolve these namespaces. So if you had <foo xmlns:bar=baz bar:bin=hi/>
		// then by default the second attribute would have the `Name.Space` resolved to `baz`. But we need it to
		// continue to resolve as `bar` so we can easily identify it later on.
		if ,  := [.Attr[].Name.Space];  {
			.Attr[].Name.Space = 
		}
	}
	return 
}

// GetElement looks for the given tag name at the current level, and returns the element if found, and
// skipping over non-matching elements. Returns an error if the node is not found, or if an error occurs while walking
// the document.
func ( NodeDecoder) ( string) ( xml.StartElement,  error) {
	for {
		, ,  := .Token()
		if  != nil {
			return , 
		}
		if  {
			return , fmt.Errorf("%s node not found", )
		}
		switch {
		case strings.EqualFold(, .Name.Local):
			return , nil
		default:
			 = .Decoder.Skip()
			if  != nil {
				return , 
			}
		}
	}
}

// Value provides an abstraction to retrieve char data value within an xml element.
// The method will return an error if it encounters a nested xml element instead of char data.
// This method should only be used to retrieve simple type or blob shape values as []byte.
func ( NodeDecoder) () ( []byte,  error) {
	,  := .Decoder.Token()
	if  != nil {
		return , 
	}

	 := .StartEl.End()

	switch ev := .(type) {
	case xml.CharData:
		 = .Copy()
	case xml.EndElement: // end tag or self-closing
		if  ==  {
			return []byte{}, 
		}
		return , fmt.Errorf("expected value for %v element, got %T type %v instead", .StartEl.Name.Local, , )
	default:
		return , fmt.Errorf("expected value for %v element, got %T type %v instead", .StartEl.Name.Local, , )
	}

	,  = .Decoder.Token()
	if  != nil {
		return , 
	}

	if ,  := .(xml.EndElement);  {
		if  ==  {
			return , 
		}
	}

	return , fmt.Errorf("expected end element %v, got %T type %v instead", , , )
}

// FetchRootElement takes in a decoder and returns the first start element within the xml body.
// This function is useful in fetching the start element of an XML response and ignore the
// comments and preamble
func ( *xml.Decoder) ( xml.StartElement,  error) {
	for {
		,  := .Token()
		if  != nil {
			return , 
		}

		if ,  := .(xml.StartElement);  {
			return , 
		}
	}
}