// Copyright 2012 Google, Inc. 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"
	"fmt"
	"github.com/metacubex/gopacket"
)

type RUDP struct {
	BaseLayer
	SYN, ACK, EACK, RST, NUL bool
	Version                  uint8
	HeaderLength             uint8
	SrcPort, DstPort         RUDPPort
	DataLength               uint16
	Seq, Ack, Checksum       uint32
	VariableHeaderArea       []byte
	// RUDPHeaderSyn contains SYN information for the RUDP packet,
	// if the SYN flag is set
	*RUDPHeaderSYN
	// RUDPHeaderEack contains EACK information for the RUDP packet,
	// if the EACK flag is set.
	*RUDPHeaderEACK
}

type RUDPHeaderSYN struct {
	MaxOutstandingSegments, MaxSegmentSize, OptionFlags uint16
}

type RUDPHeaderEACK struct {
	SeqsReceivedOK []uint32
}

// LayerType returns gopacket.LayerTypeRUDP.
func (r *RUDP) LayerType() gopacket.LayerType { return LayerTypeRUDP }

func decodeRUDP(data []byte, p gopacket.PacketBuilder) error {
	r := &RUDP{
		SYN:          data[0]&0x80 != 0,
		ACK:          data[0]&0x40 != 0,
		EACK:         data[0]&0x20 != 0,
		RST:          data[0]&0x10 != 0,
		NUL:          data[0]&0x08 != 0,
		Version:      data[0] & 0x3,
		HeaderLength: data[1],
		SrcPort:      RUDPPort(data[2]),
		DstPort:      RUDPPort(data[3]),
		DataLength:   binary.BigEndian.Uint16(data[4:6]),
		Seq:          binary.BigEndian.Uint32(data[6:10]),
		Ack:          binary.BigEndian.Uint32(data[10:14]),
		Checksum:     binary.BigEndian.Uint32(data[14:18]),
	}
	if r.HeaderLength < 9 {
		return fmt.Errorf("RUDP packet with too-short header length %d", r.HeaderLength)
	}
	hlen := int(r.HeaderLength) * 2
	r.Contents = data[:hlen]
	r.Payload = data[hlen : hlen+int(r.DataLength)]
	r.VariableHeaderArea = data[18:hlen]
	headerData := r.VariableHeaderArea
	switch {
	case r.SYN:
		if len(headerData) != 6 {
			return fmt.Errorf("RUDP packet invalid SYN header length: %d", len(headerData))
		}
		r.RUDPHeaderSYN = &RUDPHeaderSYN{
			MaxOutstandingSegments: binary.BigEndian.Uint16(headerData[:2]),
			MaxSegmentSize:         binary.BigEndian.Uint16(headerData[2:4]),
			OptionFlags:            binary.BigEndian.Uint16(headerData[4:6]),
		}
	case r.EACK:
		if len(headerData)%4 != 0 {
			return fmt.Errorf("RUDP packet invalid EACK header length: %d", len(headerData))
		}
		r.RUDPHeaderEACK = &RUDPHeaderEACK{make([]uint32, len(headerData)/4)}
		for i := 0; i < len(headerData); i += 4 {
			r.SeqsReceivedOK[i/4] = binary.BigEndian.Uint32(headerData[i : i+4])
		}
	}
	p.AddLayer(r)
	p.SetTransportLayer(r)
	return p.NextDecoder(gopacket.LayerTypePayload)
}

func (r *RUDP) TransportFlow() gopacket.Flow {
	return gopacket.NewFlow(EndpointRUDPPort, []byte{byte(r.SrcPort)}, []byte{byte(r.DstPort)})
}
