package goja

import (
	
	
	
	
	
	
	

	
)

type valueBigInt big.Int

func ( *valueBigInt) () int64 {
	.ToNumber()
	return 0
}

func ( *valueBigInt) () String {
	return asciiString((*big.Int)().String())
}

func ( *valueBigInt) () unistring.String {
	return unistring.String(.String())
}

func ( *valueBigInt) () Value {
	return 
}

func ( *valueBigInt) () string {
	return (*big.Int)().String()
}

func ( *valueBigInt) () float64 {
	.ToNumber()
	return 0
}

func ( *valueBigInt) () Value {
	panic(typeError("Cannot convert a BigInt value to a number"))
}

func ( *valueBigInt) () bool {
	return (*big.Int)().Sign() != 0
}

func ( *valueBigInt) ( *Runtime) *Object {
	return .newPrimitiveObject(, .getBigIntPrototype(), classObject)
}

func ( *valueBigInt) ( Value) bool {
	if ,  := .(*valueBigInt);  {
		return (*big.Int)().Cmp((*big.Int)()) == 0
	}
	return false
}

func ( *valueBigInt) ( Value) bool {
	switch o := .(type) {
	case *valueBigInt:
		return (*big.Int)().Cmp((*big.Int)()) == 0
	case valueInt:
		return (*big.Int)().Cmp(big.NewInt(int64())) == 0
	case valueFloat:
		if IsInfinity() || math.IsNaN(float64()) {
			return false
		}
		if  := big.NewFloat(float64()); .IsInt() {
			,  := .Int(nil)
			return (*big.Int)().Cmp() == 0
		}
		return false
	case String:
		,  := stringToBigInt(.toTrimmedUTF8())
		if  != nil {
			return false
		}
		return .Cmp((*big.Int)()) == 0
	case valueBool:
		return (*big.Int)().Int64() == .ToInteger()
	case *Object:
		return .(.toPrimitiveNumber())
	}
	return false
}

func ( *valueBigInt) ( Value) bool {
	,  := .(*valueBigInt)
	if  {
		return (*big.Int)().Cmp((*big.Int)()) == 0
	}
	return false
}

func ( *valueBigInt) () interface{} {
	return new(big.Int).Set((*big.Int)())
}

func ( *valueBigInt) () reflect.Type {
	return typeBigInt
}

func ( *valueBigInt) ( *Runtime) *Object {
	return .getBigIntPrototype()
}

func ( *valueBigInt) ( *maphash.Hash) uint64 {
	var  byte
	if (*big.Int)().Sign() < 0 {
		 = 0x01
	} else {
		 = 0x00
	}
	_ = .WriteByte()
	_, _ = .Write((*big.Int)().Bytes())
	 := .Sum64()
	.Reset()
	return 
}

func toBigInt( Value) *valueBigInt {
	// Undefined	Throw a TypeError exception.
	// Null			Throw a TypeError exception.
	// Boolean		Return 1n if prim is true and 0n if prim is false.
	// BigInt		Return prim.
	// Number		Throw a TypeError exception.
	// String		1. Let n be StringToBigInt(prim).
	//				2. If n is undefined, throw a SyntaxError exception.
	//				3. Return n.
	// Symbol		Throw a TypeError exception.
	switch prim := .(type) {
	case *valueBigInt:
		return 
	case String:
		,  := stringToBigInt(.toTrimmedUTF8())
		if  != nil {
			panic(syntaxError(fmt.Sprintf("Cannot convert %s to a BigInt", )))
		}
		return (*valueBigInt)()
	case valueBool:
		return (*valueBigInt)(big.NewInt(.ToInteger()))
	case *Symbol:
		panic(typeError("Cannot convert Symbol to a BigInt"))
	case *Object:
		return (.toPrimitiveNumber())
	default:
		panic(typeError(fmt.Sprintf("Cannot convert %s to a BigInt", )))
	}
}

func numberToBigInt( Value) *valueBigInt {
	switch v := toNumeric().(type) {
	case *valueBigInt:
		return 
	case valueInt:
		return (*valueBigInt)(big.NewInt(.ToInteger()))
	case valueFloat:
		if IsInfinity() || math.IsNaN(float64()) {
			panic(rangeError(fmt.Sprintf("Cannot convert %s to a BigInt", )))
		}
		if  := big.NewFloat(float64()); .IsInt() {
			,  := .Int(nil)
			return (*valueBigInt)()
		}
		panic(rangeError(fmt.Sprintf("Cannot convert %s to a BigInt", )))
	case *Object:
		 := .toPrimitiveNumber()
		switch .(type) {
		case valueInt, valueFloat:
			return ()
		default:
			return toBigInt()
		}
	default:
		panic(newTypeError("Cannot convert %s to a BigInt", ))
	}
}

func stringToBigInt( string) (*big.Int, error) {
	var  big.Int
	,  := stringToInt()
	if  != nil {
		switch {
		case isRangeErr():
			.SetString(, 0)
		case  == strconv.ErrSyntax:
		default:
			return nil, strconv.ErrSyntax
		}
	} else {
		.SetInt64()
	}
	return &, nil
}

func ( *Runtime) ( Value) Value {
	switch t := .(type) {
	case *valueBigInt:
		return 
	case *Object:
		switch t := .self.(type) {
		case *primitiveValueObject:
			return .(.pValue)
		case *objectGoReflect:
			if .exportType() == typeBigInt && .valueOf != nil {
				return .valueOf()
			}
		}
	}
	panic(.NewTypeError("requires that 'this' be a BigInt"))
}

func ( *Runtime) ( FunctionCall) Value {
	return .thisBigIntValue(.This)
}

func ( *Runtime) ( FunctionCall) Value {
	 := (*big.Int)(.thisBigIntValue(.This).(*valueBigInt))
	 := .Argument(0)
	var  int

	if  == _undefined {
		 = 10
	} else {
		 = int(.ToInteger())
		if  < 2 ||  > 36 {
			panic(.newError(.getRangeError(), "radix must be an integer between 2 and 36"))
		}
	}

	return asciiString(.Text())
}

func ( *Runtime) ( FunctionCall) Value {
	if len(.Arguments) < 2 {
		panic(.NewTypeError("Cannot convert undefined to a BigInt"))
	}
	 := .toIndex(.Argument(0).ToNumber())
	if  < 0 {
		panic(.NewTypeError("Invalid value: not (convertible to) a safe integer"))
	}
	 := toBigInt(.Argument(1))

	 := new(big.Int).Lsh(big.NewInt(1), uint())
	 := new(big.Int).Mod((*big.Int)(), )
	if  > 0 && .Cmp(new(big.Int).Lsh(big.NewInt(1), uint(-1))) >= 0 {
		return (*valueBigInt)(.Sub(, ))
	} else {
		return (*valueBigInt)()
	}
}

func ( *Runtime) ( FunctionCall) Value {
	if len(.Arguments) < 2 {
		panic(.NewTypeError("Cannot convert undefined to a BigInt"))
	}
	 := .toIndex(.Argument(0).ToNumber())
	if  < 0 {
		panic(.NewTypeError("Invalid value: not (convertible to) a safe integer"))
	}
	 := (*big.Int)(toBigInt(.Argument(1)))
	 := new(big.Int).Mod(, new(big.Int).Lsh(big.NewInt(1), uint()))
	return (*valueBigInt)()
}

var bigintTemplate *objectTemplate
var bigintTemplateOnce sync.Once

func getBigIntTemplate() *objectTemplate {
	bigintTemplateOnce.Do(func() {
		bigintTemplate = createBigIntTemplate()
	})
	return bigintTemplate
}

func createBigIntTemplate() *objectTemplate {
	 := newObjectTemplate()
	.protoFactory = func( *Runtime) *Object {
		return .getFunctionPrototype()
	}

	.putStr("name", func( *Runtime) Value { return valueProp(asciiString("BigInt"), false, false, true) })
	.putStr("length", func( *Runtime) Value { return valueProp(intToValue(1), false, false, true) })
	.putStr("prototype", func( *Runtime) Value { return valueProp(.getBigIntPrototype(), false, false, false) })

	.putStr("asIntN", func( *Runtime) Value { return .methodProp(.bigint_asIntN, "asIntN", 2) })
	.putStr("asUintN", func( *Runtime) Value { return .methodProp(.bigint_asUintN, "asUintN", 2) })

	return 
}

func ( *Runtime) ( FunctionCall) Value {
	if len(.Arguments) > 0 {
		switch v := .Argument(0).(type) {
		case *valueBigInt, valueInt, valueFloat, *Object:
			return numberToBigInt()
		default:
			return toBigInt()
		}
	}
	return (*valueBigInt)(big.NewInt(0))
}

func ( *Runtime) ( []Value,  *Object) *Object {
	if  != nil {
		panic(.NewTypeError("BigInt is not a constructor"))
	}
	var  Value
	if len() > 0 {
		 = numberToBigInt([0])
	} else {
		 = (*valueBigInt)(big.NewInt(0))
	}
	return .newPrimitiveObject(, , classObject)
}

func ( *Runtime) () *Object {
	 := .global.BigInt
	if  == nil {
		 = &Object{runtime: }
		.global.BigInt = 
		.newTemplatedFuncObject(getBigIntTemplate(), , .builtin_BigInt,
			.wrapNativeConstruct(.builtin_newBigInt, , .getBigIntPrototype()))
	}
	return 
}

func createBigIntProtoTemplate() *objectTemplate {
	 := newObjectTemplate()
	.protoFactory = func( *Runtime) *Object {
		return .global.ObjectPrototype
	}

	.putStr("length", func( *Runtime) Value { return valueProp(intToValue(0), false, false, true) })
	.putStr("name", func( *Runtime) Value { return valueProp(asciiString("BigInt"), false, false, true) })
	.putStr("constructor", func( *Runtime) Value { return valueProp(.getBigInt(), true, false, true) })

	.putStr("toLocaleString", func( *Runtime) Value { return .methodProp(.bigintproto_toString, "toLocaleString", 0) })
	.putStr("toString", func( *Runtime) Value { return .methodProp(.bigintproto_toString, "toString", 0) })
	.putStr("valueOf", func( *Runtime) Value { return .methodProp(.bigintproto_valueOf, "valueOf", 0) })
	.putSym(SymToStringTag, func( *Runtime) Value { return valueProp(asciiString("BigInt"), false, false, true) })

	return 
}

var bigintProtoTemplate *objectTemplate
var bigintProtoTemplateOnce sync.Once

func getBigIntProtoTemplate() *objectTemplate {
	bigintProtoTemplateOnce.Do(func() {
		bigintProtoTemplate = createBigIntProtoTemplate()
	})
	return bigintProtoTemplate
}

func ( *Runtime) () *Object {
	 := .global.BigIntPrototype
	if  == nil {
		 = &Object{runtime: }
		.global.BigIntPrototype = 
		 := .newTemplatedObject(getBigIntProtoTemplate(), )
		.class = classObject
	}
	return 
}