// Copyright 2015 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.

package icmp

import "encoding/binary"

// MPLSLabel represents an MPLS label stack entry.
type MPLSLabel struct {
	Label int  // label value
	TC    int  // traffic class; formerly experimental use
	S     bool // bottom of stack
	TTL   int  // time to live
}

const (
	classMPLSLabelStack        = 1
	typeIncomingMPLSLabelStack = 1
)

// MPLSLabelStack represents an MPLS label stack.
type MPLSLabelStack struct {
	Class  int // extension object class number
	Type   int // extension object sub-type
	Labels []MPLSLabel
}

// Len implements the Len method of Extension interface.
func (ls *MPLSLabelStack) Len(proto int) int {
	return 4 + (4 * len(ls.Labels))
}

// Marshal implements the Marshal method of Extension interface.
func (ls *MPLSLabelStack) Marshal(proto int) ([]byte, error) {
	b := make([]byte, ls.Len(proto))
	if err := ls.marshal(proto, b); err != nil {
		return nil, err
	}
	return b, nil
}

func (ls *MPLSLabelStack) marshal(proto int, b []byte) error {
	l := ls.Len(proto)
	binary.BigEndian.PutUint16(b[:2], uint16(l))
	b[2], b[3] = classMPLSLabelStack, typeIncomingMPLSLabelStack
	off := 4
	for _, ll := range ls.Labels {
		b[off], b[off+1], b[off+2] = byte(ll.Label>>12), byte(ll.Label>>4&0xff), byte(ll.Label<<4&0xf0)
		b[off+2] |= byte(ll.TC << 1 & 0x0e)
		if ll.S {
			b[off+2] |= 0x1
		}
		b[off+3] = byte(ll.TTL)
		off += 4
	}
	return nil
}

func parseMPLSLabelStack(b []byte) (Extension, error) {
	ls := &MPLSLabelStack{
		Class: int(b[2]),
		Type:  int(b[3]),
	}
	for b = b[4:]; len(b) >= 4; b = b[4:] {
		ll := MPLSLabel{
			Label: int(b[0])<<12 | int(b[1])<<4 | int(b[2])>>4,
			TC:    int(b[2]&0x0e) >> 1,
			TTL:   int(b[3]),
		}
		if b[2]&0x1 != 0 {
			ll.S = true
		}
		ls.Labels = append(ls.Labels, ll)
	}
	return ls, nil
}
