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

//go:build goexperiment.jsonv2

package jsonopts

import (
	
	
)

// Options is the common options type shared across json packages.
type Options interface {
	// JSONOptions is exported so related json packages can implement Options.
	JSONOptions(internal.NotForPublicUse)
}

// experimentalFormatTagSupporter is a special option method that allows
// [github.com/go-json-experiment/json.ExperimentalSupportFormatTag]
// to enable experimental `format` tag functionality at runtime.
type experimentalFormatTagSupporter interface{ ExperimentalSupportFormatTag() bool }

// Struct is the combination of all options in struct form.
// This is efficient to pass down the call stack and to query.
type Struct struct {
	Flags jsonflags.Flags

	CoderValues
	ArshalValues
}

type CoderValues struct {
	Indent       string // jsonflags.Indent
	IndentPrefix string // jsonflags.IndentPrefix
	ByteLimit    int64  // jsonflags.ByteLimit
	DepthLimit   int    // jsonflags.DepthLimit
}

type ArshalValues struct {
	// The Marshalers and Unmarshalers fields use the any type to avoid a
	// concrete dependency on *json.Marshalers and *json.Unmarshalers,
	// which would in turn create a dependency on the "reflect" package.

	Marshalers   any // jsonflags.Marshalers
	Unmarshalers any // jsonflags.Unmarshalers

	Format string // valid if jsonflags.FormatTag is set
}

// DefaultOptionsV2 is the set of all options that define default v2 behavior.
var DefaultOptionsV2 = Struct{
	Flags: jsonflags.Flags{
		Presence: uint64(jsonflags.DefaultV1Flags),
		Values:   uint64(0), // all flags in DefaultV1Flags are false
	},
}

// DefaultOptionsV1 is the set of all options that define default v1 behavior.
var DefaultOptionsV1 = Struct{
	Flags: jsonflags.Flags{
		Presence: uint64(jsonflags.DefaultV1Flags),
		Values:   uint64(jsonflags.DefaultV1Flags), // all flags in DefaultV1Flags are true
	},
}

func (*Struct) (internal.NotForPublicUse) {}

// GetUnknownOption is injected by the "json" package to handle Options
// declared in that package so that "jsonopts" can handle them.
var GetUnknownOption = func(Struct, Options) (any, bool) { panic("unknown option") }

func [ any]( Options,  func() Options) (, bool) {
	// Collapse the options to *Struct to simplify lookup.
	,  := .(*Struct)
	if ! {
		var  Struct
		.Join()
		 = &
	}

	// Lookup the option based on the return value of the setter.
	var  
	switch opt := ().(type) {
	case jsonflags.Bools:
		 := .Flags.Get()
		 := .Flags.Has()
		if ! &&  == jsonflags.StringifyNumbers && .Flags.Get(jsonflags.StringTag) {
			return any(true).(), true // check also whether the option is specified via a `string` tag
		}
		return any().(), 
	case experimentalFormatTagSupporter:
		 := .Flags.Get(jsonflags.FormatTagSupported)
		 := .Flags.Has(jsonflags.FormatTagSupported)
		return any().(), 
	case Indent:
		if !.Flags.Has(jsonflags.Indent) {
			return , false
		}
		return any(.Indent).(), true
	case IndentPrefix:
		if !.Flags.Has(jsonflags.IndentPrefix) {
			return , false
		}
		return any(.IndentPrefix).(), true
	case ByteLimit:
		if !.Flags.Has(jsonflags.ByteLimit) {
			return , false
		}
		return any(.ByteLimit).(), true
	case DepthLimit:
		if !.Flags.Has(jsonflags.DepthLimit) {
			return , false
		}
		return any(.DepthLimit).(), true
	default:
		,  := GetUnknownOption(*, )
		return .(), 
	}
}

// JoinUnknownOption is injected by the "json" package to handle Options
// declared in that package so that "jsonopts" can handle them.
var JoinUnknownOption = func(Struct, Options) Struct { panic("unknown option") }

func ( *Struct) ( ...Options) {
	for ,  := range  {
		switch src := .(type) {
		case nil:
			continue
		case jsonflags.Bools:
			.Flags.Set()
		case experimentalFormatTagSupporter:
			if .ExperimentalSupportFormatTag() {
				.Flags.Set(jsonflags.FormatTagSupported | 1)
			} else {
				.Flags.Set(jsonflags.FormatTagSupported | 0)
			}
		case Indent:
			.Flags.Set(jsonflags.Multiline | jsonflags.Indent | 1)
			.Indent = string()
		case IndentPrefix:
			.Flags.Set(jsonflags.Multiline | jsonflags.IndentPrefix | 1)
			.IndentPrefix = string()
		case ByteLimit:
			.Flags.Set(jsonflags.ByteLimit | 1)
			.ByteLimit = int64()
		case DepthLimit:
			.Flags.Set(jsonflags.DepthLimit | 1)
			.DepthLimit = int()
		case *Struct:
			.Flags.Join(.Flags)
			if .Flags.Has(jsonflags.NonBooleanFlags) {
				if .Flags.Has(jsonflags.Indent) {
					.Indent = .Indent
				}
				if .Flags.Has(jsonflags.IndentPrefix) {
					.IndentPrefix = .IndentPrefix
				}
				if .Flags.Has(jsonflags.ByteLimit) {
					.ByteLimit = .ByteLimit
				}
				if .Flags.Has(jsonflags.DepthLimit) {
					.DepthLimit = .DepthLimit
				}
				if .Flags.Has(jsonflags.Marshalers) {
					.Marshalers = .Marshalers
				}
				if .Flags.Has(jsonflags.Unmarshalers) {
					.Unmarshalers = .Unmarshalers
				}
				if .Flags.Has(jsonflags.FormatTag) {
					.Format = .Format
				}
			}
		default:
			* = JoinUnknownOption(*, )
		}
	}
}

// InitializeMultiline sets default options implied by Multiline.
func ( *Struct) () {
	if !.Flags.Has(jsonflags.SpaceAfterColon) {
		.Flags.Set(jsonflags.SpaceAfterColon | 1)
	}
	if !.Flags.Has(jsonflags.SpaceAfterComma) {
		.Flags.Set(jsonflags.SpaceAfterComma | 0)
	}
	if !.Flags.Has(jsonflags.Indent) {
		.Flags.Set(jsonflags.Indent | 1)
		.Indent = "\t"
	}
}

// ChangedWhitespace reports whether whitespace values have changed.
func (,  Struct) bool {
	return .Flags.Get(jsonflags.Multiline) != .Flags.Get(jsonflags.Multiline) ||
		.Flags.Get(jsonflags.SpaceAfterColon) != .Flags.Get(jsonflags.SpaceAfterColon) ||
		.Flags.Get(jsonflags.SpaceAfterComma) != .Flags.Get(jsonflags.SpaceAfterComma) ||
		(.Flags.Get(jsonflags.Multiline) && (.Indent != .Indent || .IndentPrefix != .IndentPrefix))
}

type (
	Indent       string // jsontext.WithIndent
	IndentPrefix string // jsontext.WithIndentPrefix
	ByteLimit    int64  // jsontext.WithByteLimit
	DepthLimit   int    // jsontext.WithDepthLimit
	// type for jsonflags.Marshalers declared in "json" package
	// type for jsonflags.Unmarshalers declared in "json" package
)

func (Indent) (internal.NotForPublicUse)       {}
func (IndentPrefix) (internal.NotForPublicUse) {}
func (ByteLimit) (internal.NotForPublicUse)    {}
func (DepthLimit) (internal.NotForPublicUse)   {}