// Copyright 2019 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 protodesc

import (
	
	
	

	
	
	
	
	
	
	
)

var defaults = &descriptorpb.FeatureSetDefaults{}
var defaultsCacheMu sync.Mutex
var defaultsCache = make(map[filedesc.Edition]*descriptorpb.FeatureSet)

func init() {
	 := proto.Unmarshal(editiondefaults.Defaults, defaults)
	if  != nil {
		fmt.Fprintf(os.Stderr, "unmarshal editions defaults: %v\n", )
		os.Exit(1)
	}
}

func fromEditionProto( descriptorpb.Edition) filedesc.Edition {
	return filedesc.Edition()
}

func toEditionProto( filedesc.Edition) descriptorpb.Edition {
	switch  {
	case filedesc.EditionUnknown:
		return descriptorpb.Edition_EDITION_UNKNOWN
	case filedesc.EditionProto2:
		return descriptorpb.Edition_EDITION_PROTO2
	case filedesc.EditionProto3:
		return descriptorpb.Edition_EDITION_PROTO3
	case filedesc.Edition2023:
		return descriptorpb.Edition_EDITION_2023
	case filedesc.Edition2024:
		return descriptorpb.Edition_EDITION_2024
	default:
		panic(fmt.Sprintf("unknown value for edition: %v", ))
	}
}

func getFeatureSetFor( filedesc.Edition) *descriptorpb.FeatureSet {
	defaultsCacheMu.Lock()
	defer defaultsCacheMu.Unlock()
	if ,  := defaultsCache[];  {
		return 
	}
	 := toEditionProto()
	if defaults.GetMinimumEdition() >  || defaults.GetMaximumEdition() <  {
		// This should never happen protodesc.(FileOptions).New would fail when
		// initializing the file descriptor.
		// This most likely means the embedded defaults were not updated.
		fmt.Fprintf(os.Stderr, "internal error: unsupported edition %v (did you forget to update the embedded defaults (i.e. the bootstrap descriptor proto)?)\n", )
		os.Exit(1)
	}
	 := defaults.GetDefaults()[0]
	// Using a linear search for now.
	// Editions are guaranteed to be sorted and thus we could use a binary search.
	// Given that there are only a handful of editions (with one more per year)
	// there is not much reason to use a binary search.
	for ,  := range defaults.GetDefaults() {
		if .GetEdition() <=  {
			 = 
		} else {
			break
		}
	}
	 := proto.Clone(.GetFixedFeatures()).(*descriptorpb.FeatureSet)
	proto.Merge(, .GetOverridableFeatures())
	defaultsCache[] = 
	return 
}

// mergeEditionFeatures merges the parent and child feature sets. This function
// should be used when initializing Go descriptors from descriptor protos which
// is why the parent is a filedesc.EditionsFeatures (Go representation) while
// the child is a descriptorproto.FeatureSet (protoc representation).
// Any feature set by the child overwrites what is set by the parent.
func mergeEditionFeatures( protoreflect.Descriptor,  *descriptorpb.FeatureSet) filedesc.EditionFeatures {
	var  filedesc.EditionFeatures
	switch p := .(type) {
	case *filedesc.File:
		 = .L1.EditionFeatures
	case *filedesc.Message:
		 = .L1.EditionFeatures
	default:
		panic(fmt.Sprintf("unknown parent type %T", ))
	}
	if  == nil {
		return 
	}
	if  := .FieldPresence;  != nil {
		.IsFieldPresence = * == descriptorpb.FeatureSet_LEGACY_REQUIRED ||
			* == descriptorpb.FeatureSet_EXPLICIT
		.IsLegacyRequired = * == descriptorpb.FeatureSet_LEGACY_REQUIRED
	}
	if  := .EnumType;  != nil {
		.IsOpenEnum = * == descriptorpb.FeatureSet_OPEN
	}

	if  := .RepeatedFieldEncoding;  != nil {
		.IsPacked = * == descriptorpb.FeatureSet_PACKED
	}

	if  := .Utf8Validation;  != nil {
		.IsUTF8Validated = * == descriptorpb.FeatureSet_VERIFY
	}

	if  := .MessageEncoding;  != nil {
		.IsDelimitedEncoded = * == descriptorpb.FeatureSet_DELIMITED
	}

	if  := .JsonFormat;  != nil {
		.IsJSONCompliant = * == descriptorpb.FeatureSet_ALLOW
	}

	// We must not use proto.GetExtension(child, gofeaturespb.E_Go)
	// because that only works for messages we generated, but not for
	// dynamicpb messages. See golang/protobuf#1669.
	//
	// Further, we harden this code against adversarial inputs: a
	// service which accepts descriptors from a possibly malicious
	// source shouldn't crash.
	 := .ProtoReflect().Get(gofeaturespb.E_Go.TypeDescriptor())
	if !.IsValid() {
		return 
	}
	,  := .Interface().(protoreflect.Message)
	if ! {
		return 
	}
	// gf.Interface() could be *dynamicpb.Message or *gofeaturespb.GoFeatures.
	 := .Descriptor().Fields()

	if  := .ByNumber(genid.GoFeatures_LegacyUnmarshalJsonEnum_field_number);  != nil &&
		!.IsList() &&
		.Kind() == protoreflect.BoolKind &&
		.Has() {
		.GenerateLegacyUnmarshalJSON = .Get().Bool()
	}

	if  := .ByNumber(genid.GoFeatures_StripEnumPrefix_field_number);  != nil &&
		!.IsList() &&
		.Kind() == protoreflect.EnumKind &&
		.Has() {
		.StripEnumPrefix = int(.Get().Enum())
	}

	if  := .ByNumber(genid.GoFeatures_ApiLevel_field_number);  != nil &&
		!.IsList() &&
		.Kind() == protoreflect.EnumKind &&
		.Has() {
		.APILevel = int(.Get().Enum())
	}

	return 
}

// initFileDescFromFeatureSet initializes editions related fields in fd based
// on fs. If fs is nil it is assumed to be an empty featureset and all fields
// will be initialized with the appropriate default. fd.L1.Edition must be set
// before calling this function.
func initFileDescFromFeatureSet( *filedesc.File,  *descriptorpb.FeatureSet) {
	 := getFeatureSetFor(.L1.Edition)
	// initialize the featureset with the defaults
	.L1.EditionFeatures = mergeEditionFeatures(, )
	// overwrite any options explicitly specified
	.L1.EditionFeatures = mergeEditionFeatures(, )
}