package binary

import (
	
	
	

	
	
	
)

func ensureElementKindFuncRef( *bytes.Reader) error {
	,  := .ReadByte()
	if  != nil {
		return fmt.Errorf("read element prefix: %w", )
	}
	if  != 0x0 { // ElemKind is fixed to 0x0 now: https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section
		return fmt.Errorf("element kind must be zero but was 0x%x", )
	}
	return nil
}

func decodeElementInitValueVector( *bytes.Reader) ([]wasm.Index, error) {
	, ,  := leb128.DecodeUint32()
	if  != nil {
		return nil, fmt.Errorf("get size of vector: %w", )
	}

	 := make([]wasm.Index, )
	for  := range  {
		, ,  := leb128.DecodeUint32()
		if  != nil {
			return nil, fmt.Errorf("read function index: %w", )
		}

		if  >= wasm.MaximumFunctionIndex {
			return nil, fmt.Errorf("too large function index in Element init: %d", )
		}
		[] = 
	}
	return , nil
}

func decodeElementConstExprVector( *bytes.Reader,  wasm.RefType,  api.CoreFeatures) ([]wasm.Index, error) {
	, ,  := leb128.DecodeUint32()
	if  != nil {
		return nil, fmt.Errorf("failed to get the size of constexpr vector: %w", )
	}
	 := make([]wasm.Index, )
	for  := range  {
		var  wasm.ConstantExpression
		 := decodeConstantExpression(, , &)
		if  != nil {
			return nil, 
		}
		switch .Opcode {
		case wasm.OpcodeRefFunc:
			if  != wasm.RefTypeFuncref {
				return nil, fmt.Errorf("element type mismatch: want %s, but constexpr has funcref", wasm.RefTypeName())
			}
			, ,  := leb128.LoadUint32(.Data)
			if  >= wasm.MaximumFunctionIndex {
				return nil, fmt.Errorf("too large function index in Element init: %d", )
			}
			[] = 
		case wasm.OpcodeRefNull:
			if  != .Data[0] {
				return nil, fmt.Errorf("element type mismatch: want %s, but constexpr has %s",
					wasm.RefTypeName(), wasm.RefTypeName(.Data[0]))
			}
			[] = wasm.ElementInitNullReference
		case wasm.OpcodeGlobalGet:
			, ,  := leb128.LoadInt32(.Data)
			// Resolving the reference type from globals is done at instantiation phase. See the comment on
			// wasm.elementInitImportedGlobalReferenceType.
			[] = wasm.WrapGlobalIndexAsElementInit(wasm.Index())
		default:
			return nil, fmt.Errorf("const expr must be either ref.null or ref.func but was %s", wasm.InstructionName(.Opcode))
		}
	}
	return , nil
}

func decodeElementRefType( *bytes.Reader) ( wasm.RefType,  error) {
	,  = .ReadByte()
	if  != nil {
		 = fmt.Errorf("read element ref type: %w", )
		return
	}
	if  != wasm.RefTypeFuncref &&  != wasm.RefTypeExternref {
		return 0, errors.New("ref type must be funcref or externref for element as of WebAssembly 2.0")
	}
	return
}

const (
	// The prefix is explained at https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section

	// elementSegmentPrefixLegacy is the legacy prefix and is only valid one before CoreFeatureBulkMemoryOperations.
	elementSegmentPrefixLegacy = iota
	// elementSegmentPrefixPassiveFuncrefValueVector is the passive element whose indexes are encoded as vec(varint), and reftype is fixed to funcref.
	elementSegmentPrefixPassiveFuncrefValueVector
	// elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex is the same as elementSegmentPrefixPassiveFuncrefValueVector but active and table index is encoded.
	elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex
	// elementSegmentPrefixDeclarativeFuncrefValueVector is the same as elementSegmentPrefixPassiveFuncrefValueVector but declarative.
	elementSegmentPrefixDeclarativeFuncrefValueVector
	// elementSegmentPrefixActiveFuncrefConstExprVector is active whoce reftype is fixed to funcref and indexes are encoded as vec(const_expr).
	elementSegmentPrefixActiveFuncrefConstExprVector
	// elementSegmentPrefixPassiveConstExprVector is passive whoce indexes are encoded as vec(const_expr), and reftype is encoded.
	elementSegmentPrefixPassiveConstExprVector
	// elementSegmentPrefixPassiveConstExprVector is active whoce indexes are encoded as vec(const_expr), and reftype and table index are encoded.
	elementSegmentPrefixActiveConstExprVector
	// elementSegmentPrefixDeclarativeConstExprVector is declarative whoce indexes are encoded as vec(const_expr), and reftype is encoded.
	elementSegmentPrefixDeclarativeConstExprVector
)

func decodeElementSegment( *bytes.Reader,  api.CoreFeatures,  *wasm.ElementSegment) error {
	, ,  := leb128.DecodeUint32()
	if  != nil {
		return fmt.Errorf("read element prefix: %w", )
	}

	if  != elementSegmentPrefixLegacy {
		if  := .RequireEnabled(api.CoreFeatureBulkMemoryOperations);  != nil {
			return fmt.Errorf("non-zero prefix for element segment is invalid as %w", )
		}
	}

	// Encoding depends on the prefix and described at https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section
	switch  {
	case elementSegmentPrefixLegacy:
		// Legacy prefix which is WebAssembly 1.0 compatible.
		 = decodeConstantExpression(, , &.OffsetExpr)
		if  != nil {
			return fmt.Errorf("read expr for offset: %w", )
		}

		.Init,  = decodeElementInitValueVector()
		if  != nil {
			return 
		}

		.Mode = wasm.ElementModeActive
		.Type = wasm.RefTypeFuncref
		return nil
	case elementSegmentPrefixPassiveFuncrefValueVector:
		// Prefix 1 requires funcref.
		if  = ensureElementKindFuncRef();  != nil {
			return 
		}

		.Init,  = decodeElementInitValueVector()
		if  != nil {
			return 
		}
		.Mode = wasm.ElementModePassive
		.Type = wasm.RefTypeFuncref
		return nil
	case elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex:
		.TableIndex, _,  = leb128.DecodeUint32()
		if  != nil {
			return fmt.Errorf("get size of vector: %w", )
		}

		if .TableIndex != 0 {
			if  := .RequireEnabled(api.CoreFeatureReferenceTypes);  != nil {
				return fmt.Errorf("table index must be zero but was %d: %w", .TableIndex, )
			}
		}

		 := decodeConstantExpression(, , &.OffsetExpr)
		if  != nil {
			return fmt.Errorf("read expr for offset: %w", )
		}

		// Prefix 2 requires funcref.
		if  = ensureElementKindFuncRef();  != nil {
			return 
		}

		.Init,  = decodeElementInitValueVector()
		if  != nil {
			return 
		}

		.Mode = wasm.ElementModeActive
		.Type = wasm.RefTypeFuncref
		return nil
	case elementSegmentPrefixDeclarativeFuncrefValueVector:
		// Prefix 3 requires funcref.
		if  = ensureElementKindFuncRef();  != nil {
			return 
		}
		.Init,  = decodeElementInitValueVector()
		if  != nil {
			return 
		}
		.Type = wasm.RefTypeFuncref
		.Mode = wasm.ElementModeDeclarative
		return nil
	case elementSegmentPrefixActiveFuncrefConstExprVector:
		 := decodeConstantExpression(, , &.OffsetExpr)
		if  != nil {
			return fmt.Errorf("read expr for offset: %w", )
		}

		.Init,  = decodeElementConstExprVector(, wasm.RefTypeFuncref, )
		if  != nil {
			return 
		}
		.Mode = wasm.ElementModeActive
		.Type = wasm.RefTypeFuncref
		return nil
	case elementSegmentPrefixPassiveConstExprVector:
		.Type,  = decodeElementRefType()
		if  != nil {
			return 
		}
		.Init,  = decodeElementConstExprVector(, .Type, )
		if  != nil {
			return 
		}
		.Mode = wasm.ElementModePassive
		return nil
	case elementSegmentPrefixActiveConstExprVector:
		.TableIndex, _,  = leb128.DecodeUint32()
		if  != nil {
			return fmt.Errorf("get size of vector: %w", )
		}

		if .TableIndex != 0 {
			if  := .RequireEnabled(api.CoreFeatureReferenceTypes);  != nil {
				return fmt.Errorf("table index must be zero but was %d: %w", .TableIndex, )
			}
		}
		 := decodeConstantExpression(, , &.OffsetExpr)
		if  != nil {
			return fmt.Errorf("read expr for offset: %w", )
		}

		.Type,  = decodeElementRefType()
		if  != nil {
			return 
		}

		.Init,  = decodeElementConstExprVector(, .Type, )
		if  != nil {
			return 
		}

		.Mode = wasm.ElementModeActive
		return nil
	case elementSegmentPrefixDeclarativeConstExprVector:
		.Type,  = decodeElementRefType()
		if  != nil {
			return 
		}
		.Init,  = decodeElementConstExprVector(, .Type, )
		if  != nil {
			return 
		}

		.Mode = wasm.ElementModeDeclarative
		return nil
	default:
		return fmt.Errorf("invalid element segment prefix: 0x%x", )
	}
}