// This file contains XML structures for communicating with UPnP devices.

package goupnp

import (
	
	
	
	
	
	

	
	
)

const (
	DeviceXMLNamespace = "urn:schemas-upnp-org:device-1-0"
)

// RootDevice is the device description as described by section 2.3 "Device
// description" in
// http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf
type RootDevice struct {
	XMLName     xml.Name    `xml:"root"`
	SpecVersion SpecVersion `xml:"specVersion"`
	URLBase     url.URL     `xml:"-"`
	URLBaseStr  string      `xml:"URLBase"`
	Device      Device      `xml:"device"`
}

// SetURLBase sets the URLBase for the RootDevice and its underlying components.
func ( *RootDevice) ( *url.URL) {
	.URLBase = *
	.URLBaseStr = .String()
	.Device.SetURLBase()
}

// SpecVersion is part of a RootDevice, describes the version of the
// specification that the data adheres to.
type SpecVersion struct {
	Major int32 `xml:"major"`
	Minor int32 `xml:"minor"`
}

// Device is a UPnP device. It can have child devices.
type Device struct {
	DeviceType       string    `xml:"deviceType"`
	FriendlyName     string    `xml:"friendlyName"`
	Manufacturer     string    `xml:"manufacturer"`
	ManufacturerURL  URLField  `xml:"manufacturerURL"`
	ModelDescription string    `xml:"modelDescription"`
	ModelName        string    `xml:"modelName"`
	ModelNumber      string    `xml:"modelNumber"`
	ModelType        string    `xml:"modelType"`
	ModelURL         URLField  `xml:"modelURL"`
	SerialNumber     string    `xml:"serialNumber"`
	UDN              string    `xml:"UDN"`
	UPC              string    `xml:"UPC,omitempty"`
	Icons            []Icon    `xml:"iconList>icon,omitempty"`
	Services         []Service `xml:"serviceList>service,omitempty"`
	Devices          []Device  `xml:"deviceList>device,omitempty"`

	// Extra observed elements:
	PresentationURL URLField `xml:"presentationURL"`
}

// VisitDevices calls visitor for the device, and all its descendent devices.
func ( *Device) ( func(*Device)) {
	()
	for  := range .Devices {
		.Devices[].()
	}
}

// VisitServices calls visitor for all Services under the device and all its
// descendent devices.
func ( *Device) ( func(*Service)) {
	.VisitDevices(func( *Device) {
		for  := range .Services {
			(&.Services[])
		}
	})
}

// FindService finds all (if any) Services under the device and its descendents
// that have the given ServiceType.
func ( *Device) ( string) []*Service {
	var  []*Service
	.VisitServices(func( *Service) {
		if .ServiceType ==  {
			 = append(, )
		}
	})
	return 
}

// SetURLBase sets the URLBase for the Device and its underlying components.
func ( *Device) ( *url.URL) {
	.ManufacturerURL.SetURLBase()
	.ModelURL.SetURLBase()
	.PresentationURL.SetURLBase()
	for  := range .Icons {
		.Icons[].SetURLBase()
	}
	for  := range .Services {
		.Services[].SetURLBase()
	}
	for  := range .Devices {
		.Devices[].()
	}
}

func ( *Device) () string {
	return fmt.Sprintf("Device ID %s : %s (%s)", .UDN, .DeviceType, .FriendlyName)
}

// Icon is a representative image that a device might include in its
// description.
type Icon struct {
	Mimetype string   `xml:"mimetype"`
	Width    int32    `xml:"width"`
	Height   int32    `xml:"height"`
	Depth    int32    `xml:"depth"`
	URL      URLField `xml:"url"`
}

// SetURLBase sets the URLBase for the Icon.
func ( *Icon) ( *url.URL) {
	.URL.SetURLBase()
}

// Service is a service provided by a UPnP Device.
type Service struct {
	ServiceType string   `xml:"serviceType"`
	ServiceId   string   `xml:"serviceId"`
	SCPDURL     URLField `xml:"SCPDURL"`
	ControlURL  URLField `xml:"controlURL"`
	EventSubURL URLField `xml:"eventSubURL"`
}

// SetURLBase sets the URLBase for the Service.
func ( *Service) ( *url.URL) {
	.SCPDURL.SetURLBase()
	.ControlURL.SetURLBase()
	.EventSubURL.SetURLBase()
}

func ( *Service) () string {
	return fmt.Sprintf("Service ID %s : %s", .ServiceId, .ServiceType)
}

// RequestSCPDCtx requests the SCPD (soap actions and state variables description)
// for the service.
func ( *Service) ( context.Context) (*scpd.SCPD, error) {
	if !.SCPDURL.Ok {
		return nil, errors.New("bad/missing SCPD URL, or no URLBase has been set")
	}
	 := new(scpd.SCPD)
	if  := requestXml(, .SCPDURL.URL.String(), scpd.SCPDXMLNamespace, );  != nil {
		return nil, 
	}
	return , nil
}

// RequestSCPD is the legacy version of RequestSCPDCtx, but uses
// context.Background() as the context.
func ( *Service) () (*scpd.SCPD, error) {
	return .RequestSCPDCtx(context.Background())
}

// RequestSCDP is for compatibility only, prefer RequestSCPD. This was a
// misspelling of RequestSCDP.
func ( *Service) () (*scpd.SCPD, error) {
	return .RequestSCPD()
}

func ( *Service) () *soap.SOAPClient {
	return soap.NewSOAPClient(.ControlURL.URL)
}

// URLField is a URL that is part of a device description.
type URLField struct {
	URL url.URL `xml:"-"`
	Ok  bool    `xml:"-"`
	Str string  `xml:",chardata"`
}

func ( *URLField) ( *url.URL) {
	 := .Str
	if !strings.Contains(, "://") && !strings.HasPrefix(, "/") {
		 = "/" + 
	}

	,  := url.Parse()
	if  != nil {
		.URL = url.URL{}
		.Ok = false
		return
	}

	.URL = *.ResolveReference()
	.Ok = true
}