// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package layers

import (
	"encoding/binary"
	"errors"
	"fmt"

	"github.com/gopacket/gopacket"
)

// Potential values for ARP.Operation.
const (
	ARPRequest = 1
	ARPReply   = 2
)

// ARP is a ARP packet header.
type ARP struct {
	BaseLayer
	AddrType          LinkType
	Protocol          EthernetType
	HwAddressSize     uint8
	ProtAddressSize   uint8
	Operation         uint16
	SourceHwAddress   []byte
	SourceProtAddress []byte
	DstHwAddress      []byte
	DstProtAddress    []byte
}

// LayerType returns LayerTypeARP
func (arp *ARP) LayerType() gopacket.LayerType { return LayerTypeARP }

// DecodeFromBytes decodes the given bytes into this layer.
func (arp *ARP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
	if len(data) < 8 {
		df.SetTruncated()
		return fmt.Errorf("ARP length %d too short", len(data))
	}
	arp.AddrType = LinkType(binary.BigEndian.Uint16(data[0:2]))
	arp.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))
	arp.HwAddressSize = data[4]
	arp.ProtAddressSize = data[5]
	arp.Operation = binary.BigEndian.Uint16(data[6:8])
	arpLength := 8 + 2*arp.HwAddressSize + 2*arp.ProtAddressSize
	if len(data) < int(arpLength) {
		df.SetTruncated()
		return fmt.Errorf("ARP length %d too short, %d expected", len(data), arpLength)
	}
	arp.SourceHwAddress = data[8 : 8+arp.HwAddressSize]
	arp.SourceProtAddress = data[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize]
	arp.DstHwAddress = data[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize]
	arp.DstProtAddress = data[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize]

	arp.Contents = data[:arpLength]
	arp.Payload = data[arpLength:]
	return nil
}

// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (arp *ARP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
	size := 8 + len(arp.SourceHwAddress) + len(arp.SourceProtAddress) + len(arp.DstHwAddress) + len(arp.DstProtAddress)
	bytes, err := b.PrependBytes(size)
	if err != nil {
		return err
	}
	if opts.FixLengths {
		if len(arp.SourceHwAddress) != len(arp.DstHwAddress) {
			return errors.New("mismatched hardware address sizes")
		}
		arp.HwAddressSize = uint8(len(arp.SourceHwAddress))
		if len(arp.SourceProtAddress) != len(arp.DstProtAddress) {
			return errors.New("mismatched prot address sizes")
		}
		arp.ProtAddressSize = uint8(len(arp.SourceProtAddress))
	}
	binary.BigEndian.PutUint16(bytes, uint16(arp.AddrType))
	binary.BigEndian.PutUint16(bytes[2:], uint16(arp.Protocol))
	bytes[4] = arp.HwAddressSize
	bytes[5] = arp.ProtAddressSize
	binary.BigEndian.PutUint16(bytes[6:], arp.Operation)
	start := 8
	for _, addr := range [][]byte{
		arp.SourceHwAddress,
		arp.SourceProtAddress,
		arp.DstHwAddress,
		arp.DstProtAddress,
	} {
		copy(bytes[start:], addr)
		start += len(addr)
	}
	return nil
}

// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (arp *ARP) CanDecode() gopacket.LayerClass {
	return LayerTypeARP
}

// NextLayerType returns the layer type contained by this DecodingLayer.
func (arp *ARP) NextLayerType() gopacket.LayerType {
	return gopacket.LayerTypePayload
}

func decodeARP(data []byte, p gopacket.PacketBuilder) error {

	arp := &ARP{}
	return decodingLayerDecoder(arp, data, p)
}
