package encoder

import (
	
	
	

	
)

type Code interface {
	Kind() CodeKind
	ToOpcode(*compileContext) Opcodes
	Filter(*FieldQuery) Code
}

type AnonymousCode interface {
	ToAnonymousOpcode(*compileContext) Opcodes
}

type Opcodes []*Opcode

func ( Opcodes) () *Opcode {
	if len() == 0 {
		return nil
	}
	return [0]
}

func ( Opcodes) () *Opcode {
	if len() == 0 {
		return nil
	}
	return [len()-1]
}

func ( Opcodes) ( ...*Opcode) Opcodes {
	return append(, ...)
}

type CodeKind int

const (
	CodeKindInterface CodeKind = iota
	CodeKindPtr
	CodeKindInt
	CodeKindUint
	CodeKindFloat
	CodeKindString
	CodeKindBool
	CodeKindStruct
	CodeKindMap
	CodeKindSlice
	CodeKindArray
	CodeKindBytes
	CodeKindMarshalJSON
	CodeKindMarshalText
	CodeKindRecursive
)

type IntCode struct {
	typ      *runtime.Type
	bitSize  uint8
	isString bool
	isPtr    bool
}

func ( *IntCode) () CodeKind {
	return CodeKindInt
}

func ( *IntCode) ( *compileContext) Opcodes {
	var  *Opcode
	switch {
	case .isPtr:
		 = newOpCode(, .typ, OpIntPtr)
	case .isString:
		 = newOpCode(, .typ, OpIntString)
	default:
		 = newOpCode(, .typ, OpInt)
	}
	.NumBitSize = .bitSize
	.incIndex()
	return Opcodes{}
}

func ( *IntCode) ( *FieldQuery) Code {
	return 
}

type UintCode struct {
	typ      *runtime.Type
	bitSize  uint8
	isString bool
	isPtr    bool
}

func ( *UintCode) () CodeKind {
	return CodeKindUint
}

func ( *UintCode) ( *compileContext) Opcodes {
	var  *Opcode
	switch {
	case .isPtr:
		 = newOpCode(, .typ, OpUintPtr)
	case .isString:
		 = newOpCode(, .typ, OpUintString)
	default:
		 = newOpCode(, .typ, OpUint)
	}
	.NumBitSize = .bitSize
	.incIndex()
	return Opcodes{}
}

func ( *UintCode) ( *FieldQuery) Code {
	return 
}

type FloatCode struct {
	typ     *runtime.Type
	bitSize uint8
	isPtr   bool
}

func ( *FloatCode) () CodeKind {
	return CodeKindFloat
}

func ( *FloatCode) ( *compileContext) Opcodes {
	var  *Opcode
	switch {
	case .isPtr:
		switch .bitSize {
		case 32:
			 = newOpCode(, .typ, OpFloat32Ptr)
		default:
			 = newOpCode(, .typ, OpFloat64Ptr)
		}
	default:
		switch .bitSize {
		case 32:
			 = newOpCode(, .typ, OpFloat32)
		default:
			 = newOpCode(, .typ, OpFloat64)
		}
	}
	.incIndex()
	return Opcodes{}
}

func ( *FloatCode) ( *FieldQuery) Code {
	return 
}

type StringCode struct {
	typ   *runtime.Type
	isPtr bool
}

func ( *StringCode) () CodeKind {
	return CodeKindString
}

func ( *StringCode) ( *compileContext) Opcodes {
	 := .typ == runtime.Type2RType(jsonNumberType)
	var  *Opcode
	if .isPtr {
		if  {
			 = newOpCode(, .typ, OpNumberPtr)
		} else {
			 = newOpCode(, .typ, OpStringPtr)
		}
	} else {
		if  {
			 = newOpCode(, .typ, OpNumber)
		} else {
			 = newOpCode(, .typ, OpString)
		}
	}
	.incIndex()
	return Opcodes{}
}

func ( *StringCode) ( *FieldQuery) Code {
	return 
}

type BoolCode struct {
	typ   *runtime.Type
	isPtr bool
}

func ( *BoolCode) () CodeKind {
	return CodeKindBool
}

func ( *BoolCode) ( *compileContext) Opcodes {
	var  *Opcode
	switch {
	case .isPtr:
		 = newOpCode(, .typ, OpBoolPtr)
	default:
		 = newOpCode(, .typ, OpBool)
	}
	.incIndex()
	return Opcodes{}
}

func ( *BoolCode) ( *FieldQuery) Code {
	return 
}

type BytesCode struct {
	typ   *runtime.Type
	isPtr bool
}

func ( *BytesCode) () CodeKind {
	return CodeKindBytes
}

func ( *BytesCode) ( *compileContext) Opcodes {
	var  *Opcode
	switch {
	case .isPtr:
		 = newOpCode(, .typ, OpBytesPtr)
	default:
		 = newOpCode(, .typ, OpBytes)
	}
	.incIndex()
	return Opcodes{}
}

func ( *BytesCode) ( *FieldQuery) Code {
	return 
}

type SliceCode struct {
	typ   *runtime.Type
	value Code
}

func ( *SliceCode) () CodeKind {
	return CodeKindSlice
}

func ( *SliceCode) ( *compileContext) Opcodes {
	// header => opcode => elem => end
	//             ^        |
	//             |________|
	 := .typ.Elem().Size()
	 := newSliceHeaderCode(, .typ)
	.incIndex()

	.incIndent()
	 := .value.ToOpcode()
	.decIndent()

	.First().Flags |= IndirectFlags
	 := newSliceElemCode(, .typ.Elem(), , )
	.incIndex()
	 := newOpCode(, .typ, OpSliceEnd)
	.incIndex()
	.End = 
	.Next = .First()
	.Last().Next = 
	.Next = .First()
	.End = 
	return Opcodes{}.Add(...).Add().Add()
}

func ( *SliceCode) ( *FieldQuery) Code {
	return 
}

type ArrayCode struct {
	typ   *runtime.Type
	value Code
}

func ( *ArrayCode) () CodeKind {
	return CodeKindArray
}

func ( *ArrayCode) ( *compileContext) Opcodes {
	// header => opcode => elem => end
	//             ^        |
	//             |________|
	 := .typ.Elem()
	 := .typ.Len()
	 := .Size()

	 := newArrayHeaderCode(, .typ, )
	.incIndex()

	.incIndent()
	 := .value.ToOpcode()
	.decIndent()

	.First().Flags |= IndirectFlags

	 := newArrayElemCode(, , , , )
	.incIndex()

	 := newOpCode(, .typ, OpArrayEnd)
	.incIndex()

	.End = 
	.Next = .First()
	.Last().Next = 
	.Next = .First()
	.End = 

	return Opcodes{}.Add(...).Add().Add()
}

func ( *ArrayCode) ( *FieldQuery) Code {
	return 
}

type MapCode struct {
	typ   *runtime.Type
	key   Code
	value Code
}

func ( *MapCode) () CodeKind {
	return CodeKindMap
}

func ( *MapCode) ( *compileContext) Opcodes {
	// header => code => value => code => key => code => value => code => end
	//                                     ^                       |
	//                                     |_______________________|
	 := newMapHeaderCode(, .typ)
	.incIndex()

	 := .key.ToOpcode()

	 := newMapValueCode(, .typ.Elem(), )
	.incIndex()

	.incIndent()
	 := .value.ToOpcode()
	.decIndent()

	.First().Flags |= IndirectFlags

	 := newMapKeyCode(, .typ.Key(), )
	.incIndex()

	 := newMapEndCode(, .typ, )
	.incIndex()

	.Next = .First()
	.Last().Next = 
	.Next = .First()
	.Last().Next = 
	.Next = .First()

	.End = 
	.End = 
	.End = 
	return Opcodes{}.Add(...).Add().Add(...).Add().Add()
}

func ( *MapCode) ( *FieldQuery) Code {
	return 
}

type StructCode struct {
	typ                       *runtime.Type
	fields                    []*StructFieldCode
	isPtr                     bool
	disableIndirectConversion bool
	isIndirect                bool
	isRecursive               bool
}

func ( *StructCode) () CodeKind {
	return CodeKindStruct
}

func ( *StructCode) ( *StructFieldCode,  *Opcode) *Opcode {
	if isEmbeddedStruct() {
		return .lastAnonymousFieldCode()
	}
	 := 
	for .NextField != nil {
		 = .NextField
	}
	return 
}

func ( *StructCode) ( *Opcode) *Opcode {
	// firstField is special StructHead operation for anonymous structure.
	// So, StructHead's next operation is truly struct head operation.
	for .Op == OpStructHead || .Op == OpStructField {
		 = .Next
	}
	 := 
	for .NextField != nil {
		 = .NextField
	}
	return 
}

func ( *StructCode) ( *compileContext) Opcodes {
	// header => code => structField => code => end
	//                        ^          |
	//                        |__________|
	if .isRecursive {
		 := newRecursiveCode(, .typ, &CompiledCode{})
		.Type = .typ
		.incIndex()
		*.recursiveCodes = append(*.recursiveCodes, )
		return Opcodes{}
	}
	 := Opcodes{}
	var  *Opcode
	.incIndent()
	for ,  := range .fields {
		 :=  == 0
		 :=  == len(.fields)-1
		 := .ToOpcode(, , )
		for ,  := range  {
			if .isIndirect {
				.Flags |= IndirectFlags
			}
		}
		 := .First()
		if len() > 0 {
			.Last().Next = 
			.Idx = .First().Idx
		}
		if  != nil {
			.NextField = 
		}
		if  {
			 := .Last()
			if len() > 0 {
				.First().End = 
			} else {
				.End = 
			}
			 = .Add(...)
			break
		}
		 = .lastFieldCode(, )
		 = .Add(...)
	}
	if len() == 0 {
		 := &Opcode{
			Op:         OpStructHead,
			Idx:        opcodeOffset(.ptrIndex),
			Type:       .typ,
			DisplayIdx: .opcodeIndex,
			Indent:     .indent,
		}
		.incOpcodeIndex()
		 := &Opcode{
			Op:         OpStructEnd,
			Idx:        opcodeOffset(.ptrIndex),
			DisplayIdx: .opcodeIndex,
			Indent:     .indent,
		}
		.NextField = 
		.Next = 
		.End = 
		 = .Add(, )
		.incIndex()
	}
	.decIndent()
	.structTypeToCodes[uintptr(unsafe.Pointer(.typ))] = 
	return 
}

func ( *StructCode) ( *compileContext) Opcodes {
	// header => code => structField => code => end
	//                        ^          |
	//                        |__________|
	if .isRecursive {
		 := newRecursiveCode(, .typ, &CompiledCode{})
		.Type = .typ
		.incIndex()
		*.recursiveCodes = append(*.recursiveCodes, )
		return Opcodes{}
	}
	 := Opcodes{}
	var  *Opcode
	for ,  := range .fields {
		 :=  == 0
		 :=  == len(.fields)-1
		 := .ToAnonymousOpcode(, , )
		for ,  := range  {
			if .isIndirect {
				.Flags |= IndirectFlags
			}
		}
		 := .First()
		if len() > 0 {
			.Last().Next = 
			.Idx = .First().Idx
		}
		if  != nil {
			.NextField = 
		}
		if  {
			 := .Last()
			if len() > 0 {
				.First().End = 
			} else {
				.End = 
			}
		}
		 = 
		 = .Add(...)
	}
	return 
}

func ( *StructCode) ( runtime.StructTags) {
	 := make([]*StructFieldCode, 0, len(.fields))
	for ,  := range .fields {
		if .isAnonymous {
			 := .getAnonymousStruct()
			if  != nil && !.isRecursive {
				.()
				if len(.fields) > 0 {
					 = append(, )
				}
				continue
			}
		}
		if .ExistsKey(.key) {
			continue
		}
		 = append(, )
	}
	.fields = 
}

func ( *StructCode) () {
	if .isIndirect {
		return
	}
	.isIndirect = true
	if len(.fields) == 0 {
		return
	}
	 := .fields[0].getStruct()
	if  == nil {
		return
	}
	.()
}

func ( *StructCode) ( *FieldQuery) Code {
	 := map[string]*FieldQuery{}
	for ,  := range .Fields {
		[.Name] = 
	}
	 := make([]*StructFieldCode, 0, len(.fields))
	for ,  := range .fields {
		,  := [.key]
		if ! {
			continue
		}
		 := &StructFieldCode{
			typ:                .typ,
			key:                .key,
			tag:                .tag,
			value:              .value,
			offset:             .offset,
			isAnonymous:        .isAnonymous,
			isTaggedKey:        .isTaggedKey,
			isNilableType:      .isNilableType,
			isNilCheck:         .isNilCheck,
			isAddrForMarshaler: .isAddrForMarshaler,
			isNextOpPtrType:    .isNextOpPtrType,
		}
		if len(.Fields) > 0 {
			.value = .value.Filter()
		}
		 = append(, )
	}
	return &StructCode{
		typ:                       .typ,
		fields:                    ,
		isPtr:                     .isPtr,
		disableIndirectConversion: .disableIndirectConversion,
		isIndirect:                .isIndirect,
		isRecursive:               .isRecursive,
	}
}

type StructFieldCode struct {
	typ                *runtime.Type
	key                string
	tag                *runtime.StructTag
	value              Code
	offset             uintptr
	isAnonymous        bool
	isTaggedKey        bool
	isNilableType      bool
	isNilCheck         bool
	isAddrForMarshaler bool
	isNextOpPtrType    bool
	isMarshalerContext bool
}

func ( *StructFieldCode) () *StructCode {
	 := .value
	,  := .(*PtrCode)
	if  {
		 = .value
	}
	,  := .(*StructCode)
	if  {
		return 
	}
	return nil
}

func ( *StructFieldCode) () *StructCode {
	if !.isAnonymous {
		return nil
	}
	return .getStruct()
}

func optimizeStructHeader( *Opcode,  *runtime.StructTag) OpType {
	 := .ToHeaderType(.IsString)
	if .IsOmitEmpty {
		 = .HeadToOmitEmptyHead()
	}
	return 
}

func optimizeStructField( *Opcode,  *runtime.StructTag) OpType {
	 := .ToFieldType(.IsString)
	if .IsOmitEmpty {
		 = .FieldToOmitEmptyField()
	}
	return 
}

func ( *StructFieldCode) ( *compileContext,  *Opcode,  Opcodes) Opcodes {
	 := .First()
	 := optimizeStructHeader(, .tag)
	.Op = 
	if .Flags&MarshalerContextFlags != 0 {
		.Flags |= MarshalerContextFlags
	}
	.NumBitSize = .NumBitSize
	.PtrNum = .PtrNum
	.FieldQuery = .FieldQuery
	 := Opcodes{}
	if .IsMultipleOpHead() {
		.Next = 
		 = .Add(...)
	} else {
		.decIndex()
	}
	return 
}

func ( *StructFieldCode) ( *compileContext,  *Opcode,  Opcodes) Opcodes {
	 := .First()
	 := optimizeStructField(, .tag)
	.Op = 
	if .Flags&MarshalerContextFlags != 0 {
		.Flags |= MarshalerContextFlags
	}
	.NumBitSize = .NumBitSize
	.PtrNum = .PtrNum
	.FieldQuery = .FieldQuery

	 := Opcodes{}
	if .IsMultipleOpField() {
		.Next = 
		 = .Add(...)
	} else {
		.decIndex()
	}
	return 
}

func ( *StructFieldCode) ( *compileContext,  Opcodes) Opcodes {
	 := &Opcode{
		Op:         OpStructEnd,
		Idx:        opcodeOffset(.ptrIndex),
		DisplayIdx: .opcodeIndex,
		Indent:     .indent,
	}
	.Last().Next = 
	 := .First()
	for .Op == OpStructField || .Op == OpStructHead {
		 = .Next
	}
	for .NextField != nil {
		 = .NextField
	}
	.NextField = 

	 = .Add()
	.incOpcodeIndex()
	return 
}

func ( *StructFieldCode) ( *compileContext) string {
	if .escapeKey {
		 := &RuntimeContext{Option: &Option{Flag: HTMLEscapeOption}}
		return fmt.Sprintf(`%s:`, string(AppendString(, []byte{}, .key)))
	}
	return fmt.Sprintf(`"%s":`, .key)
}

func ( *StructFieldCode) () OpFlags {
	var  OpFlags
	if .isTaggedKey {
		 |= IsTaggedKeyFlags
	}
	if .isNilableType {
		 |= IsNilableTypeFlags
	}
	if .isNilCheck {
		 |= NilCheckFlags
	}
	if .isAddrForMarshaler {
		 |= AddrForMarshalerFlags
	}
	if .isNextOpPtrType {
		 |= IsNextOpPtrTypeFlags
	}
	if .isAnonymous {
		 |= AnonymousKeyFlags
	}
	if .isMarshalerContext {
		 |= MarshalerContextFlags
	}
	return 
}

func ( *StructFieldCode) ( *compileContext) Opcodes {
	if .isAnonymous {
		,  := .value.(AnonymousCode)
		if  {
			return .ToAnonymousOpcode()
		}
	}
	return .value.ToOpcode()
}

func ( *StructFieldCode) ( *compileContext, ,  bool) Opcodes {
	 := &Opcode{
		Idx:        opcodeOffset(.ptrIndex),
		Flags:      .flags(),
		Key:        .structKey(),
		Offset:     uint32(.offset),
		Type:       .typ,
		DisplayIdx: .opcodeIndex,
		Indent:     .indent,
		DisplayKey: .key,
	}
	.incIndex()
	 := .toValueOpcodes()
	if  {
		 := .headerOpcodes(, , )
		if  {
			 = .addStructEndCode(, )
		}
		return 
	}
	 := .fieldOpcodes(, , )
	if  {
		if isEnableStructEndOptimization(.value) {
			.Op = .Op.FieldToEnd()
		} else {
			 = .addStructEndCode(, )
		}
	}
	return 
}

func ( *StructFieldCode) ( *compileContext, ,  bool) Opcodes {
	 := &Opcode{
		Idx:        opcodeOffset(.ptrIndex),
		Flags:      .flags() | AnonymousHeadFlags,
		Key:        .structKey(),
		Offset:     uint32(.offset),
		Type:       .typ,
		DisplayIdx: .opcodeIndex,
		Indent:     .indent,
		DisplayKey: .key,
	}
	.incIndex()
	 := .toValueOpcodes()
	if  {
		return .headerOpcodes(, , )
	}
	return .fieldOpcodes(, , )
}

func isEnableStructEndOptimization( Code) bool {
	switch .Kind() {
	case CodeKindInt,
		CodeKindUint,
		CodeKindFloat,
		CodeKindString,
		CodeKindBool,
		CodeKindBytes:
		return true
	case CodeKindPtr:
		return (.(*PtrCode).value)
	default:
		return false
	}
}

type InterfaceCode struct {
	typ        *runtime.Type
	fieldQuery *FieldQuery
	isPtr      bool
}

func ( *InterfaceCode) () CodeKind {
	return CodeKindInterface
}

func ( *InterfaceCode) ( *compileContext) Opcodes {
	var  *Opcode
	switch {
	case .isPtr:
		 = newOpCode(, .typ, OpInterfacePtr)
	default:
		 = newOpCode(, .typ, OpInterface)
	}
	.FieldQuery = .fieldQuery
	if .typ.NumMethod() > 0 {
		.Flags |= NonEmptyInterfaceFlags
	}
	.incIndex()
	return Opcodes{}
}

func ( *InterfaceCode) ( *FieldQuery) Code {
	return &InterfaceCode{
		typ:        .typ,
		fieldQuery: ,
		isPtr:      .isPtr,
	}
}

type MarshalJSONCode struct {
	typ                *runtime.Type
	fieldQuery         *FieldQuery
	isAddrForMarshaler bool
	isNilableType      bool
	isMarshalerContext bool
}

func ( *MarshalJSONCode) () CodeKind {
	return CodeKindMarshalJSON
}

func ( *MarshalJSONCode) ( *compileContext) Opcodes {
	 := newOpCode(, .typ, OpMarshalJSON)
	.FieldQuery = .fieldQuery
	if .isAddrForMarshaler {
		.Flags |= AddrForMarshalerFlags
	}
	if .isMarshalerContext {
		.Flags |= MarshalerContextFlags
	}
	if .isNilableType {
		.Flags |= IsNilableTypeFlags
	} else {
		.Flags &= ^IsNilableTypeFlags
	}
	.incIndex()
	return Opcodes{}
}

func ( *MarshalJSONCode) ( *FieldQuery) Code {
	return &MarshalJSONCode{
		typ:                .typ,
		fieldQuery:         ,
		isAddrForMarshaler: .isAddrForMarshaler,
		isNilableType:      .isNilableType,
		isMarshalerContext: .isMarshalerContext,
	}
}

type MarshalTextCode struct {
	typ                *runtime.Type
	fieldQuery         *FieldQuery
	isAddrForMarshaler bool
	isNilableType      bool
}

func ( *MarshalTextCode) () CodeKind {
	return CodeKindMarshalText
}

func ( *MarshalTextCode) ( *compileContext) Opcodes {
	 := newOpCode(, .typ, OpMarshalText)
	.FieldQuery = .fieldQuery
	if .isAddrForMarshaler {
		.Flags |= AddrForMarshalerFlags
	}
	if .isNilableType {
		.Flags |= IsNilableTypeFlags
	} else {
		.Flags &= ^IsNilableTypeFlags
	}
	.incIndex()
	return Opcodes{}
}

func ( *MarshalTextCode) ( *FieldQuery) Code {
	return &MarshalTextCode{
		typ:                .typ,
		fieldQuery:         ,
		isAddrForMarshaler: .isAddrForMarshaler,
		isNilableType:      .isNilableType,
	}
}

type PtrCode struct {
	typ    *runtime.Type
	value  Code
	ptrNum uint8
}

func ( *PtrCode) () CodeKind {
	return CodeKindPtr
}

func ( *PtrCode) ( *compileContext) Opcodes {
	 := .value.ToOpcode()
	.First().Op = convertPtrOp(.First())
	.First().PtrNum = .ptrNum
	return 
}

func ( *PtrCode) ( *compileContext) Opcodes {
	var  Opcodes
	,  := .value.(AnonymousCode)
	if  {
		 = .ToAnonymousOpcode()
	} else {
		 = .value.ToOpcode()
	}
	.First().Op = convertPtrOp(.First())
	.First().PtrNum = .ptrNum
	return 
}

func ( *PtrCode) ( *FieldQuery) Code {
	return &PtrCode{
		typ:    .typ,
		value:  .value.Filter(),
		ptrNum: .ptrNum,
	}
}

func convertPtrOp( *Opcode) OpType {
	 := .Op.HeadToPtrHead()
	if .Op !=  {
		if .PtrNum > 0 {
			// ptr field and ptr head
			.PtrNum--
		}
		return 
	}
	switch .Op {
	case OpInt:
		return OpIntPtr
	case OpUint:
		return OpUintPtr
	case OpFloat32:
		return OpFloat32Ptr
	case OpFloat64:
		return OpFloat64Ptr
	case OpString:
		return OpStringPtr
	case OpBool:
		return OpBoolPtr
	case OpBytes:
		return OpBytesPtr
	case OpNumber:
		return OpNumberPtr
	case OpArray:
		return OpArrayPtr
	case OpSlice:
		return OpSlicePtr
	case OpMap:
		return OpMapPtr
	case OpMarshalJSON:
		return OpMarshalJSONPtr
	case OpMarshalText:
		return OpMarshalTextPtr
	case OpInterface:
		return OpInterfacePtr
	case OpRecursive:
		return OpRecursivePtr
	}
	return .Op
}

func isEmbeddedStruct( *StructFieldCode) bool {
	if !.isAnonymous {
		return false
	}
	 := .typ
	if .Kind() == reflect.Ptr {
		 = .Elem()
	}
	return .Kind() == reflect.Struct
}