// 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"
	"github.com/tsg/gopacket"
	"net"
	"time"
)

type IGMPType uint8

// IGMP is the packet structure for IGMP messages.
type IGMP struct {
	BaseLayer
	Type            IGMPType
	MaxResponseTime time.Duration
	Checksum        uint16
	GroupAddress    net.IP
	// The following are used only by IGMPv3
	SupressRouterProcessing bool
	RobustnessValue         uint8
	IntervalTime            time.Duration
	SourceAddresses         []net.IP
}

// LayerType returns LayerTypeIGMP
func (i *IGMP) LayerType() gopacket.LayerType { return LayerTypeIGMP }

// igmpTimeDecode decodes the duration created by the given byte, using the
// algorithm in http://www.rfc-base.org/txt/rfc-3376.txt section 4.1.1.
func igmpTimeDecode(t uint8) time.Duration {
	if t&0x80 == 0 {
		return time.Millisecond * 100 * time.Duration(t)
	}
	mant := (t & 0x70) >> 4
	exp := t & 0x0F
	return time.Millisecond * 100 * time.Duration((mant|0x10)<<(exp+3))
}

// DecodeFromBytes decodes the given bytes into this layer.
func (i *IGMP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
	i.Type = IGMPType(data[0])
	i.MaxResponseTime = igmpTimeDecode(data[1])
	i.Checksum = binary.BigEndian.Uint16(data[2:4])
	i.GroupAddress = net.IP(data[4:8])
	if i.Type == 0x11 && len(data) > 8 {
		i.SupressRouterProcessing = data[8]&0x8 != 0
		i.RobustnessValue = data[8] & 0x7
		i.IntervalTime = igmpTimeDecode(data[9])
		numSources := int(binary.BigEndian.Uint16(data[10:12]))
		for j := 0; j < numSources; j++ {
			i.SourceAddresses = append(i.SourceAddresses, net.IP(data[12+j*4:16+j*4]))
		}
	} else {
		i.SupressRouterProcessing = false
		i.RobustnessValue = 0
		i.IntervalTime = 0
		i.SourceAddresses = nil
	}
	return nil
}

// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (i *IGMP) CanDecode() gopacket.LayerClass {
	return LayerTypeIGMP
}

// NextLayerType returns the layer type contained by this DecodingLayer.
func (i *IGMP) NextLayerType() gopacket.LayerType {
	return gopacket.LayerTypeZero
}

func decodeIGMP(data []byte, p gopacket.PacketBuilder) error {
	i := &IGMP{}
	return decodingLayerDecoder(i, data, p)
}
