package goja

import (
	
	
	
	

	
	
)

// JsonEncodable allows custom JSON encoding by JSON.stringify()
// Note that if the returned value itself also implements JsonEncodable, it won't have any effect.
type JsonEncodable interface {
	JsonEncodable() interface{}
}

// FieldNameMapper provides custom mapping between Go and JavaScript property names.
type FieldNameMapper interface {
	// FieldName returns a JavaScript name for the given struct field in the given type.
	// If this method returns "" the field becomes hidden.
	FieldName(t reflect.Type, f reflect.StructField) string

	// MethodName returns a JavaScript name for the given method in the given type.
	// If this method returns "" the method becomes hidden.
	MethodName(t reflect.Type, m reflect.Method) string
}

type tagFieldNameMapper struct {
	tagName      string
	uncapMethods bool
}

func ( tagFieldNameMapper) ( reflect.Type,  reflect.StructField) string {
	 := .Tag.Get(.tagName)
	if  := strings.IndexByte(, ',');  != -1 {
		 = [:]
	}
	if parser.IsIdentifier() {
		return 
	}
	return ""
}

func uncapitalize( string) string {
	return strings.ToLower([0:1]) + [1:]
}

func ( tagFieldNameMapper) ( reflect.Type,  reflect.Method) string {
	if .uncapMethods {
		return uncapitalize(.Name)
	}
	return .Name
}

type uncapFieldNameMapper struct {
}

func ( uncapFieldNameMapper) ( reflect.Type,  reflect.StructField) string {
	return uncapitalize(.Name)
}

func ( uncapFieldNameMapper) ( reflect.Type,  reflect.Method) string {
	return uncapitalize(.Name)
}

type reflectFieldInfo struct {
	Index     []int
	Anonymous bool
}

type reflectFieldsInfo struct {
	Fields map[string]reflectFieldInfo
	Names  []string
}

type reflectMethodsInfo struct {
	Methods map[string]int
	Names   []string
}

type reflectValueWrapper interface {
	esValue() Value
	reflectValue() reflect.Value
	setReflectValue(reflect.Value)
}

func isContainer( reflect.Kind) bool {
	switch  {
	case reflect.Struct, reflect.Slice, reflect.Array:
		return true
	}
	return false
}

func copyReflectValueWrapper( reflectValueWrapper) {
	 := .reflectValue()
	 := reflect.New(.Type()).Elem()
	.Set()
	.setReflectValue()
}

type objectGoReflect struct {
	baseObject
	origValue, fieldsValue reflect.Value

	fieldsInfo  *reflectFieldsInfo
	methodsInfo *reflectMethodsInfo

	methodsValue reflect.Value

	valueCache map[string]reflectValueWrapper

	toString, valueOf func() Value

	toJson func() interface{}
}

func ( *objectGoReflect) () {
	.baseObject.init()
	switch .fieldsValue.Kind() {
	case reflect.Bool:
		.class = classBoolean
		.prototype = .val.runtime.getBooleanPrototype()
		.toString = ._toStringBool
		.valueOf = ._valueOfBool
	case reflect.String:
		.class = classString
		.prototype = .val.runtime.getStringPrototype()
		.toString = ._toStringString
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		.class = classNumber
		.prototype = .val.runtime.getNumberPrototype()
		.valueOf = ._valueOfInt
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		.class = classNumber
		.prototype = .val.runtime.getNumberPrototype()
		.valueOf = ._valueOfUint
	case reflect.Float32, reflect.Float64:
		.class = classNumber
		.prototype = .val.runtime.getNumberPrototype()
		.valueOf = ._valueOfFloat
	default:
		.class = classObject
		.prototype = .val.runtime.global.ObjectPrototype
	}

	if .fieldsValue.Kind() == reflect.Struct {
		.fieldsInfo = .val.runtime.fieldsInfo(.fieldsValue.Type())
	}

	var  reflect.Type
	// Always use pointer type for non-interface values to be able to access both methods defined on
	// the literal type and on the pointer.
	if .fieldsValue.Kind() != reflect.Interface {
		 = reflect.PtrTo(.fieldsValue.Type())
	} else {
		 = .fieldsValue.Type()
	}

	.methodsInfo = .val.runtime.methodsInfo()

	// Container values and values that have at least one method defined on the pointer type
	// need to be addressable.
	if !.origValue.CanAddr() && (isContainer(.origValue.Kind()) || len(.methodsInfo.Names) > 0) {
		 := reflect.New(.origValue.Type()).Elem()
		.Set(.origValue)
		.origValue = 
		if .Kind() != reflect.Ptr {
			.fieldsValue = 
		}
	}

	.extensible = true

	switch .origValue.Interface().(type) {
	case fmt.Stringer:
		.toString = ._toStringStringer
	case error:
		.toString = ._toStringError
	}

	if len(.methodsInfo.Names) > 0 && .fieldsValue.Kind() != reflect.Interface {
		.methodsValue = .fieldsValue.Addr()
	} else {
		.methodsValue = .fieldsValue
	}

	if ,  := .origValue.Interface().(JsonEncodable);  {
		.toJson = .JsonEncodable
	}
}

func ( *objectGoReflect) ( unistring.String,  Value) Value {
	if  := ._get(.String());  != nil {
		return 
	}
	return .baseObject.getStr(, )
}

func ( *objectGoReflect) ( string) reflect.Value {
	if .fieldsInfo != nil {
		if ,  := .fieldsInfo.Fields[];  {
			return .fieldsValue.FieldByIndex(.Index)
		}
	}

	return reflect.Value{}
}

func ( *objectGoReflect) ( string) reflect.Value {
	if .methodsInfo != nil {
		if ,  := .methodsInfo.Methods[];  {
			return .methodsValue.Method()
		}
	}

	return reflect.Value{}
}

func ( *objectGoReflect) ( reflect.Value) (Value, reflectValueWrapper) {
	if isContainer(.Kind()) {
		 := .val.runtime.toValue(.Interface(), )
		if ,  := .(*Object);  {
			if ,  := .self.(reflectValueWrapper);  {
				return , 
			}
		}
		return , nil
	}

	if .Kind() == reflect.Interface {
		 = .Elem()
	}

	if .Kind() == reflect.Invalid {
		return _null, nil
	}

	return .val.runtime.toValue(.Interface(), ), nil
}

func ( *objectGoReflect) ( string) Value {
	if  := .valueCache[];  != nil {
		return .esValue()
	}
	if  := ._getField(); .IsValid() {
		,  := .elemToValue()
		if  != nil {
			if .valueCache == nil {
				.valueCache = make(map[string]reflectValueWrapper)
			}
			.valueCache[] = 
		}
		return 
	}
	return nil
}

func ( *objectGoReflect) ( string) Value {
	if .fieldsValue.Kind() == reflect.Struct {
		if  := ._getFieldValue();  != nil {
			return 
		}
	}

	if  := ._getMethod(); .IsValid() {
		return .val.runtime.toValue(.Interface(), )
	}

	return nil
}

func ( *objectGoReflect) ( unistring.String) Value {
	 := .String()
	if .fieldsValue.Kind() == reflect.Struct {
		if  := ._getFieldValue();  != nil {
			return &valueProperty{
				value:      ,
				writable:   true,
				enumerable: true,
			}
		}
	}

	if  := ._getMethod(); .IsValid() {
		return &valueProperty{
			value:      .val.runtime.toValue(.Interface(), ),
			enumerable: true,
		}
	}

	return .baseObject.getOwnPropStr()
}

func ( *objectGoReflect) ( unistring.String,  Value,  bool) bool {
	,  := ._put(.String(), , )
	if ! {
		if ,  := ._setForeignStr(, nil, , .val, ); ! {
			.val.runtime.typeErrorResult(, "Cannot assign to property %s of a host object", )
			return false
		} else {
			return 
		}
	}
	return 
}

func ( *objectGoReflect) ( unistring.String, ,  Value,  bool) (bool, bool) {
	return ._setForeignStr(, trueValIfPresent(._has(.String())), , , )
}

func ( *objectGoReflect) ( valueInt, ,  Value,  bool) (bool, bool) {
	return ._setForeignIdx(, nil, , , )
}

func ( *objectGoReflect) ( string,  Value,  bool) (,  bool) {
	if .fieldsValue.Kind() == reflect.Struct {
		if  := ._getField(); .IsValid() {
			 := .valueCache[]
			if  != nil {
				copyReflectValueWrapper()
			}

			 := .val.runtime.toReflectValue(, , &objectExportCtx{})
			if  != nil {
				if  != nil {
					.setReflectValue()
				}
				.val.runtime.typeErrorResult(, "Go struct conversion error: %v", )
				return true, false
			}
			if  != nil {
				delete(.valueCache, )
			}
			return true, true
		}
	}
	return false, false
}

func ( *objectGoReflect) ( unistring.String,  Value, , ,  bool) Value {
	if ,  := ._put(.String(), , false);  {
		return 
	}
	return .baseObject._putProp(, , , , )
}

func ( *Runtime) ( unistring.String,  PropertyDescriptor,  bool) bool {
	if .Getter != nil || .Setter != nil {
		.typeErrorResult(, "Host objects do not support accessor properties")
		return false
	}
	if .Writable == FLAG_FALSE {
		.typeErrorResult(, "Host object field %s cannot be made read-only", )
		return false
	}
	if .Configurable == FLAG_TRUE {
		.typeErrorResult(, "Host object field %s cannot be made configurable", )
		return false
	}
	return true
}

func ( *objectGoReflect) ( unistring.String,  PropertyDescriptor,  bool) bool {
	if .val.runtime.checkHostObjectPropertyDescr(, , ) {
		 := .String()
		if ,  := ._put(, .Value, ); ! {
			.val.runtime.typeErrorResult(, "Cannot define property '%s' on a host object", )
			return false
		} else {
			return 
		}
	}
	return false
}

func ( *objectGoReflect) ( string) bool {
	if .fieldsValue.Kind() == reflect.Struct {
		if  := ._getField(); .IsValid() {
			return true
		}
	}
	if  := ._getMethod(); .IsValid() {
		return true
	}
	return false
}

func ( *objectGoReflect) ( unistring.String) bool {
	return ._has(.String()) || .baseObject.hasOwnPropertyStr()
}

func ( *objectGoReflect) () Value {
	return intToValue(.fieldsValue.Int())
}

func ( *objectGoReflect) () Value {
	return intToValue(int64(.fieldsValue.Uint()))
}

func ( *objectGoReflect) () Value {
	if .fieldsValue.Bool() {
		return valueTrue
	} else {
		return valueFalse
	}
}

func ( *objectGoReflect) () Value {
	return floatToValue(.fieldsValue.Float())
}

func ( *objectGoReflect) () Value {
	return newStringValue(.origValue.Interface().(fmt.Stringer).String())
}

func ( *objectGoReflect) () Value {
	return newStringValue(.fieldsValue.String())
}

func ( *objectGoReflect) () Value {
	if .fieldsValue.Bool() {
		return stringTrue
	} else {
		return stringFalse
	}
}

func ( *objectGoReflect) () Value {
	return newStringValue(.origValue.Interface().(error).Error())
}

func ( *objectGoReflect) ( unistring.String,  bool) bool {
	 := .String()
	if ._has() {
		.val.runtime.typeErrorResult(, "Cannot delete property %s from a Go type", )
		return false
	}
	return .baseObject.deleteStr(, )
}

type goreflectPropIter struct {
	o   *objectGoReflect
	idx int
}

func ( *goreflectPropIter) () (propIterItem, iterNextFunc) {
	 := .o.fieldsInfo.Names
	if .idx < len() {
		 := [.idx]
		.idx++
		return propIterItem{name: newStringValue(), enumerable: _ENUM_TRUE}, .
	}

	.idx = 0
	return .nextMethod()
}

func ( *goreflectPropIter) () (propIterItem, iterNextFunc) {
	 := .o.methodsInfo.Names
	if .idx < len() {
		 := [.idx]
		.idx++
		return propIterItem{name: newStringValue(), enumerable: _ENUM_TRUE}, .
	}

	return propIterItem{}, nil
}

func ( *objectGoReflect) () iterNextFunc {
	 := &goreflectPropIter{
		o: ,
	}
	if .fieldsInfo != nil {
		return .nextField
	}

	return .nextMethod
}

func ( *objectGoReflect) ( bool,  []Value) []Value {
	// all own keys are enumerable
	if .fieldsInfo != nil {
		for ,  := range .fieldsInfo.Names {
			 = append(, newStringValue())
		}
	}

	for ,  := range .methodsInfo.Names {
		 = append(, newStringValue())
	}

	return 
}

func ( *objectGoReflect) (*objectExportCtx) interface{} {
	return .origValue.Interface()
}

func ( *objectGoReflect) () reflect.Type {
	return .origValue.Type()
}

func ( *objectGoReflect) ( objectImpl) bool {
	if ,  := .(*objectGoReflect);  {
		,  := .fieldsValue.Kind(), .fieldsValue.Kind()
		if  ==  {
			if isContainer() {
				return .fieldsValue == .fieldsValue
			}
			return .fieldsValue.Interface() == .fieldsValue.Interface()
		}
	}
	return false
}

func ( *objectGoReflect) () reflect.Value {
	return .fieldsValue
}

func ( *objectGoReflect) ( reflect.Value) {
	.fieldsValue = 
	.origValue = 
	.methodsValue = .Addr()
}

func ( *objectGoReflect) () Value {
	return .val
}

func ( *Runtime) ( reflect.Type,  []int,  *reflectFieldsInfo) {
	 := .NumField()
	for  := 0;  < ; ++ {
		 := .Field()
		 := .Name
		 := ast.IsExported()

		if ! && !.Anonymous {
			continue
		}

		if .fieldNameMapper != nil {
			 = .fieldNameMapper.FieldName(, )
		}

		if  != "" &&  {
			if ,  := .Fields[]; ! {
				.Names = append(.Names, )
			} else {
				if len(.Index) <= len() {
					continue
				}
			}
		}

		if  != "" || .Anonymous {
			 := make([]int, len()+1)
			copy(, )
			[len()-1] = 

			if  != "" &&  {
				.Fields[] = reflectFieldInfo{
					Index:     ,
					Anonymous: .Anonymous,
				}
			}
			if .Anonymous {
				 := .Type
				for .Kind() == reflect.Ptr {
					 = .Elem()
				}
				if .Kind() == reflect.Struct {
					.(, , )
				}
			}
		}
	}
}

var emptyMethodsInfo = reflectMethodsInfo{}

func ( *Runtime) ( reflect.Type) ( *reflectMethodsInfo) {
	 := .NumMethod()
	if  == 0 {
		return &emptyMethodsInfo
	}
	 = new(reflectMethodsInfo)
	.Methods = make(map[string]int, )
	.Names = make([]string, 0, )
	for  := 0;  < ; ++ {
		 := .Method()
		 := .Name
		if !ast.IsExported() {
			continue
		}
		if .fieldNameMapper != nil {
			 = .fieldNameMapper.MethodName(, )
			if  == "" {
				continue
			}
		}

		if ,  := .Methods[]; ! {
			.Names = append(.Names, )
		}

		.Methods[] = 
	}
	return
}

func ( *Runtime) ( reflect.Type) ( *reflectFieldsInfo) {
	 = new(reflectFieldsInfo)
	 := .NumField()
	.Fields = make(map[string]reflectFieldInfo, )
	.Names = make([]string, 0, )
	.buildFieldInfo(, nil, )
	return
}

func ( *Runtime) ( reflect.Type) ( *reflectFieldsInfo) {
	var  bool
	if ,  = .fieldsInfoCache[]; ! {
		 = .buildFieldsInfo()
		if .fieldsInfoCache == nil {
			.fieldsInfoCache = make(map[reflect.Type]*reflectFieldsInfo)
		}
		.fieldsInfoCache[] = 
	}

	return
}

func ( *Runtime) ( reflect.Type) ( *reflectMethodsInfo) {
	var  bool
	if ,  = .methodsInfoCache[]; ! {
		 = .buildMethodsInfo()
		if .methodsInfoCache == nil {
			.methodsInfoCache = make(map[reflect.Type]*reflectMethodsInfo)
		}
		.methodsInfoCache[] = 
	}

	return
}

// SetFieldNameMapper sets a custom field name mapper for Go types. It can be called at any time, however
// the mapping for any given value is fixed at the point of creation.
// Setting this to nil restores the default behaviour which is all exported fields and methods are mapped to their
// original unchanged names.
func ( *Runtime) ( FieldNameMapper) {
	.fieldNameMapper = 
	.fieldsInfoCache = nil
	.methodsInfoCache = nil
}

// TagFieldNameMapper returns a FieldNameMapper that uses the given tagName for struct fields and optionally
// uncapitalises (making the first letter lower case) method names.
// The common tag value syntax is supported (name[,options]), however options are ignored.
// Setting name to anything other than a valid ECMAScript identifier makes the field hidden.
func ( string,  bool) FieldNameMapper {
	return tagFieldNameMapper{
		tagName:      ,
		uncapMethods: ,
	}
}

// UncapFieldNameMapper returns a FieldNameMapper that uncapitalises struct field and method names
// making the first letter lower case.
func () FieldNameMapper {
	return uncapFieldNameMapper{}
}