//go:build !linux

package multicast

import (
	"net"
	"strconv"
	"syscall"
	"time"

	"golang.org/x/net/ipv4"
)

const (
	// same size as GStreamer's rtspsrc
	multicastTTL = 16
)

// singleConn is a multicast connection
// that works on a single interface.
type singleConn struct {
	addr   *net.UDPAddr
	conn   *net.UDPConn
	connIP *ipv4.PacketConn
}

// NewSingleConn allocates a single-interface multicast connection.
func NewSingleConn(
	intf *net.Interface,
	address string,
	listenPacket func(network, address string) (net.PacketConn, error),
) (Conn, error) {
	addr, err := net.ResolveUDPAddr("udp4", address)
	if err != nil {
		return nil, err
	}

	tmp, err := listenPacket("udp4", "224.0.0.0:"+strconv.FormatInt(int64(addr.Port), 10))
	if err != nil {
		return nil, err
	}
	conn := tmp.(*net.UDPConn)

	connIP := ipv4.NewPacketConn(conn)

	err = connIP.JoinGroup(intf, &net.UDPAddr{IP: addr.IP})
	if err != nil {
		conn.Close() //nolint:errcheck
		return nil, err
	}

	err = connIP.SetMulticastInterface(intf)
	if err != nil {
		conn.Close() //nolint:errcheck
		return nil, err
	}

	err = connIP.SetMulticastTTL(multicastTTL)
	if err != nil {
		conn.Close() //nolint:errcheck
		return nil, err
	}

	return &singleConn{
		addr:   addr,
		conn:   conn,
		connIP: connIP,
	}, nil
}

// Close implements Conn.
func (c *singleConn) Close() error {
	return c.conn.Close()
}

// SetReadBuffer implements Conn.
func (c *singleConn) SetReadBuffer(bytes int) error {
	return c.conn.SetReadBuffer(bytes)
}

// SyscallConn implements Conn.
func (c *singleConn) SyscallConn() (syscall.RawConn, error) {
	return c.conn.SyscallConn()
}

// LocalAddr implements Conn.
func (c *singleConn) LocalAddr() net.Addr {
	return c.conn.LocalAddr()
}

// SetDeadline implements Conn.
func (c *singleConn) SetDeadline(_ time.Time) error {
	panic("unimplemented")
}

// SetReadDeadline implements Conn.
func (c *singleConn) SetReadDeadline(t time.Time) error {
	return c.conn.SetReadDeadline(t)
}

// SetWriteDeadline implements Conn.
func (c *singleConn) SetWriteDeadline(t time.Time) error {
	return c.conn.SetWriteDeadline(t)
}

// WriteTo implements Conn.
func (c *singleConn) WriteTo(b []byte, addr net.Addr) (int, error) {
	return c.conn.WriteTo(b, addr)
}

// ReadFrom implements Conn.
func (c *singleConn) ReadFrom(b []byte) (int, net.Addr, error) {
	return c.conn.ReadFrom(b)
}
