/*
 * Copyright (c) 2018 - present.  Boling Consulting Solutions (bcsw.net)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * NOTE: This file was generated, manual edits will be overwritten!
 *
 * Generated by 'goCodeGenerator.py':
 *              https://github.com/opencord/omci-lib-go/OMCI-parser/README.md
 */

package omci

import (
	"errors"
	"fmt"
	"github.com/deckarep/golang-set"
	"github.com/google/gopacket"
	me "github.com/opencord/omci-lib-go/generated"
	"time"
)

var encoderMap map[MessageType]func(*me.ManagedEntity, options) (gopacket.SerializableLayer, error)

func init() {
	encoderMap = make(map[MessageType]func(*me.ManagedEntity, options) (gopacket.SerializableLayer, error))

	encoderMap[CreateRequestType] = CreateRequestFrame
	encoderMap[DeleteRequestType] = DeleteRequestFrame
	encoderMap[SetRequestType] = SetRequestFrame
	encoderMap[GetRequestType] = GetRequestFrame
	encoderMap[GetAllAlarmsRequestType] = GetAllAlarmsRequestFrame
	encoderMap[GetAllAlarmsNextRequestType] = GetAllAlarmsNextRequestFrame
	encoderMap[MibUploadRequestType] = MibUploadRequestFrame
	encoderMap[MibUploadNextRequestType] = MibUploadNextRequestFrame
	encoderMap[MibResetRequestType] = MibResetRequestFrame
	//encoderMap[TestRequestType] = TestRequestFrame
	encoderMap[StartSoftwareDownloadRequestType] = StartSoftwareDownloadRequestFrame
	encoderMap[DownloadSectionRequestType] = DownloadSectionRequestFrame
	encoderMap[EndSoftwareDownloadRequestType] = EndSoftwareDownloadRequestFrame
	encoderMap[ActivateSoftwareRequestType] = ActivateSoftwareRequestFrame
	encoderMap[CommitSoftwareRequestType] = CommitSoftwareRequestFrame
	encoderMap[SynchronizeTimeRequestType] = SynchronizeTimeRequestFrame
	encoderMap[RebootRequestType] = RebootRequestFrame
	encoderMap[GetNextRequestType] = GetNextRequestFrame
	encoderMap[GetCurrentDataRequestType] = GetCurrentDataRequestFrame
	encoderMap[SetTableRequestType] = SetTableRequestFrame
	encoderMap[CreateResponseType] = CreateResponseFrame
	encoderMap[DeleteResponseType] = DeleteResponseFrame
	encoderMap[SetResponseType] = SetResponseFrame
	encoderMap[GetResponseType] = GetResponseFrame
	encoderMap[GetAllAlarmsResponseType] = GetAllAlarmsResponseFrame
	encoderMap[GetAllAlarmsNextResponseType] = GetAllAlarmsNextResponseFrame
	encoderMap[MibUploadResponseType] = MibUploadResponseFrame
	encoderMap[MibUploadNextResponseType] = MibUploadNextResponseFrame
	encoderMap[MibResetResponseType] = MibResetResponseFrame
	//encoderMap[TestResponseType] = TestResponseFrame
	encoderMap[StartSoftwareDownloadResponseType] = StartSoftwareDownloadResponseFrame
	encoderMap[DownloadSectionResponseType] = DownloadSectionResponseFrame
	encoderMap[EndSoftwareDownloadResponseType] = EndSoftwareDownloadResponseFrame
	encoderMap[ActivateSoftwareResponseType] = ActivateSoftwareResponseFrame
	encoderMap[CommitSoftwareResponseType] = CommitSoftwareResponseFrame
	encoderMap[SynchronizeTimeResponseType] = SynchronizeTimeResponseFrame
	encoderMap[RebootResponseType] = RebootResponseFrame
	encoderMap[GetNextResponseType] = GetNextResponseFrame
	encoderMap[GetCurrentDataResponseType] = GetCurrentDataResponseFrame
	encoderMap[SetTableResponseType] = SetTableResponseFrame
	encoderMap[AlarmNotificationType] = AlarmNotificationFrame
	encoderMap[AttributeValueChangeType] = AttributeValueChangeFrame
	//encoderMap[TestResultType] = TestResultFrame
}

type options struct {
	frameFormat               DeviceIdent
	failIfTruncated           bool
	attributeMask             uint16
	result                    me.Results      // Common for many responses
	attrExecutionMask         uint16          // Create Response Only if results == 3 or Set Response only if results == 0
	unsupportedMask           uint16          // Set Response only if results == 9
	sequenceNumberCountOrSize uint16          // For get-next request frames and for frames that return number of commands or length
	transactionID             uint16          // OMCI TID
	mode                      uint8           // Get All Alarms retrieval mode
	alarm                     AlarmOptions    // Alarm related frames
	software                  SoftwareOptions // Software image related frames
	payload                   interface{}     // ME or list of MEs, alarm bitmap, timestamp, ...
	addDefaults               bool            // Add missing SetByCreate attributes for Create Requests
}

var defaultFrameOptions = options{
	frameFormat:               BaselineIdent,
	failIfTruncated:           false,
	attributeMask:             0xFFFF,
	result:                    me.Success,
	attrExecutionMask:         0,
	unsupportedMask:           0,
	sequenceNumberCountOrSize: 0,
	transactionID:             0,
	mode:                      0,
	software:                  defaultSoftwareOptions,
	alarm:                     defaultAlarmOptions,
	payload:                   nil,
	addDefaults:               false,
}

// FrameOption sets options such as frame format, etc.
type FrameOption func(*options)

// FrameFormat determines determines the OMCI message format used on the fiber.
// The default value is BaselineIdent
func FrameFormat(ff DeviceIdent) FrameOption {
	return func(o *options) {
		o.frameFormat = ff
	}
}

// FailIfTruncated determines whether a request to encode a frame that does
// not have enough room for all requested options should fail and return an
// error.
//
// If set to 'false', the behaviour depends on the message type/operation
// requested. The table below provides more information:
//
//   Request Type	Behavour
//	 ------------------------------------------------------------------------
//	 CreateRequest  A single CreateRequest struct is always returned as the
//                  CreateRequest message does not have an attributes Mask
//                  field and a Baseline OMCI message is large enough to
//                  support all Set-By-Create attributes.
//
//   GetResponse	If multiple OMCI response frames are needed to return
//					all requested attributes, only the attributes that can
//					fit will be returned and the FailedAttributeMask field
//					set to the attributes that could not be returned
//
//					If this is an ME with an attribute that is a table, the
//					first GetResponse struct will return the size of the
//					attribute and the following GetNextResponse structs will
//					contain the attribute data. The ONU application is
//					responsible for stashing these extra struct(s) away in
//					anticipation of possible GetNext Requests occurring for
//					the attribute.  See the discussion on Table attributes
//					in the GetResponse section of ITU G.988 for more
//					information.
//
// If set to 'true', no struct(s) are returned and an error is provided.
//
// The default value is 'false'
func FailIfTruncated(f bool) FrameOption {
	return func(o *options) {
		o.failIfTruncated = f
	}
}

// attributeMask determines the attributes to encode into the frame.
// The default value is 0xFFFF which specifies all available attributes
// in the frame
func AttributeMask(m uint16) FrameOption {
	return func(o *options) {
		o.attributeMask = m
	}
}

// AttributeExecutionMask is used by the Create and Set Response frames to indicate
// attributes that failed to be created/set.
func AttributeExecutionMask(m uint16) FrameOption {
	return func(o *options) {
		o.attrExecutionMask = m
	}
}

// UnsupportedAttributeMask is used by the Set Response frames to indicate
// attributes are not supported on this ONU
func UnsupportedAttributeMask(m uint16) FrameOption {
	return func(o *options) {
		o.unsupportedMask = m
	}
}

// Result is used to set returned results in responses
// that have that field
func Result(r me.Results) FrameOption {
	return func(o *options) {
		o.result = r
	}
}

// SequenceNumberCountOrSize is used by the GetNext and MibUploadGetNext request frames and for
// frames that return number of commands or length such as Get (table attribute) or
// MibUpload/GetAllAlarms/...
func SequenceNumberCountOrSize(m uint16) FrameOption {
	return func(o *options) {
		o.sequenceNumberCountOrSize = m
	}
}

// TransactionID is to specify the TID in the OMCI header. The default is
// zero which requires the caller to set it to the appropriate value if this
// is not an autonomous ONU notification frame
func TransactionID(tid uint16) FrameOption {
	return func(o *options) {
		o.transactionID = tid
	}
}

// RetrievalMode is to specify the the Alarm Retrieval Mode in a GetAllAlarms Request
func RetrievalMode(m uint8) FrameOption {
	return func(o *options) {
		o.mode = m
	}
}

// SuccessResult is to specify the the SuccessResult for a SynchronizeTime Response
func SuccessResult(m uint8) FrameOption {
	return func(o *options) {
		o.mode = m
	}
}

// RebootCondition is to specify the the Reboot Condition for a ONU Reboot request
func RebootCondition(m uint8) FrameOption {
	return func(o *options) {
		o.mode = m
	}
}

// Alarm is used to specify a collection of options related to Alarm notifications
func Alarm(ao AlarmOptions) FrameOption {
	return func(o *options) {
		o.alarm = ao
	}
}

// Software is used to specify a collection of options related to Software image
// manipulation
func Software(so SoftwareOptions) FrameOption {
	return func(o *options) {
		o.software = so
	}
}

// Payload is used to specify ME payload options that are not simple types. This
// include the ME (list of MEs) to encode into a GetNextMibUpload response, the
// alarm bitmap for alarm relates responses/notifications, alarm bitmaps, and
// for specifying the download section data when performing Software Download.
func Payload(p interface{}) FrameOption {
	return func(o *options) {
		o.payload = p
	}
}

// AddDefaults is used to specify that if a SetByCreate attribute is not
// specified in the list of attributes for a Create Request, use the attribute
// defined default
func AddDefaults(add bool) FrameOption {
	return func(o *options) {
		o.addDefaults = add
	}
}

// Alarm related frames have a wide variety of settable values. Placing them
// in a separate struct is mainly to keep the base options simple
type AlarmOptions struct {
	AlarmClassID  me.ClassID
	AlarmInstance uint16
	AlarmBitmap   []byte // Should be up to 58 octets
}

var defaultAlarmOptions = AlarmOptions{
	AlarmClassID:  0,
	AlarmInstance: 0,
	AlarmBitmap:   nil,
}

// Software related frames have a wide variety of settable values. Placing them
// in a separate struct is mainly to keep the base options simple
type SoftwareOptions struct {
	WindowSize   uint8 // Window size - 1
	ImageSize    uint32
	CircuitPacks []uint16 // slot (upper 8 bits) and instance (lower 8 bits)
	Results      []DownloadResults
}

var defaultSoftwareOptions = SoftwareOptions{
	WindowSize:   0,
	ImageSize:    0,
	CircuitPacks: nil,
	Results:      nil,
}

// EncodeFrame will encode the Managed Entity specific protocol struct and an
// OMCILayer struct. This struct can be provided to the gopacket.SerializeLayers()
// function to be serialized into a buffer for transmission.
func EncodeFrame(m *me.ManagedEntity, messageType MessageType, opt ...FrameOption) (*OMCI, gopacket.SerializableLayer, error) {
	// Check for message type support
	msgType := me.MsgType(messageType & me.MsgTypeMask)
	meDefinition := m.GetManagedEntityDefinition()

	if !me.SupportsMsgType(meDefinition, msgType) {
		msg := fmt.Sprintf("managed entity %v does not support %v Message-Type",
			meDefinition.GetName(), msgType)
		return nil, nil, errors.New(msg)
	}
	// Decode options
	opts := defaultFrameOptions
	for _, o := range opt {
		o(&opts)
	}
	// TODO: If AttributesMask option passed in, check for deprecated options. Allow encoding option
	//       that will ignore deprecated option.   Add additional in the get and set meframe_test,go
	//       test functions to test this. Also have it test attribute name(s) to see if the attribute
	//       is deprecated.  The OMCI-Parser now supports detection of deprecated attributes and
	//       provides that to the code-generator (and currently available in generated golang code).
	// Note: Transaction ID should be set before frame serialization
	omci := &OMCI{
		TransactionID:    opts.transactionID,
		MessageType:      messageType,
		DeviceIdentifier: opts.frameFormat,
	}
	var meInfo gopacket.SerializableLayer
	var err error

	if encoder, ok := encoderMap[messageType]; ok {
		meInfo, err = encoder(m, opts)
	} else {
		err = fmt.Errorf("message-type: %v/%#x is not supported", messageType, messageType)
	}
	if err != nil {
		return nil, nil, err
	}
	return omci, meInfo, err
}

// For most all create methods below, error checking for valid masks, attribute
// values, and other fields is left to when the frame is actually serialized.

func checkAttributeMask(m *me.ManagedEntity, mask uint16) (uint16, error) {
	if mask == defaultFrameOptions.attributeMask {
		// Scale back to just what is allowed
		return m.GetAllowedAttributeMask(), nil
	}
	if mask&m.GetManagedEntityDefinition().GetAllowedAttributeMask() != mask {
		return 0, errors.New("invalid attribute mask")
	}
	return mask & m.GetManagedEntityDefinition().GetAllowedAttributeMask(), nil
}

// return the maximum space that can be used by attributes
func maxPacketAvailable(m *me.ManagedEntity, opt options) uint {
	if opt.frameFormat == BaselineIdent {
		// OMCI Header          - 4 octets
		// Class ID/Instance ID - 4 octets
		// Length field			- 4 octets
		// MIC                  - 4 octets
		return MaxBaselineLength - 16
	}
	// OMCI Header          - 4 octets
	// Class ID/Instance ID - 4 octets
	// Length field			- 4 octets
	// MIC                  - 4 octets
	return MaxExtendedLength - 16
}

func calculateAttributeMask(m *me.ManagedEntity, requestedMask uint16) (uint16, error) {
	attrDefs := m.GetAttributeDefinitions()
	var entityIDName string
	if entry, ok := attrDefs[0]; ok {
		entityIDName = entry.GetName()
	} else {
		panic("unexpected error") // All attribute definition maps have an entity ID
	}
	attributeNames := make([]interface{}, 0)
	for attrName := range m.GetAttributeValueMap() {
		if attrName == entityIDName {
			continue // No mask for EntityID
		}
		attributeNames = append(attributeNames, attrName)
	}
	calculatedMask, err := me.GetAttributesBitmap(attrDefs, mapset.NewSetWith(attributeNames...))

	if err != nil {
		return 0, err
	}
	return calculatedMask & requestedMask, nil
}

// GenFrame is a helper function to make tests a little easier to read.
// For a real application, use the .../omci/generated/class.go 'New'
// functions to create your Managed Entity and then use it to call the
// EncodeFrame method.
func GenFrame(meInstance *me.ManagedEntity, messageType MessageType, options ...FrameOption) ([]byte, error) {
	omciLayer, msgLayer, err := EncodeFrame(meInstance, messageType, options...)
	if err != nil {
		return nil, err
	}
	// Serialize the frame and send it
	var serializeOptions gopacket.SerializeOptions
	serializeOptions.FixLengths = true

	buffer := gopacket.NewSerializeBuffer()
	err = gopacket.SerializeLayers(buffer, serializeOptions, omciLayer, msgLayer)
	if err != nil {
		return nil, err
	}
	return buffer.Bytes(), nil
}

func CreateRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// NOTE: The OMCI parser does not extract the default values of set-by-create attributes
	//       and are the zero 'default' (or nil) at this time.  For this reason, make sure
	//       you specify all non-zero default values and pass them in appropriate
	meLayer := &CreateRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		Attributes: m.GetAttributeValueMap(),
	}
	// Add any missing SetByCreate options if requested
	if opt.addDefaults {
		if attrDefs, err := me.GetAttributesDefinitions(m.GetClassID()); err.StatusCode() == me.Success {
			for index, attr := range attrDefs {
				if me.SupportsAttributeAccess(attr, me.SetByCreate) {
					if index == 0 {
						continue // Skip Entity ID, if it is SetByCreate, they should always specify it
					}
					if _, found := meLayer.Attributes[attr.GetName()]; !found {
						meLayer.Attributes[attr.GetName()] = attr.DefValue
					}
				}
			}
		}
	}
	return meLayer, nil
}

func CreateResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	meLayer := &CreateResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		Result: opt.result,
	}
	if meLayer.Result == me.ParameterError {
		meLayer.AttributeExecutionMask = opt.attrExecutionMask
	}
	return meLayer, nil
}

func DeleteRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	meLayer := &DeleteRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	return meLayer, nil
}

func DeleteResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	meLayer := &DeleteResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		Result: opt.result,
	}
	return meLayer, nil
}

func SetRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	mask, err = calculateAttributeMask(m, mask)
	if err != nil {
		return nil, err
	}
	meDefinition := m.GetManagedEntityDefinition()
	attrDefs := meDefinition.GetAttributeDefinitions()
	attrMap := m.GetAttributeValueMap()

	// Get payload space available
	maxPayload := maxPacketAvailable(m, opt)
	payloadAvailable := int(maxPayload) - 2 // Less attribute mask
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
		// payloadAvailable -= 2				// Less length
	}
	meLayer := &SetRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		AttributeMask: 0,
		Attributes:    make(me.AttributeValueMap),
	}
	for mask != 0 {
		// Iterate down the attributes (Attribute 0 is the ManagedEntity ID)
		var attrIndex uint
		for attrIndex = 1; attrIndex <= 16; attrIndex++ {
			// Is this attribute requested
			if mask&(1<<(16-attrIndex)) != 0 {
				// Get definitions since we need the name
				attrDef, ok := attrDefs[attrIndex]
				if !ok {
					msg := fmt.Sprintf("Unexpected error, index %v not valued for ME %v",
						attrIndex, meDefinition.GetName())
					return nil, errors.New(msg)
				}
				var attrValue interface{}
				attrValue, ok = attrMap[attrDef.Name]
				if !ok {
					msg := fmt.Sprintf("Unexpected error, attribute %v not provided in ME %v: %v",
						attrDef.GetName(), meDefinition.GetName(), m)
					return nil, errors.New(msg)
				}
				// Is space available?
				if attrDef.Size <= payloadAvailable {
					// Mark bit handled
					mask &= ^attrDef.Mask
					meLayer.AttributeMask |= attrDef.Mask
					meLayer.Attributes[attrDef.Name] = attrValue
					payloadAvailable -= attrDef.Size
				} else {
					// TODO: Should we set truncate?
					msg := fmt.Sprintf("out-of-space. Cannot fit attribute %v into SetRequest message",
						attrDef.GetName())
					return nil, me.NewMessageTruncatedError(msg)
				}
			}
		}
	}
	if err == nil && meLayer.AttributeMask == 0 {
		// TODO: Is a set request with no attributes valid?
		return nil, errors.New("no attributes encoded for SetRequest")
	}
	return meLayer, nil
}

func SetResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	meLayer := &SetResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		Result: opt.result,
	}
	if meLayer.Result == me.AttributeFailure {
		meLayer.UnsupportedAttributeMask = opt.unsupportedMask
		meLayer.FailedAttributeMask = opt.attrExecutionMask
	}
	return meLayer, nil
}

func GetRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	// Given mask sent in (could be default of 0xFFFF) get what is allowable.
	// This will be all allowed if 0xFFFF is passed in, or a subset if a fixed
	// number of items.
	maxMask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Now scan attributes and reduce mask to only those requested
	var mask uint16
	mask, err = calculateAttributeMask(m, maxMask)
	if err != nil {
		return nil, err
	}
	if mask == 0 {
		// TODO: Is a Get request with no attributes valid?
		return nil, errors.New("no attributes requested for GetRequest")
	}
	meLayer := &GetRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		AttributeMask: mask,
	}
	return meLayer, nil
}

func GetResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	mask, err = calculateAttributeMask(m, mask)
	if err != nil {
		return nil, err
	}
	meLayer := &GetResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		Result:        opt.result,
		AttributeMask: 0,
		Attributes:    make(me.AttributeValueMap),
	}
	if meLayer.Result == me.AttributeFailure {
		meLayer.UnsupportedAttributeMask = opt.unsupportedMask
		meLayer.FailedAttributeMask = opt.attrExecutionMask
	}
	// Encode whatever we can
	if meLayer.Result == me.Success || meLayer.Result == me.AttributeFailure {
		// Encode results
		// Get payload space available
		maxPayload := maxPacketAvailable(m, opt)
		payloadAvailable := int(maxPayload) - 2 - 4 // Less attribute mask and attribute error encoding
		meDefinition := m.GetManagedEntityDefinition()
		attrDefs := meDefinition.GetAttributeDefinitions()
		attrMap := m.GetAttributeValueMap()

		if mask != 0 {
			// Iterate down the attributes (Attribute 0 is the ManagedEntity ID)
			var attrIndex uint
			for attrIndex = 1; attrIndex <= 16; attrIndex++ {
				// Is this attribute requested
				if mask&(1<<(16-attrIndex)) != 0 {
					// Get definitions since we need the name
					attrDef, ok := attrDefs[attrIndex]
					if !ok {
						msg := fmt.Sprintf("Unexpected error, index %v not valued for ME %v",
							attrIndex, meDefinition.GetName())
						return nil, errors.New(msg)
					}
					var attrValue interface{}
					attrValue, ok = attrMap[attrDef.Name]
					if !ok {
						msg := fmt.Sprintf("Unexpected error, attribute %v not provided in ME %v: %v",
							attrDef.GetName(), meDefinition.GetName(), m)
						return nil, errors.New(msg)
					}
					// Is space available?
					if attrDef.Size <= payloadAvailable {
						// Mark bit handled
						mask &= ^attrDef.Mask
						meLayer.AttributeMask |= attrDef.Mask
						meLayer.Attributes[attrDef.Name] = attrValue
						payloadAvailable -= attrDef.Size

					} else if opt.failIfTruncated {
						// TODO: Should we set truncate?
						msg := fmt.Sprintf("out-of-space. Cannot fit attribute %v into GetResponse message",
							attrDef.GetName())
						return nil, me.NewMessageTruncatedError(msg)
					} else {
						// Add to existing 'failed' mask and update result
						meLayer.FailedAttributeMask |= attrDef.Mask
						meLayer.Result = me.AttributeFailure
					}
				}
			}
		}
	}
	return meLayer, nil
}

func GetAllAlarmsRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &GetAllAlarmsRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		AlarmRetrievalMode: opt.mode,
	}
	return meLayer, nil
}

func GetAllAlarmsResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &GetAllAlarmsResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		NumberOfCommands: opt.sequenceNumberCountOrSize,
	}
	return meLayer, nil
}

func GetAllAlarmsNextRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &GetAllAlarmsNextRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		CommandSequenceNumber: opt.sequenceNumberCountOrSize,
	}
	return meLayer, nil
}

func GetAllAlarmsNextResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &GetAllAlarmsNextResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		AlarmEntityClass:    opt.alarm.AlarmClassID,
		AlarmEntityInstance: opt.alarm.AlarmInstance,
	}
	if len(opt.alarm.AlarmBitmap) > 28 {
		return nil, errors.New("invalid Alarm Bitmap Size. Must be [0..27]")
	}
	for octet := 0; octet < len(opt.alarm.AlarmBitmap); octet++ {
		meLayer.AlarmBitMap[octet] = opt.alarm.AlarmBitmap[octet]
	}
	for octet := len(opt.alarm.AlarmBitmap); octet < 28; octet++ {
		meLayer.AlarmBitMap[octet] = 0
	}
	return meLayer, nil
}

func MibUploadRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &MibUploadRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: 0,
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	return meLayer, nil
}

func MibUploadResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &MibUploadResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: 0,
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		NumberOfCommands: opt.sequenceNumberCountOrSize,
	}
	return meLayer, nil
}

func MibUploadNextRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &MibUploadNextRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: 0,
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		CommandSequenceNumber: opt.sequenceNumberCountOrSize,
	}
	return meLayer, nil
}

func MibUploadNextResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &MibUploadNextResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	if opt.payload == nil {
		// Shortcut used to specify the request sequence number is out of range, encode
		// a ME instance with class ID of zero to specify this per ITU G.988
		meDef := me.ManagedEntityDefinition{
			Name:                 "InvalidSequenceNumberManagedEntity",
			ClassID:              me.ClassID(0),
			MessageTypes:         nil,
			AttributeDefinitions: make(me.AttributeDefinitionMap),
		}
		opt.payload, _ = me.NewManagedEntity(meDef)
	}
	if _, ok := opt.payload.(*[]me.ManagedEntity); ok {
		if opt.frameFormat == BaselineIdent {
			return nil, errors.New("invalid payload for Baseline message")
		}
		// TODO: List of MEs. valid for extended messages only
	} else if managedEntity, ok := opt.payload.(*me.ManagedEntity); ok {
		// Single ME
		meLayer.ReportedME = *managedEntity
	} else {
		return nil, errors.New("invalid payload for MibUploadNextResponse frame")
	}
	return meLayer, nil
}

func MibResetRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &MibResetRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	return meLayer, nil
}

func MibResetResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &MibResetResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		Result: opt.result,
	}
	return meLayer, nil
}

func AlarmNotificationFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Common for all MEs
	meLayer := &AlarmNotificationMsg{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	// Get payload space available
	maxPayload := maxPacketAvailable(m, opt)
	payloadAvailable := int(maxPayload) - 1 // Less alarm sequence number

	// TODO: Lots of work to do
	fmt.Println(mask, maxPayload, payloadAvailable)

	return meLayer, errors.New("todo: Not implemented")
}

func AttributeValueChangeFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Common for all MEs
	meLayer := &AttributeValueChangeMsg{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		AttributeMask: 0,
		Attributes:    make(me.AttributeValueMap),
	}
	// Get payload space available
	maxPayload := maxPacketAvailable(m, opt)
	payloadAvailable := int(maxPayload) - 2 // Less attribute mask

	// TODO: Lots of work to do

	fmt.Println(mask, maxPayload, payloadAvailable)
	return meLayer, errors.New("todo: Not implemented")
}

//func TestRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
//	if opt.frameFormat == ExtendedIdent {
//		return nil, errors.New("Extended message set for this message type is not supported")
//	}
//	mask, err := checkAttributeMask(m, opt.attributeMask)
//	if err != nil {
//		return nil, err
//	}
//	// Common for all MEs
//	meLayer := &TestRequest{
//		MeBasePacket: MeBasePacket{
//			EntityClass:    m.GetClassID(),
//			EntityInstance: m.GetEntityID(),
//			Extended:       opt.frameFormat == ExtendedIdent,
//		},
//	}
//	// Get payload space available
//	maxPayload := maxPacketAvailable(m, opt)
//
//	// TODO: Lots of work to do
//
//	fmt.Println(mask, maxPayload)
//	return meLayer, errors.New("todo: Not implemented")
//}

//func TestResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
//	if opt.frameFormat == ExtendedIdent {
//		return nil, errors.New("Extended message set for this message type is not supported")
//	}
//	mask, err := checkAttributeMask(m, opt.attributeMask)
//	if err != nil {
//		return nil, err
//	}
//	// Common for all MEs
//	meLayer := &TestResponse{
//		MeBasePacket: MeBasePacket{
//			EntityClass:    m.GetClassID(),
//			EntityInstance: m.GetEntityID(),
//			Extended:       opt.frameFormat == ExtendedIdent,
//		},
//	}
//	// Get payload space available
//	maxPayload := maxPacketAvailable(m, opt)
//
//	// TODO: Lots of work to do
//
//	fmt.Println(mask, maxPayload)
//	return meLayer, errors.New("todo: Not implemented")
//}

func StartSoftwareDownloadRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &StartSoftwareDownloadRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		WindowSize:           opt.software.WindowSize,
		ImageSize:            opt.software.ImageSize,
		NumberOfCircuitPacks: byte(len(opt.software.CircuitPacks)),
		CircuitPacks:         opt.software.CircuitPacks,
	}
	// TODO: Add length check to insure we do not exceed maximum packet size
	// payloadAvailable := int(maxPacketAvailable(m, opt))
	payloadAvailable := 2
	sizeNeeded := 1
	if sizeNeeded > payloadAvailable {
		// TODO: Should we set truncate?
		msg := "out-of-space. Cannot fit Circuit Pack instances into Start Software Download Request message"
		return nil, me.NewMessageTruncatedError(msg)
	}
	return meLayer, nil
}

func StartSoftwareDownloadResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &StartSoftwareDownloadResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		WindowSize:        opt.software.WindowSize,
		NumberOfInstances: byte(len(opt.software.CircuitPacks)),
		MeResults:         opt.software.Results,
	}
	// TODO: Add length check to insure we do not exceed maximum packet size
	// payloadAvailable := int(maxPacketAvailable(m, opt))
	payloadAvailable := 2
	sizeNeeded := 1
	if sizeNeeded > payloadAvailable {
		// TODO: Should we set truncate?
		msg := "out-of-space. Cannot fit Results  into Start Software Download Response message"
		return nil, me.NewMessageTruncatedError(msg)
	}
	return meLayer, nil
}

func DownloadSectionRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Common for all MEs
	meLayer := &DownloadSectionRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	// Get payload space available
	maxPayload := maxPacketAvailable(m, opt)

	// TODO: Lots of work to do

	fmt.Println(mask, maxPayload)
	return meLayer, errors.New("todo: Not implemented")
}

func DownloadSectionResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Common for all MEs
	meLayer := &DownloadSectionResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	// Get payload space available
	maxPayload := maxPacketAvailable(m, opt)

	// TODO: Lots of work to do

	fmt.Println(mask, maxPayload)
	return meLayer, errors.New("todo: Not implemented")
}

func EndSoftwareDownloadRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Common for all MEs
	meLayer := &EndSoftwareDownloadRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	// Get payload space available
	maxPayload := maxPacketAvailable(m, opt)

	// TODO: Lots of work to do

	fmt.Println(mask, maxPayload)
	return meLayer, errors.New("todo: Not implemented")
}

func EndSoftwareDownloadResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Common for all MEs
	meLayer := &EndSoftwareDownloadResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	// Get payload space available
	maxPayload := maxPacketAvailable(m, opt)

	// TODO: Lots of work to do

	fmt.Println(mask, maxPayload)
	return meLayer, errors.New("todo: Not implemented")
}

func ActivateSoftwareRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Common for all MEs
	meLayer := &ActivateSoftwareRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	// Get payload space available
	maxPayload := maxPacketAvailable(m, opt)

	// TODO: Lots of work to do

	fmt.Println(mask, maxPayload)
	return meLayer, errors.New("todo: Not implemented")
}

func ActivateSoftwareResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Common for all MEs
	meLayer := &ActivateSoftwareResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	// Get payload space available
	maxPayload := maxPacketAvailable(m, opt)

	// TODO: Lots of work to do

	fmt.Println(mask, maxPayload)
	return meLayer, errors.New("todo: Not implemented")
}

func CommitSoftwareRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Common for all MEs
	meLayer := &CommitSoftwareRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	// Get payload space available
	maxPayload := maxPacketAvailable(m, opt)

	// TODO: Lots of work to do

	fmt.Println(mask, maxPayload)
	return meLayer, errors.New("todo: Not implemented")
}

func CommitSoftwareResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Common for all MEs
	meLayer := &CommitSoftwareResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	// Get payload space available
	maxPayload := maxPacketAvailable(m, opt)

	// TODO: Lots of work to do

	fmt.Println(mask, maxPayload)
	return meLayer, errors.New("todo: Not implemented")
}

func SynchronizeTimeRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &SynchronizeTimeRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	// Decode payload option. If nil, no timestamp provided
	if timestamp, ok := opt.payload.(int64); ok {
		tm := time.Unix(timestamp, 0)
		meLayer.Year = uint16(tm.UTC().Year())
		meLayer.Month = uint8(tm.UTC().Month())
		meLayer.Day = uint8(tm.UTC().Day())
		meLayer.Hour = uint8(tm.UTC().Hour())
		meLayer.Minute = uint8(tm.UTC().Minute())
		meLayer.Second = uint8(tm.UTC().Second())
	}
	return meLayer, nil
}

func SynchronizeTimeResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &SynchronizeTimeResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		Result:         opt.result,
		SuccessResults: opt.mode,
	}
	return meLayer, nil
}

func RebootRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &RebootRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		RebootCondition: opt.mode,
	}
	return meLayer, nil
}

func RebootResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Common for all MEs
	meLayer := &RebootResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		Result: opt.result,
	}
	return meLayer, nil
}

func GetNextRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Validate attribute mask
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Now scan attributes and reduce mask to only those requested
	mask, err = calculateAttributeMask(m, mask)
	if err != nil {
		return nil, err
	}
	if mask == 0 {
		return nil, errors.New("no attributes requested for GetNextRequest")
	}
	// TODO: If more than one attribute or the attribute requested is not a table attribute, return an error
	// Common for all MEs
	meLayer := &GetNextRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		AttributeMask:  mask,
		SequenceNumber: opt.sequenceNumberCountOrSize,
	}
	return meLayer, nil
}

func GetNextResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	// Validate attribute mask
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	mask, err = calculateAttributeMask(m, mask)
	if err != nil {
		return nil, err
	}
	//
	// Common for all MEs
	meLayer := &GetNextResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
		Result:        opt.result,
		AttributeMask: 0,
		Attributes:    make(me.AttributeValueMap),
	}
	if meLayer.Result == me.Success {
		// Get payload space available
		maxPayload := maxPacketAvailable(m, opt)
		payloadAvailable := int(maxPayload) - 3 // Less results and attribute mask
		meDefinition := m.GetManagedEntityDefinition()
		attrDefs := meDefinition.GetAttributeDefinitions()
		attrMap := m.GetAttributeValueMap()

		if mask == 0 {
			return nil, errors.New("no attributes requested for GetNextResponse")
		}
		// TODO: If more than one attribute or the attribute requested is not a table attribute, return an error
		// Iterate down the attributes (Attribute 0 is the ManagedEntity ID)
		var attrIndex uint
		for attrIndex = 1; attrIndex <= 16; attrIndex++ {
			// Is this attribute requested
			if mask&(1<<(16-attrIndex)) != 0 {
				// Get definitions since we need the name
				attrDef, ok := attrDefs[attrIndex]
				if !ok {
					msg := fmt.Sprintf("Unexpected error, index %v not valued for ME %v",
						attrIndex, meDefinition.GetName())
					return nil, errors.New(msg)
				}
				var attrValue interface{}
				attrValue, ok = attrMap[attrDef.Name]
				if !ok || attrValue == nil {
					msg := fmt.Sprintf("Unexpected error, attribute %v not provided in ME %v: %v",
						attrDef.GetName(), meDefinition.GetName(), m)
					return nil, errors.New(msg)
				}
				// Is space available?
				if attrDef.Size <= payloadAvailable {
					// Mark bit handled
					mask &= ^attrDef.Mask
					meLayer.AttributeMask |= attrDef.Mask
					meLayer.Attributes[attrDef.Name] = attrValue
					payloadAvailable -= attrDef.Size
				} else {
					// TODO: Should we set truncate?
					msg := fmt.Sprintf("out-of-space. Cannot fit attribute %v into GetNextResponse message",
						attrDef.GetName())
					return nil, me.NewMessageTruncatedError(msg)
				}
			}
		}
	}
	return meLayer, nil
}

//func TestResultFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
//	if opt.frameFormat == ExtendedIdent {
//		return nil, errors.New("Extended message set for this message type is not supported")
//	}
//	mask, err := checkAttributeMask(m, opt.attributeMask)
//	if err != nil {
//		return nil, err
//	}
//	// Common for all MEs
//	meLayer := &TestResultNotification{
//		MeBasePacket: MeBasePacket{
//			EntityClass:    m.GetClassID(),
//			EntityInstance: m.GetEntityID(),
//			Extended:       opt.frameFormat == ExtendedIdent,
//		},
//	}
//	// Get payload space available
//	maxPayload := maxPacketAvailable(m, opt)
//
//	// TODO: Lots of work to do
//
//	fmt.Println(mask, maxPayload)
//	return meLayer, errors.New("todo: Not implemented")
//}

func GetCurrentDataRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Common for all MEs
	meLayer := &GetCurrentDataRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	// Get payload space available
	maxPayload := maxPacketAvailable(m, opt)

	// TODO: Lots of work to do

	fmt.Println(mask, maxPayload)
	return meLayer, errors.New("todo: Not implemented")
}

func GetCurrentDataResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Common for all MEs
	meLayer := &GetCurrentDataResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	// Get payload space available
	maxPayload := maxPacketAvailable(m, opt)

	// TODO: Lots of work to do

	fmt.Println(mask, maxPayload)
	return meLayer, errors.New("todo: Not implemented")
}

func SetTableRequestFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat != ExtendedIdent {
		return nil, errors.New("SetTable message type only supported with Extended OMCI Messaging")
	}
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Common for all MEs
	meLayer := &SetTableRequest{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       opt.frameFormat == ExtendedIdent,
		},
	}
	// Get payload space available
	maxPayload := maxPacketAvailable(m, opt)

	// TODO: Lots of work to do

	fmt.Println(mask, maxPayload)
	return meLayer, errors.New("todo: Not implemented")
}

func SetTableResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
	if opt.frameFormat != ExtendedIdent {
		return nil, errors.New("SetTable message type only supported with Extended OMCI Message Set")
	}
	if opt.frameFormat == ExtendedIdent {
		return nil, errors.New("Extended message set for this message type is not supported")
	}
	mask, err := checkAttributeMask(m, opt.attributeMask)
	if err != nil {
		return nil, err
	}
	// Common for all MEs
	meLayer := &SetTableResponse{
		MeBasePacket: MeBasePacket{
			EntityClass:    m.GetClassID(),
			EntityInstance: m.GetEntityID(),
			Extended:       true,
		},
	}
	// Get payload space available
	maxPayload := maxPacketAvailable(m, opt)

	// TODO: Lots of work to do

	fmt.Println(mask, maxPayload)
	return meLayer, errors.New("todo: Not implemented")
}
