// Copyright 2014 The ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ql

import (
	"fmt"
	"math/rand"
	"reflect"
	"strconv"
	"strings"
	"time"
)

//TODO agg bigint, bigrat, time, duration

var builtin = map[string]struct {
	f           func([]interface{}, map[interface{}]interface{}) (interface{}, error)
	minArgs     int
	maxArgs     int
	isStatic    bool
	isAggregate bool
}{
	"__testBlob":   {builtinTestBlob, 1, 1, true, false},
	"__testString": {builtinTestString, 1, 1, true, false},
	"avg":          {builtinAvg, 1, 1, false, true},
	"complex":      {builtinComplex, 2, 2, true, false},
	"contains":     {builtinContains, 2, 2, true, false},
	"count":        {builtinCount, 0, 1, false, true},
	"date":         {builtinDate, 8, 8, true, false},
	"day":          {builtinDay, 1, 1, true, false},
	"formatTime":   {builtinFormatTime, 2, 2, true, false},
	"formatFloat":  {builtinFormatFloat, 1, 4, true, false},
	"formatInt":    {builtinFormatInt, 1, 2, true, false},
	"hasPrefix":    {builtinHasPrefix, 2, 2, true, false},
	"hasSuffix":    {builtinHasSuffix, 2, 2, true, false},
	"hour":         {builtinHour, 1, 1, true, false},
	"hours":        {builtinHours, 1, 1, true, false},
	"id":           {builtinID, 0, 1, false, false},
	"imag":         {builtinImag, 1, 1, true, false},
	"len":          {builtinLen, 1, 1, true, false},
	"max":          {builtinMax, 1, 1, false, true},
	"min":          {builtinMin, 1, 1, false, true},
	"minute":       {builtinMinute, 1, 1, true, false},
	"minutes":      {builtinMinutes, 1, 1, true, false},
	"month":        {builtinMonth, 1, 1, true, false},
	"nanosecond":   {builtinNanosecond, 1, 1, true, false},
	"nanoseconds":  {builtinNanoseconds, 1, 1, true, false},
	"now":          {builtinNow, 0, 0, false, false},
	"parseTime":    {builtinParseTime, 2, 2, true, false},
	"real":         {builtinReal, 1, 1, true, false},
	"second":       {builtinSecond, 1, 1, true, false},
	"seconds":      {builtinSeconds, 1, 1, true, false},
	"since":        {builtinSince, 1, 1, false, false},
	"sum":          {builtinSum, 1, 1, false, true},
	"timeIn":       {builtinTimeIn, 2, 2, true, false},
	"weekday":      {builtinWeekday, 1, 1, true, false},
	"year":         {builtinYear, 1, 1, true, false},
	"yearDay":      {builtinYearday, 1, 1, true, false},
}

func badNArgs(min int, s string, arg []interface{}) error {
	a := []string{}
	for _, v := range arg {
		a = append(a, fmt.Sprintf("%v", v))
	}
	switch len(arg) < min {
	case true:
		return fmt.Errorf("missing argument to %s(%s)", s, strings.Join(a, ", "))
	default: //case false:
		return fmt.Errorf("too many arguments to %s(%s)", s, strings.Join(a, ", "))
	}
}

func invArg(arg interface{}, s string) error {
	return fmt.Errorf("invalid argument %v (type %T) for %s", arg, arg, s)
}

func builtinTestBlob(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	n, err := intExpr(arg[0])
	if err != nil {
		return nil, err
	}

	rng := rand.New(rand.NewSource(n))
	b := make([]byte, n)
	for i := range b {
		b[i] = byte(rng.Int())
	}
	return b, nil
}

func builtinTestString(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	n, err := intExpr(arg[0])
	if err != nil {
		return nil, err
	}

	rng := rand.New(rand.NewSource(n))
	b := make([]byte, n)
	for i := range b {
		b[i] = byte(rng.Int())
	}
	return string(b), nil
}

func builtinAvg(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	type avg struct {
		sum interface{}
		n   uint64
	}

	if _, ok := ctx["$agg0"]; ok {
		return
	}

	fn := ctx["$fn"]
	if _, ok := ctx["$agg"]; ok {
		data, ok := ctx[fn].(avg)
		if !ok {
			return
		}

		switch x := data.sum.(type) {
		case complex64:
			return complex64(complex128(x) / complex(float64(data.n), 0)), nil
		case complex128:
			return complex64(complex128(x) / complex(float64(data.n), 0)), nil
		case float32:
			return float32(float64(x) / float64(data.n)), nil
		case float64:
			return float64(x) / float64(data.n), nil
		case int8:
			return int8(int64(x) / int64(data.n)), nil
		case int16:
			return int16(int64(x) / int64(data.n)), nil
		case int32:
			return int32(int64(x) / int64(data.n)), nil
		case int64:
			return int64(int64(x) / int64(data.n)), nil
		case uint8:
			return uint8(uint64(x) / data.n), nil
		case uint16:
			return uint16(uint64(x) / data.n), nil
		case uint32:
			return uint32(uint64(x) / data.n), nil
		case uint64:
			return uint64(uint64(x) / data.n), nil
		}

	}

	data, _ := ctx[fn].(avg)
	y := arg[0]
	if y == nil {
		return
	}

	switch x := data.sum.(type) {
	case nil:
		switch y := y.(type) {
		case float32, float64, int8, int16, int32, int64, uint8, uint16, uint32, uint64:
			data = avg{y, 0}
		default:
			return nil, fmt.Errorf("avg: cannot accept %v (value if type %T)", y, y)
		}
	case complex64:
		data.sum = x + y.(complex64)
	case complex128:
		data.sum = x + y.(complex128)
	case float32:
		data.sum = x + y.(float32)
	case float64:
		data.sum = x + y.(float64)
	case int8:
		data.sum = x + y.(int8)
	case int16:
		data.sum = x + y.(int16)
	case int32:
		data.sum = x + y.(int32)
	case int64:
		data.sum = x + y.(int64)
	case uint8:
		data.sum = x + y.(uint8)
	case uint16:
		data.sum = x + y.(uint16)
	case uint32:
		data.sum = x + y.(uint32)
	case uint64:
		data.sum = x + y.(uint64)
	}
	data.n++
	ctx[fn] = data
	return
}

func builtinComplex(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) {
	re, im := arg[0], arg[1]
	if re == nil || im == nil {
		return nil, nil
	}

	re, im = coerce(re, im)
	if reflect.TypeOf(re) != reflect.TypeOf(im) {
		return nil, fmt.Errorf("complex(%T(%#v), %T(%#v)): invalid types", re, re, im, im)
	}

	switch re := re.(type) {
	case idealFloat:
		return idealComplex(complex(float64(re), float64(im.(idealFloat)))), nil
	case idealInt:
		return idealComplex(complex(float64(re), float64(im.(idealInt)))), nil
	case idealRune:
		return idealComplex(complex(float64(re), float64(im.(idealRune)))), nil
	case idealUint:
		return idealComplex(complex(float64(re), float64(im.(idealUint)))), nil
	case float32:
		return complex(float32(re), im.(float32)), nil
	case float64:
		return complex(float64(re), im.(float64)), nil
	case int8:
		return complex(float64(re), float64(im.(int8))), nil
	case int16:
		return complex(float64(re), float64(im.(int16))), nil
	case int32:
		return complex(float64(re), float64(im.(int32))), nil
	case int64:
		return complex(float64(re), float64(im.(int64))), nil
	case uint8:
		return complex(float64(re), float64(im.(uint8))), nil
	case uint16:
		return complex(float64(re), float64(im.(uint16))), nil
	case uint32:
		return complex(float64(re), float64(im.(uint32))), nil
	case uint64:
		return complex(float64(re), float64(im.(uint64))), nil
	default:
		return nil, invArg(re, "complex")
	}
}

func builtinContains(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) {
	switch s := arg[0].(type) {
	case nil:
		return nil, nil
	case string:
		switch chars := arg[1].(type) {
		case nil:
			return nil, nil
		case string:
			return strings.Contains(s, chars), nil
		default:
			return nil, invArg(chars, "string")
		}
	default:
		return nil, invArg(s, "string")
	}
}

func builtinCount(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	if _, ok := ctx["$agg0"]; ok {
		return int64(0), nil
	}

	fn := ctx["$fn"]
	if _, ok := ctx["$agg"]; ok {
		return ctx[fn].(int64), nil
	}

	n, _ := ctx[fn].(int64)
	switch len(arg) {
	case 0:
		n++
	case 1:
		if arg[0] != nil {
			n++
		}
	default:
		panic("internal error 067")
	}
	ctx[fn] = n
	return
}

func builtinDate(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) {
	for i, v := range arg {
		switch i {
		case 7:
			switch x := v.(type) {
			case string:
			default:
				return nil, invArg(x, "date")
			}
		default:
			switch x := v.(type) {
			case int64:
			case idealInt:
				arg[i] = int64(x)
			default:
				return nil, invArg(x, "date")
			}
		}
	}

	sloc := arg[7].(string)
	loc := time.Local
	switch sloc {
	case "local":
	default:
		loc, err = time.LoadLocation(sloc)
		if err != nil {
			return
		}
	}

	return time.Date(
		int(arg[0].(int64)),
		time.Month(arg[1].(int64)),
		int(arg[2].(int64)),
		int(arg[3].(int64)),
		int(arg[4].(int64)),
		int(arg[5].(int64)),
		int(arg[6].(int64)),
		loc,
	), nil
}

func builtinLen(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case string:
		return int64(len(x)), nil
	default:
		return nil, invArg(x, "len")
	}
}

func builtinDay(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Time:
		return int64(x.Day()), nil
	default:
		return nil, invArg(x, "day")
	}
}

func builtinFormatTime(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Time:
		switch y := arg[1].(type) {
		case nil:
			return nil, nil
		case string:
			return x.Format(y), nil
		default:
			return nil, invArg(y, "formatTime")
		}
	default:
		return nil, invArg(x, "formatTime")
	}
}

func builtinFormatFloat(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	var val float64
	var fmt byte = 'g'

	prec := -1
	bitSize := 64

	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case float32:
		val = float64(x)
		bitSize = 32
	case float64:
		val = x
	default:
		return nil, invArg(x, "formatFloat")
	}

	switch len(arg) {
	case 4:
		arg3 := coerce1(arg[3], int64(0))
		switch y := arg3.(type) {
		case nil:
			return nil, nil
		case int64:
			bitSize = int(y)
		default:
			return nil, invArg(y, "formatFloat")
		}
		fallthrough
	case 3:
		arg2 := coerce1(arg[2], int64(0))
		switch y := arg2.(type) {
		case nil:
			return nil, nil
		case int64:
			prec = int(y)
		default:
			return nil, invArg(y, "formatFloat")
		}
		fallthrough
	case 2:
		arg1 := coerce1(arg[1], byte(0))
		switch y := arg1.(type) {
		case nil:
			return nil, nil
		case byte:
			fmt = y
		default:
			return nil, invArg(y, "formatFloat")
		}
	}

	return strconv.FormatFloat(val, fmt, prec, bitSize), nil
}

func builtinFormatInt(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	var intVal int64
	var uintVal uint64

	uintType := false
	base := 10

	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case int8:
		intVal = int64(x)
	case int16:
		intVal = int64(x)
	case int32:
		intVal = int64(x)
	case int64:
		intVal = x
	case uint8:
		uintType = true
		uintVal = uint64(x)
	case uint16:
		uintType = true
		uintVal = uint64(x)
	case uint32:
		uintType = true
		uintVal = uint64(x)
	case uint64:
		uintType = true
		uintVal = x
	default:
		return nil, invArg(x, "formatInt")
	}

	switch len(arg) {
	case 2:
		arg1 := coerce1(arg[1], int64(0))
		switch y := arg1.(type) {
		case nil:
			return nil, nil
		case int64:
			base = int(y)
		default:
			return nil, invArg(y, "formatInt")
		}
	}

	if uintType {
		return strconv.FormatUint(uintVal, base), nil
	}

	return strconv.FormatInt(intVal, base), nil
}

func builtinHasPrefix(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) {
	switch s := arg[0].(type) {
	case nil:
		return nil, nil
	case string:
		switch prefix := arg[1].(type) {
		case nil:
			return nil, nil
		case string:
			return strings.HasPrefix(s, prefix), nil
		default:
			return nil, invArg(prefix, "string")
		}
	default:
		return nil, invArg(s, "string")
	}
}

func builtinHasSuffix(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) {
	switch s := arg[0].(type) {
	case nil:
		return nil, nil
	case string:
		switch suffix := arg[1].(type) {
		case nil:
			return nil, nil
		case string:
			return strings.HasSuffix(s, suffix), nil
		default:
			return nil, invArg(suffix, "string")
		}
	default:
		return nil, invArg(s, "string")
	}
}

func builtinHour(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Time:
		return int64(x.Hour()), nil
	default:
		return nil, invArg(x, "hour")
	}
}

func builtinHours(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Duration:
		return x.Hours(), nil
	default:
		return nil, invArg(x, "hours")
	}
}

func builtinID(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := ctx["$id"].(type) {
	case map[string]interface{}:
		if len(arg) == 0 {
			return nil, nil
		}

		tab := arg[0].(*ident)
		id, ok := x[tab.s]
		if !ok {
			return nil, fmt.Errorf("value not available: id(%s)", tab)
		}

		if _, ok := id.(int64); ok {
			return id, nil
		}

		return nil, fmt.Errorf("value not available: id(%s)", tab)
	case int64:
		return x, nil
	default:
		return nil, nil
	}
}

func builtinImag(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case idealComplex:
		return imag(x), nil
	case complex64:
		return imag(x), nil
	case complex128:
		return imag(x), nil
	default:
		return nil, invArg(x, "imag")
	}
}

func builtinMax(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	if _, ok := ctx["$agg0"]; ok {
		return
	}

	fn := ctx["$fn"]
	if _, ok := ctx["$agg"]; ok {
		if v, ok = ctx[fn]; ok {
			return
		}

		return nil, nil
	}

	max := ctx[fn]
	y := arg[0]
	if y == nil {
		return
	}
	switch x := max.(type) {
	case nil:
		switch y := y.(type) {
		case float32, float64, string, int8, int16, int32, int64, uint8, uint16, uint32, uint64, time.Time:
			max = y
		default:
			return nil, fmt.Errorf("max: cannot accept %v (value if type %T)", y, y)
		}
	case float32:
		if y := y.(float32); y > x {
			max = y
		}
	case float64:
		if y := y.(float64); y > x {
			max = y
		}
	case string:
		if y := y.(string); y > x {
			max = y
		}
	case int8:
		if y := y.(int8); y > x {
			max = y
		}
	case int16:
		if y := y.(int16); y > x {
			max = y
		}
	case int32:
		if y := y.(int32); y > x {
			max = y
		}
	case int64:
		if y := y.(int64); y > x {
			max = y
		}
	case uint8:
		if y := y.(uint8); y > x {
			max = y
		}
	case uint16:
		if y := y.(uint16); y > x {
			max = y
		}
	case uint32:
		if y := y.(uint32); y > x {
			max = y
		}
	case uint64:
		if y := y.(uint64); y > x {
			max = y
		}
	case time.Time:
		if y := y.(time.Time); y.After(x) {
			max = y
		}
	}
	ctx[fn] = max
	return
}

func builtinMin(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	if _, ok := ctx["$agg0"]; ok {
		return
	}

	fn := ctx["$fn"]
	if _, ok := ctx["$agg"]; ok {
		if v, ok = ctx[fn]; ok {
			return
		}

		return nil, nil
	}

	min := ctx[fn]
	y := arg[0]
	if y == nil {
		return
	}
	switch x := min.(type) {
	case nil:
		switch y := y.(type) {
		case float32, float64, string, int8, int16, int32, int64, uint8, uint16, uint32, uint64, time.Time:
			min = y
		default:
			return nil, fmt.Errorf("min: cannot accept %v (value if type %T)", y, y)
		}
	case float32:
		if y := y.(float32); y < x {
			min = y
		}
	case float64:
		if y := y.(float64); y < x {
			min = y
		}
	case string:
		if y := y.(string); y < x {
			min = y
		}
	case int8:
		if y := y.(int8); y < x {
			min = y
		}
	case int16:
		if y := y.(int16); y < x {
			min = y
		}
	case int32:
		if y := y.(int32); y < x {
			min = y
		}
	case int64:
		if y := y.(int64); y < x {
			min = y
		}
	case uint8:
		if y := y.(uint8); y < x {
			min = y
		}
	case uint16:
		if y := y.(uint16); y < x {
			min = y
		}
	case uint32:
		if y := y.(uint32); y < x {
			min = y
		}
	case uint64:
		if y := y.(uint64); y < x {
			min = y
		}
	case time.Time:
		if y := y.(time.Time); y.Before(x) {
			min = y
		}
	}
	ctx[fn] = min
	return
}

func builtinMinute(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Time:
		return int64(x.Minute()), nil
	default:
		return nil, invArg(x, "minute")
	}
}

func builtinMinutes(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Duration:
		return x.Minutes(), nil
	default:
		return nil, invArg(x, "minutes")
	}
}

func builtinMonth(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Time:
		return int64(x.Month()), nil
	default:
		return nil, invArg(x, "month")
	}
}

func builtinNanosecond(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Time:
		return int64(x.Nanosecond()), nil
	default:
		return nil, invArg(x, "nanosecond")
	}
}

func builtinNanoseconds(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Duration:
		return x.Nanoseconds(), nil
	default:
		return nil, invArg(x, "nanoseconds")
	}
}

func builtinNow(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	return time.Now(), nil
}

func builtinParseTime(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	var a [2]string
	for i, v := range arg {
		switch x := v.(type) {
		case nil:
			return nil, nil
		case string:
			a[i] = x
		default:
			return nil, invArg(x, "parseTime")
		}
	}

	t, err := time.Parse(a[0], a[1])
	if err != nil {
		return nil, err
	}

	ls := t.Location().String()
	if ls == "UTC" {
		return t, nil
	}

	l, err := time.LoadLocation(ls)
	if err != nil {
		return t, nil
	}

	return time.ParseInLocation(a[0], a[1], l)
}

func builtinReal(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case idealComplex:
		return real(x), nil
	case complex64:
		return real(x), nil
	case complex128:
		return real(x), nil
	default:
		return nil, invArg(x, "real")
	}
}

func builtinSecond(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Time:
		return int64(x.Second()), nil
	default:
		return nil, invArg(x, "second")
	}
}

func builtinSeconds(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Duration:
		return x.Seconds(), nil
	default:
		return nil, invArg(x, "seconds")
	}
}

func builtinSince(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Time:
		return time.Since(x), nil
	default:
		return nil, invArg(x, "since")
	}
}

func builtinSum(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	if _, ok := ctx["$agg0"]; ok {
		return
	}

	fn := ctx["$fn"]
	if _, ok := ctx["$agg"]; ok {
		if v, ok = ctx[fn]; ok {
			return
		}

		return nil, nil
	}

	sum := ctx[fn]
	y := arg[0]
	if y == nil {
		return
	}
	switch x := sum.(type) {
	case nil:
		switch y := y.(type) {
		case complex64, complex128, float32, float64, int8, int16, int32, int64, uint8, uint16, uint32, uint64:
			sum = y
		default:
			return nil, fmt.Errorf("sum: cannot accept %v (value if type %T)", y, y)
		}
	case complex64:
		sum = x + y.(complex64)
	case complex128:
		sum = x + y.(complex128)
	case float32:
		sum = x + y.(float32)
	case float64:
		sum = x + y.(float64)
	case int8:
		sum = x + y.(int8)
	case int16:
		sum = x + y.(int16)
	case int32:
		sum = x + y.(int32)
	case int64:
		sum = x + y.(int64)
	case uint8:
		sum = x + y.(uint8)
	case uint16:
		sum = x + y.(uint16)
	case uint32:
		sum = x + y.(uint32)
	case uint64:
		sum = x + y.(uint64)
	}
	ctx[fn] = sum
	return
}

func builtinTimeIn(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Time:
		switch y := arg[1].(type) {
		case nil:
			return nil, nil
		case string:
			loc := time.Local
			switch y {
			case "local":
			default:
				loc, err = time.LoadLocation(y)
				if err != nil {
					return
				}
			}

			return x.In(loc), nil
		default:
			return nil, invArg(x, "timeIn")
		}
	default:
		return nil, invArg(x, "timeIn")
	}
}

func builtinWeekday(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Time:
		return int64(x.Weekday()), nil
	default:
		return nil, invArg(x, "weekday")
	}
}

func builtinYear(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Time:
		return int64(x.Year()), nil
	default:
		return nil, invArg(x, "year")
	}
}

func builtinYearday(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) {
	switch x := arg[0].(type) {
	case nil:
		return nil, nil
	case time.Time:
		return int64(x.YearDay()), nil
	default:
		return nil, invArg(x, "yearDay")
	}
}
