package eval

import (
	"go/ast"
	"go/token"
	"reflect"
	"testing"
)

//
//	Types, funcs & objects for testing
//
type SampleStruct struct {
	F uint16
}

func (s SampleStruct) M(x uint32) uint64 { return uint64(s.F) * uint64(x) }
func (s SampleStruct) M2(x uint32) (uint64, int64) {
	return uint64(s.F) * uint64(x), int64(s.F) - int64(x)
}

type SampleInt int8

func (s SampleInt) Mv(x int16) int32  { return -int32(s) * int32(x) }
func (s *SampleInt) Mp(x int16) int32 { return -int32(*s)*int32(x) + 1 }

// Check for method extracting.
// It is not trivial to fully check returned value, so do it separately from other tests
func TestSelector(t *testing.T) {
	type testSelectorElement struct {
		expr string
		vars Args
		arg  interface{}
		r    interface{}
	}

	tests := []testSelectorElement{
		{"a.M", ArgsFromInterfaces(ArgsI{"a": SampleStruct{2}}), uint32(3), uint64(6)},
		{"b.Mv", ArgsFromInterfaces(ArgsI{"b": SampleInt(4)}), int16(5), int32(-20)},
		{"b.Mv", ArgsFromInterfaces(ArgsI{"b": new(SampleInt)}), int16(6), int32(0)},
		{"b.Mp", ArgsFromInterfaces(ArgsI{"b": new(SampleInt)}), int16(7), int32(1)},
	}

	for _, v := range tests {

		expr, err := ParseString(v.expr, "")
		if err != nil {
			t.Errorf("%v: %v", v.expr, err)
			continue
		}
		selectorAst, ok := expr.e.(*ast.SelectorExpr)
		if !ok {
			t.Errorf("%v: not a astSelectorExpr", v.expr)
			continue
		}

		r, posErr := expr.astSelectorExpr(selectorAst, v.vars)
		err = posErr.error(token.NewFileSet())
		if err != nil {
			t.Errorf("expect not error, got %v", err.Error())
			continue
		}

		if r.Kind() != Datas || r.Data().Kind() != Regular || r.Data().Regular().Kind() != reflect.Func {
			t.Errorf("expect function, got %v", r.String())
			continue
		}
		rV := r.Data().Regular()

		rs := rV.Call([]reflect.Value{reflect.ValueOf(v.arg)})
		if l := len(rs); l != 1 {
			t.Errorf("expect 1 result, got %v", l)
			continue
		}

		if rI := rs[0].Interface(); rI != v.r {
			t.Errorf("expect %v, got %v", v.r, rI)
		}
	}
}

// Check for getting address (&).
// It is not trivial to fully check returned value, so do it separately from other tests
func TestUnary(t *testing.T) {
	tmp := SampleStruct{5}
	tmp2 := []int8{6}
	tests := []testExprElement{
		{"&a.F", ArgsFromInterfaces(ArgsI{"a": &tmp}), MakeDataRegularInterface(&tmp.F), false},
		{"&a[0]", ArgsFromInterfaces(ArgsI{"a": tmp2}), MakeDataRegularInterface(&tmp2[0]), false},
	}

	for _, v := range tests {
		expr, err := ParseString(v.expr, "")
		if err != nil {
			t.Errorf("%v: %v", v.expr, err)
			continue
		}
		unaryAst, ok := expr.e.(*ast.UnaryExpr)
		if !ok {
			t.Errorf("%v: not a astUnaryExpr", v.expr)
			continue
		}

		r, posErr := expr.astUnaryExpr(unaryAst, v.vars)
		err = posErr.error(token.NewFileSet())
		if !v.Validate(r, err) {
			t.Errorf(v.ErrorMsg(r, err))
		}
	}
}

// Check for cases which can not be generated by parsing expression source.
func TestAstSelectorExpr(t *testing.T) {
	tmp := ast.BasicLit{Kind: token.INT, Value: "1234"}
	e := ast.SelectorExpr{X: &tmp, Sel: nil}
	if r, err := ((*Expression)(nil)).astSelectorExpr(&e, nil); r != nil || err == nil {
		t.Errorf("expect %v %v, got %v %v", nil, true, r, err)
	}
}

// Check for cases which can not be generated by parsing expression source.
func TestAstBasicLit(t *testing.T) {
	e := ast.BasicLit{Kind: token.INT, Value: "str"}
	if r, err := ((*Expression)(nil)).astBasicLit(&e, nil); r != nil || err == nil {
		t.Errorf("expect %v %v, got %v %v", nil, true, r, err)
	}
}

// Check for cases which can not be generated by parsing expression source.
func TestAstExpr(t *testing.T) {
	if r, err := ((*Expression)(nil)).astExpr(nil, nil); r != nil || err == nil {
		t.Errorf("expect %v %v, got %v %v", nil, true, r, err)
	}
	if r, err := ((*Expression)(nil)).astExpr(&ast.BadExpr{}, nil); r != nil || err == nil {
		t.Errorf("expect %v %v, got %v %v", nil, true, r, err)
	}
}

// Check for cases which can not be generated by parsing expression source.
func TestAstStructType(t *testing.T) {
	//
	//	1
	//
	e := &ast.StructType{}
	if r, err := ((*Expression)(nil)).astStructType(e, nil); r != nil || err == nil {
		t.Errorf("expect %v %v, got %v %v", nil, true, r, err)
	}

	//
	//	2
	//
	expr, err := ParseString("struct{X int `potobuf:1`}", "")
	if err != nil {
		t.Fatal("expect no error")
	}
	e = expr.e.(*ast.StructType)
	e.Fields.List[0].Tag.Value = "1234"
	e.Fields.List[0].Tag.Kind = token.INT
	if r, err := expr.astStructType(e, nil); r != nil || err == nil {
		t.Errorf("expect %v %v, got %v %v", nil, true, r, err)
	}

	//
	//	3
	//
	expr, err = ParseString("struct{int `potobuf:1`}", "")
	if err != nil {
		t.Fatal("expect no error")
	}
	e = expr.e.(*ast.StructType)
	e.Fields.List[0].Tag.Value = "1234"
	e.Fields.List[0].Tag.Kind = token.INT
	if r, err := expr.astStructType(e, nil); r != nil || err == nil {
		t.Errorf("expect %v %v, got %v %v", nil, true, r, err)
	}
}

// Check for cases which can not be generated by parsing expression source.
func TestAstInterfaceType(t *testing.T) {
	e := &ast.InterfaceType{}
	if r, err := ((*Expression)(nil)).astInterfaceType(e, nil); r != nil || err == nil {
		t.Errorf("expect %v %v, got %v %v", nil, true, r, err)
	}
}

func TestAstSelectorExpr2(t *testing.T) {
	expr, err := ParseString(`myStruct1{i:1}`, "github.com/apaxa-go/eval")
	testR := MakeDataRegularInterface(myStruct1{i: 1})
	if err != nil {
		t.Fatal("expect no error")
	}
	r, err := expr.EvalRaw(Args{"myStruct1": MakeType(reflect.TypeOf(myStruct1{}))})
	if err != nil || !isValuesEqual(r, testR) {
		t.Fatalf("expect %v %v, got %v %v", testR, nil, r, err)
	}

	//
	//
	//
	expr, err = ParseString(`myStruct1{1,"str"}`, "github.com/apaxa-go/eval")
	testR = MakeDataRegularInterface(myStruct1{1, "str"})
	if err != nil {
		t.Fatal("expect no error")
	}
	r, err = expr.EvalRaw(Args{"myStruct1": MakeType(reflect.TypeOf(myStruct1{}))})
	if err != nil || !isValuesEqual(r, testR) {
		t.Fatalf("expect %v %v, got %v %v", testR, nil, r, err)
	}
}
