package encoder

import (
	
	
	
	
	

	
)

const uintptrSize = 4 << (^uintptr(0) >> 63)

type OpFlags uint16

const (
	AnonymousHeadFlags     OpFlags = 1 << 0
	AnonymousKeyFlags      OpFlags = 1 << 1
	IndirectFlags          OpFlags = 1 << 2
	IsTaggedKeyFlags       OpFlags = 1 << 3
	NilCheckFlags          OpFlags = 1 << 4
	AddrForMarshalerFlags  OpFlags = 1 << 5
	IsNextOpPtrTypeFlags   OpFlags = 1 << 6
	IsNilableTypeFlags     OpFlags = 1 << 7
	MarshalerContextFlags  OpFlags = 1 << 8
	NonEmptyInterfaceFlags OpFlags = 1 << 9
)

type Opcode struct {
	Op         OpType  // operation type
	Idx        uint32  // offset to access ptr
	Next       *Opcode // next opcode
	End        *Opcode // array/slice/struct/map end
	NextField  *Opcode // next struct field
	Key        string  // struct field key
	Offset     uint32  // offset size from struct header
	PtrNum     uint8   // pointer number: e.g. double pointer is 2.
	NumBitSize uint8
	Flags      OpFlags

	Type       *runtime.Type // go type
	Jmp        *CompiledCode // for recursive call
	FieldQuery *FieldQuery   // field query for Interface / MarshalJSON / MarshalText
	ElemIdx    uint32        // offset to access array/slice elem
	Length     uint32        // offset to access slice length or array length
	Indent     uint32        // indent number
	Size       uint32        // array/slice elem size
	DisplayIdx uint32        // opcode index
	DisplayKey string        // key text to display
}

func ( *Opcode) () error {
	var  uint32
	for  := ; !.IsEnd(); {
		if  != 0 {
			if .DisplayIdx != +1 {
				return fmt.Errorf(
					"invalid index. previous display index is %d but next is %d. dump = %s",
					, .DisplayIdx, .Dump(),
				)
			}
		}
		 = .DisplayIdx
		 = .IterNext()
	}
	return nil
}

func ( *Opcode) () *Opcode {
	if  == nil {
		return nil
	}
	switch .Op.CodeType() {
	case CodeArrayElem, CodeSliceElem, CodeMapKey:
		return .End
	default:
		return .Next
	}
}

func ( *Opcode) () bool {
	if  == nil {
		return true
	}
	return .Op == OpEnd || .Op == OpInterfaceEnd || .Op == OpRecursiveEnd
}

func ( *Opcode) () uint32 {
	 := uint32(0)
	for ,  := range []uint32{
		.Idx,
		.ElemIdx,
		.Length,
		.Size,
	} {
		if  <  {
			 = 
		}
	}
	return 
}

func ( *Opcode) ( bool) OpType {
	switch .Op {
	case OpInt:
		if  {
			return OpStructHeadIntString
		}
		return OpStructHeadInt
	case OpIntPtr:
		if  {
			return OpStructHeadIntPtrString
		}
		return OpStructHeadIntPtr
	case OpUint:
		if  {
			return OpStructHeadUintString
		}
		return OpStructHeadUint
	case OpUintPtr:
		if  {
			return OpStructHeadUintPtrString
		}
		return OpStructHeadUintPtr
	case OpFloat32:
		if  {
			return OpStructHeadFloat32String
		}
		return OpStructHeadFloat32
	case OpFloat32Ptr:
		if  {
			return OpStructHeadFloat32PtrString
		}
		return OpStructHeadFloat32Ptr
	case OpFloat64:
		if  {
			return OpStructHeadFloat64String
		}
		return OpStructHeadFloat64
	case OpFloat64Ptr:
		if  {
			return OpStructHeadFloat64PtrString
		}
		return OpStructHeadFloat64Ptr
	case OpString:
		if  {
			return OpStructHeadStringString
		}
		return OpStructHeadString
	case OpStringPtr:
		if  {
			return OpStructHeadStringPtrString
		}
		return OpStructHeadStringPtr
	case OpNumber:
		if  {
			return OpStructHeadNumberString
		}
		return OpStructHeadNumber
	case OpNumberPtr:
		if  {
			return OpStructHeadNumberPtrString
		}
		return OpStructHeadNumberPtr
	case OpBool:
		if  {
			return OpStructHeadBoolString
		}
		return OpStructHeadBool
	case OpBoolPtr:
		if  {
			return OpStructHeadBoolPtrString
		}
		return OpStructHeadBoolPtr
	case OpBytes:
		return OpStructHeadBytes
	case OpBytesPtr:
		return OpStructHeadBytesPtr
	case OpMap:
		return OpStructHeadMap
	case OpMapPtr:
		.Op = OpMap
		return OpStructHeadMapPtr
	case OpArray:
		return OpStructHeadArray
	case OpArrayPtr:
		.Op = OpArray
		return OpStructHeadArrayPtr
	case OpSlice:
		return OpStructHeadSlice
	case OpSlicePtr:
		.Op = OpSlice
		return OpStructHeadSlicePtr
	case OpMarshalJSON:
		return OpStructHeadMarshalJSON
	case OpMarshalJSONPtr:
		return OpStructHeadMarshalJSONPtr
	case OpMarshalText:
		return OpStructHeadMarshalText
	case OpMarshalTextPtr:
		return OpStructHeadMarshalTextPtr
	}
	return OpStructHead
}

func ( *Opcode) ( bool) OpType {
	switch .Op {
	case OpInt:
		if  {
			return OpStructFieldIntString
		}
		return OpStructFieldInt
	case OpIntPtr:
		if  {
			return OpStructFieldIntPtrString
		}
		return OpStructFieldIntPtr
	case OpUint:
		if  {
			return OpStructFieldUintString
		}
		return OpStructFieldUint
	case OpUintPtr:
		if  {
			return OpStructFieldUintPtrString
		}
		return OpStructFieldUintPtr
	case OpFloat32:
		if  {
			return OpStructFieldFloat32String
		}
		return OpStructFieldFloat32
	case OpFloat32Ptr:
		if  {
			return OpStructFieldFloat32PtrString
		}
		return OpStructFieldFloat32Ptr
	case OpFloat64:
		if  {
			return OpStructFieldFloat64String
		}
		return OpStructFieldFloat64
	case OpFloat64Ptr:
		if  {
			return OpStructFieldFloat64PtrString
		}
		return OpStructFieldFloat64Ptr
	case OpString:
		if  {
			return OpStructFieldStringString
		}
		return OpStructFieldString
	case OpStringPtr:
		if  {
			return OpStructFieldStringPtrString
		}
		return OpStructFieldStringPtr
	case OpNumber:
		if  {
			return OpStructFieldNumberString
		}
		return OpStructFieldNumber
	case OpNumberPtr:
		if  {
			return OpStructFieldNumberPtrString
		}
		return OpStructFieldNumberPtr
	case OpBool:
		if  {
			return OpStructFieldBoolString
		}
		return OpStructFieldBool
	case OpBoolPtr:
		if  {
			return OpStructFieldBoolPtrString
		}
		return OpStructFieldBoolPtr
	case OpBytes:
		return OpStructFieldBytes
	case OpBytesPtr:
		return OpStructFieldBytesPtr
	case OpMap:
		return OpStructFieldMap
	case OpMapPtr:
		.Op = OpMap
		return OpStructFieldMapPtr
	case OpArray:
		return OpStructFieldArray
	case OpArrayPtr:
		.Op = OpArray
		return OpStructFieldArrayPtr
	case OpSlice:
		return OpStructFieldSlice
	case OpSlicePtr:
		.Op = OpSlice
		return OpStructFieldSlicePtr
	case OpMarshalJSON:
		return OpStructFieldMarshalJSON
	case OpMarshalJSONPtr:
		return OpStructFieldMarshalJSONPtr
	case OpMarshalText:
		return OpStructFieldMarshalText
	case OpMarshalTextPtr:
		return OpStructFieldMarshalTextPtr
	}
	return OpStructField
}

func newOpCode( *compileContext,  *runtime.Type,  OpType) *Opcode {
	return newOpCodeWithNext(, , , newEndOp(, ))
}

func opcodeOffset( int) uint32 {
	return uint32() * uintptrSize
}

func getCodeAddrByIdx( *Opcode,  uint32) *Opcode {
	 := uintptr(unsafe.Pointer()) + uintptr()*unsafe.Sizeof(Opcode{})
	return *(**Opcode)(unsafe.Pointer(&))
}

func copyOpcode( *Opcode) *Opcode {
	 := ToEndCode().DisplayIdx + 1
	 := make([]Opcode, )
	 := (*Opcode)((*runtime.SliceHeader)(unsafe.Pointer(&)).Data)
	 := 
	 := 
	for {
		* = Opcode{
			Op:         .Op,
			Key:        .Key,
			PtrNum:     .PtrNum,
			NumBitSize: .NumBitSize,
			Flags:      .Flags,
			Idx:        .Idx,
			Offset:     .Offset,
			Type:       .Type,
			FieldQuery: .FieldQuery,
			DisplayIdx: .DisplayIdx,
			DisplayKey: .DisplayKey,
			ElemIdx:    .ElemIdx,
			Length:     .Length,
			Size:       .Size,
			Indent:     .Indent,
			Jmp:        .Jmp,
		}
		if .End != nil {
			.End = getCodeAddrByIdx(, .End.DisplayIdx)
		}
		if .NextField != nil {
			.NextField = getCodeAddrByIdx(, .NextField.DisplayIdx)
		}
		if .Next != nil {
			.Next = getCodeAddrByIdx(, .Next.DisplayIdx)
		}
		if .IsEnd() {
			break
		}
		 = getCodeAddrByIdx(, .DisplayIdx+1)
		 = .IterNext()
	}
	return 
}

func setTotalLengthToInterfaceOp( *Opcode) {
	for  := ; !.IsEnd(); {
		if .Op == OpInterface || .Op == OpInterfacePtr {
			.Length = uint32(.TotalLength())
		}
		 = .IterNext()
	}
}

func ( *Opcode) *Opcode {
	 := 
	for !.IsEnd() {
		 = .IterNext()
	}
	return 
}

func copyToInterfaceOpcode( *Opcode) *Opcode {
	 := copyOpcode()
	 := 
	 = ToEndCode()
	.Idx += uintptrSize
	.ElemIdx = .Idx + uintptrSize
	.Length = .Idx + 2*uintptrSize
	.Op = OpInterfaceEnd
	return 
}

func newOpCodeWithNext( *compileContext,  *runtime.Type,  OpType,  *Opcode) *Opcode {
	return &Opcode{
		Op:         ,
		Idx:        opcodeOffset(.ptrIndex),
		Next:       ,
		Type:       ,
		DisplayIdx: .opcodeIndex,
		Indent:     .indent,
	}
}

func newEndOp( *compileContext,  *runtime.Type) *Opcode {
	return newOpCodeWithNext(, , OpEnd, nil)
}

func ( *Opcode) () int {
	var  int
	 := 
	for !.IsEnd() {
		 := int(.MaxIdx() / uintptrSize)
		if  <  {
			 = 
		}
		if .Op == OpRecursiveEnd {
			break
		}
		 = .IterNext()
	}
	 := int(.MaxIdx() / uintptrSize)
	if  <  {
		 = 
	}
	return  + 1
}

func ( *Opcode) ( *Opcode) string {
	var  uint32
	if .Op.CodeType() == CodeArrayHead {
		 = .Length
	} else {
		 = .Length / uintptrSize
	}
	return fmt.Sprintf(
		`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d])`,
		.DisplayIdx,
		strings.Repeat("-", int(.Indent)),
		.Op,
		.Idx/uintptrSize,
		.ElemIdx/uintptrSize,
		,
	)
}

func ( *Opcode) ( *Opcode) string {
	return fmt.Sprintf(
		`[%03d]%s%s ([idx:%d])`,
		.DisplayIdx,
		strings.Repeat("-", int(.Indent)),
		.Op,
		.Idx/uintptrSize,
	)
}

func ( *Opcode) ( *Opcode) string {
	return fmt.Sprintf(
		`[%03d]%s%s ([idx:%d])`,
		.DisplayIdx,
		strings.Repeat("-", int(.Indent)),
		.Op,
		.Idx/uintptrSize,
	)
}

func ( *Opcode) ( *Opcode) string {
	var  uint32
	if .Op.CodeType() == CodeArrayElem {
		 = .Length
	} else {
		 = .Length / uintptrSize
	}
	return fmt.Sprintf(
		`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d][size:%d])`,
		.DisplayIdx,
		strings.Repeat("-", int(.Indent)),
		.Op,
		.Idx/uintptrSize,
		.ElemIdx/uintptrSize,
		,
		.Size,
	)
}

func ( *Opcode) ( *Opcode) string {
	return fmt.Sprintf(
		`[%03d]%s%s ([idx:%d][key:%s][offset:%d])`,
		.DisplayIdx,
		strings.Repeat("-", int(.Indent)),
		.Op,
		.Idx/uintptrSize,
		.DisplayKey,
		.Offset,
	)
}

func ( *Opcode) ( *Opcode) string {
	return fmt.Sprintf(
		`[%03d]%s%s ([idx:%d])`,
		.DisplayIdx,
		strings.Repeat("-", int(.Indent)),
		.Op,
		.Idx/uintptrSize,
	)
}

func ( *Opcode) ( *Opcode) string {
	return fmt.Sprintf(
		`[%03d]%s%s ([idx:%d])`,
		.DisplayIdx,
		strings.Repeat("-", int(.Indent)),
		.Op,
		.Idx/uintptrSize,
	)
}

func ( *Opcode) () string {
	 := []string{}
	for  := ; !.IsEnd(); {
		switch .Op.CodeType() {
		case CodeSliceHead:
			 = append(, .dumpHead())
			 = .Next
		case CodeMapHead:
			 = append(, .dumpMapHead())
			 = .Next
		case CodeArrayElem, CodeSliceElem:
			 = append(, .dumpElem())
			 = .End
		case CodeMapKey:
			 = append(, .dumpKey())
			 = .End
		case CodeMapValue:
			 = append(, .dumpValue())
			 = .Next
		case CodeMapEnd:
			 = append(, .dumpMapEnd())
			 = .Next
		case CodeStructField:
			 = append(, .dumpField())
			 = .Next
		case CodeStructEnd:
			 = append(, .dumpField())
			 = .Next
		default:
			 = append(, fmt.Sprintf(
				"[%03d]%s%s ([idx:%d])",
				.DisplayIdx,
				strings.Repeat("-", int(.Indent)),
				.Op,
				.Idx/uintptrSize,
			))
			 = .Next
		}
	}
	return strings.Join(, "\n")
}

func ( *Opcode) () string {
	type  struct {
		,  *Opcode
		    string
		   int
	}
	var  []

	 := &bytes.Buffer{}
	fmt.Fprintf(, "digraph \"%p\" {\n", .Type)
	fmt.Fprintln(, "mclimit=1.5;\nrankdir=TD;\nordering=out;\nnode[shape=box];")
	for  := ; !.IsEnd(); {
		 := .Op.String()
		fmt.Fprintf(, "\"%p\" [label=%q];\n", , )
		if  := .Next;  != nil {
			 = append(, {
				:   ,
				:     ,
				:  "Next",
				: 10,
			})
		}
		if  := .NextField;  != nil {
			 = append(, {
				:   ,
				:     ,
				:  "NextField",
				: 2,
			})
		}
		if  := .End;  != nil {
			 = append(, {
				:   ,
				:     ,
				:  "End",
				: 1,
			})
		}
		if  := .Jmp;  != nil {
			 = append(, {
				:   ,
				:     .Code,
				:  "Jmp",
				: 1,
			})
		}

		switch .Op.CodeType() {
		case CodeSliceHead:
			 = .Next
		case CodeMapHead:
			 = .Next
		case CodeArrayElem, CodeSliceElem:
			 = .End
		case CodeMapKey:
			 = .End
		case CodeMapValue:
			 = .Next
		case CodeMapEnd:
			 = .Next
		case CodeStructField:
			 = .Next
		case CodeStructEnd:
			 = .Next
		default:
			 = .Next
		}
		if .IsEnd() {
			fmt.Fprintf(, "\"%p\" [label=%q];\n", , .Op.String())
		}
	}
	sort.Slice(, func(,  int) bool {
		return []..DisplayIdx < []..DisplayIdx
	})
	for ,  := range  {
		fmt.Fprintf(, "\"%p\" -> \"%p\" [label=%q][weight=%d];\n", ., ., ., .)
	}
	fmt.Fprint(, "}")
	return .String()
}

func newSliceHeaderCode( *compileContext,  *runtime.Type) *Opcode {
	 := opcodeOffset(.ptrIndex)
	.incPtrIndex()
	 := opcodeOffset(.ptrIndex)
	.incPtrIndex()
	 := opcodeOffset(.ptrIndex)
	return &Opcode{
		Op:         OpSlice,
		Type:       ,
		Idx:        ,
		DisplayIdx: .opcodeIndex,
		ElemIdx:    ,
		Length:     ,
		Indent:     .indent,
	}
}

func newSliceElemCode( *compileContext,  *runtime.Type,  *Opcode,  uintptr) *Opcode {
	return &Opcode{
		Op:         OpSliceElem,
		Type:       ,
		Idx:        .Idx,
		DisplayIdx: .opcodeIndex,
		ElemIdx:    .ElemIdx,
		Length:     .Length,
		Indent:     .indent,
		Size:       uint32(),
	}
}

func newArrayHeaderCode( *compileContext,  *runtime.Type,  int) *Opcode {
	 := opcodeOffset(.ptrIndex)
	.incPtrIndex()
	 := opcodeOffset(.ptrIndex)
	return &Opcode{
		Op:         OpArray,
		Type:       ,
		Idx:        ,
		DisplayIdx: .opcodeIndex,
		ElemIdx:    ,
		Indent:     .indent,
		Length:     uint32(),
	}
}

func newArrayElemCode( *compileContext,  *runtime.Type,  *Opcode,  int,  uintptr) *Opcode {
	return &Opcode{
		Op:         OpArrayElem,
		Type:       ,
		Idx:        .Idx,
		DisplayIdx: .opcodeIndex,
		ElemIdx:    .ElemIdx,
		Length:     uint32(),
		Indent:     .indent,
		Size:       uint32(),
	}
}

func newMapHeaderCode( *compileContext,  *runtime.Type) *Opcode {
	 := opcodeOffset(.ptrIndex)
	.incPtrIndex()
	return &Opcode{
		Op:         OpMap,
		Type:       ,
		Idx:        ,
		DisplayIdx: .opcodeIndex,
		Indent:     .indent,
	}
}

func newMapKeyCode( *compileContext,  *runtime.Type,  *Opcode) *Opcode {
	return &Opcode{
		Op:         OpMapKey,
		Type:       ,
		Idx:        .Idx,
		DisplayIdx: .opcodeIndex,
		Indent:     .indent,
	}
}

func newMapValueCode( *compileContext,  *runtime.Type,  *Opcode) *Opcode {
	return &Opcode{
		Op:         OpMapValue,
		Type:       ,
		Idx:        .Idx,
		DisplayIdx: .opcodeIndex,
		Indent:     .indent,
	}
}

func newMapEndCode( *compileContext,  *runtime.Type,  *Opcode) *Opcode {
	return &Opcode{
		Op:         OpMapEnd,
		Type:       ,
		Idx:        .Idx,
		DisplayIdx: .opcodeIndex,
		Indent:     .indent,
		Next:       newEndOp(, ),
	}
}

func newRecursiveCode( *compileContext,  *runtime.Type,  *CompiledCode) *Opcode {
	return &Opcode{
		Op:         OpRecursive,
		Type:       ,
		Idx:        opcodeOffset(.ptrIndex),
		Next:       newEndOp(, ),
		DisplayIdx: .opcodeIndex,
		Indent:     .indent,
		Jmp:        ,
	}
}