package ssdp

import (
	
	
	
	
	
	
	
	

	
	
)

// Monitor monitors SSDP's alive and byebye messages.
type Monitor struct {
	Alive  AliveHandler
	Bye    ByeHandler
	Search SearchHandler

	Options []Option

	conn *multicast.Conn
	wg   sync.WaitGroup
}

// Start starts to monitor SSDP messages.
func ( *Monitor) () error {
	,  := opts2config(.Options)
	if  != nil {
		return 
	}
	,  := multicast.Listen(multicast.RecvAddrResolver, .multicastConfig.options()...)
	if  != nil {
		return 
	}
	ssdplog.Printf("monitoring on %s", .LocalAddr().String())
	.conn = 
	.wg.Add(1)
	go func() {
		.serve()
		.wg.Done()
	}()
	return nil
}

func ( *Monitor) () error {
	// TODO: update listening interfaces of m.conn
	 := .conn.ReadPackets(0, func( net.Addr,  []byte) error {
		 := make([]byte, len())
		copy(, )
		go .handleRaw(, )
		return nil
	})
	if  != nil && !errors.Is(, io.EOF) {
		return 
	}
	return nil
}

func ( *Monitor) ( net.Addr,  []byte) error {
	// Add newline to workaround buggy SSDP responses
	if !bytes.HasSuffix(, endOfHeader) {
		 = bytes.Join([][]byte{, endOfHeader}, nil)
	}
	if bytes.HasPrefix(, []byte("M-SEARCH ")) {
		return .handleSearch(, )
	}
	if bytes.HasPrefix(, []byte("NOTIFY ")) {
		return .handleNotify(, )
	}
	 := bytes.Index(, []byte("\r\n"))
	ssdplog.Printf("unexpected method: %q", string([:]))
	return nil
}

func ( *Monitor) ( net.Addr,  []byte) error {
	,  := http.ReadRequest(bufio.NewReader(bytes.NewReader()))
	if  != nil {
		return 
	}
	switch  := .Header.Get("NTS");  {
	case "ssdp:alive":
		if .Method != "NOTIFY" {
			return fmt.Errorf("unexpected method for %q: %s", "ssdp:alive", .Method)
		}
		if  := .Alive;  != nil {
			(&AliveMessage{
				From:      ,
				Type:      .Header.Get("NT"),
				USN:       .Header.Get("USN"),
				Location:  .Header.Get("LOCATION"),
				Server:    .Header.Get("SERVER"),
				rawHeader: .Header,
			})
		}
	case "ssdp:byebye":
		if .Method != "NOTIFY" {
			return fmt.Errorf("unexpected method for %q: %s", "ssdp:byebye", .Method)
		}
		if  := .Bye;  != nil {
			(&ByeMessage{
				From:      ,
				Type:      .Header.Get("NT"),
				USN:       .Header.Get("USN"),
				rawHeader: .Header,
			})
		}
	default:
		return fmt.Errorf("unknown NTS: %s", )
	}
	return nil
}

func ( *Monitor) ( net.Addr,  []byte) error {
	,  := http.ReadRequest(bufio.NewReader(bytes.NewReader()))
	if  != nil {
		return 
	}
	 := .Header.Get("MAN")
	if  != `"ssdp:discover"` {
		return fmt.Errorf("unexpected MAN: %s", )
	}
	if  := .Search;  != nil {
		(&SearchMessage{
			From:      ,
			Type:      .Header.Get("ST"),
			rawHeader: .Header,
		})
	}
	return nil
}

// Close closes monitoring.
func ( *Monitor) () error {
	if .conn != nil {
		.conn.Close()
		.conn = nil
		.wg.Wait()
	}
	return nil
}

// AliveMessage represents SSDP's ssdp:alive message.
type AliveMessage struct {
	// From is a sender of this message
	From net.Addr

	// Type is a property of "NT"
	Type string

	// USN is a property of "USN"
	USN string

	// Location is a property of "LOCATION"
	Location string

	// Server is a property of "SERVER"
	Server string

	rawHeader http.Header
	maxAge    *int
}

// Header returns all properties in alive message.
func ( *AliveMessage) () http.Header {
	return .rawHeader
}

// MaxAge extracts "max-age" value from "CACHE-CONTROL" property.
func ( *AliveMessage) () int {
	if .maxAge == nil {
		.maxAge = new(int)
		*.maxAge = extractMaxAge(.rawHeader.Get("CACHE-CONTROL"), -1)
	}
	return *.maxAge
}

// AliveHandler is handler of Alive message.
type AliveHandler func(*AliveMessage)

// ByeMessage represents SSDP's ssdp:byebye message.
type ByeMessage struct {
	// From is a sender of this message
	From net.Addr

	// Type is a property of "NT"
	Type string

	// USN is a property of "USN"
	USN string

	rawHeader http.Header
}

// Header returns all properties in bye message.
func ( *ByeMessage) () http.Header {
	return .rawHeader
}

// ByeHandler is handler of Bye message.
type ByeHandler func(*ByeMessage)

// SearchMessage represents SSDP's ssdp:discover message.
type SearchMessage struct {
	From net.Addr
	Type string

	rawHeader http.Header
}

// Header returns all properties in search message.
func ( *SearchMessage) () http.Header {
	return .rawHeader
}

// SearchHandler is handler of Search message.
type SearchHandler func(*SearchMessage)