// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin freebsd netbsd openbsd

package ipv4

import (
	"net"
	"os"
	"syscall"
	"unsafe"
)

func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
	opt.Lock()
	defer opt.Unlock()
	if cf&FlagTTL != 0 {
		if err := setIPv4ReceiveTTL(fd, on); err != nil {
			return err
		}
		if on {
			opt.set(FlagTTL)
		} else {
			opt.clear(FlagTTL)
		}
	}
	if cf&FlagDst != 0 {
		if err := setIPv4ReceiveDestinationAddress(fd, on); err != nil {
			return err
		}
		if on {
			opt.set(FlagDst)
		} else {
			opt.clear(FlagDst)
		}
	}
	if cf&FlagInterface != 0 {
		if err := setIPv4ReceiveInterface(fd, on); err != nil {
			return err
		}
		if on {
			opt.set(FlagInterface)
		} else {
			opt.clear(FlagInterface)
		}
	}
	return nil
}

func newControlMessage(opt *rawOpt) (oob []byte) {
	opt.Lock()
	defer opt.Unlock()
	l, off := 0, 0
	if opt.isset(FlagTTL) {
		l += syscall.CmsgSpace(1)
	}
	if opt.isset(FlagDst) {
		l += syscall.CmsgSpace(net.IPv4len)
	}
	if opt.isset(FlagInterface) {
		l += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink)
	}
	if l > 0 {
		oob = make([]byte, l)
		if opt.isset(FlagTTL) {
			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
			m.Level = ianaProtocolIP
			m.Type = syscall.IP_RECVTTL
			m.SetLen(syscall.CmsgLen(1))
			off += syscall.CmsgSpace(1)
		}
		if opt.isset(FlagDst) {
			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
			m.Level = ianaProtocolIP
			m.Type = syscall.IP_RECVDSTADDR
			m.SetLen(syscall.CmsgLen(net.IPv4len))
			off += syscall.CmsgSpace(net.IPv4len)
		}
		if opt.isset(FlagInterface) {
			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
			m.Level = ianaProtocolIP
			m.Type = syscall.IP_RECVIF
			m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrDatalink))
			off += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink)
		}
	}
	return
}

func parseControlMessage(b []byte) (*ControlMessage, error) {
	if len(b) == 0 {
		return nil, nil
	}
	cmsgs, err := syscall.ParseSocketControlMessage(b)
	if err != nil {
		return nil, os.NewSyscallError("parse socket control message", err)
	}
	cm := &ControlMessage{}
	for _, m := range cmsgs {
		if m.Header.Level != ianaProtocolIP {
			continue
		}
		switch m.Header.Type {
		case syscall.IP_RECVTTL:
			cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
		case syscall.IP_RECVDSTADDR:
			cm.Dst = m.Data[:net.IPv4len]
		case syscall.IP_RECVIF:
			sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&m.Data[0]))
			cm.IfIndex = int(sadl.Index)
		}
	}
	return cm, nil
}

func marshalControlMessage(cm *ControlMessage) []byte {
	// TODO(mikio): Implement IP_PKTINFO stuff when OS X 10.8 comes
	return nil
}