package main

import (
	"fmt"
	"strings"

	. "github.com/dave/jennifer/jen"
)

//go:generate sh -c "go run . > ../zz_generated.go"

var toFuncs = []struct {
	name       string
	returnType *Statement
}{
	{"ToBool", Bool()},
	{"ToString", String()},
	{"ToTime", Qual("time", "Time")},
	{"ToTimeInDefaultLocation", Qual("time", "Time")},
	{"ToDuration", Qual("time", "Duration")},
	{"ToInt", Int()},
	{"ToInt8", Int8()},
	{"ToInt16", Int16()},
	{"ToInt32", Int32()},
	{"ToInt64", Int64()},
	{"ToUint", Uint()},
	{"ToUint8", Uint8()},
	{"ToUint16", Uint16()},
	{"ToUint32", Uint32()},
	{"ToUint64", Uint64()},
	{"ToFloat32", Float32()},
	{"ToFloat64", Float64()},
	{"ToStringMapString", Map(String()).String()},
	{"ToStringMapStringSlice", Map(String()).Index().String()},
	{"ToStringMapBool", Map(String()).Bool()},
	{"ToStringMapInt", Map(String()).Int()},
	{"ToStringMapInt64", Map(String()).Int64()},
	{"ToStringMap", Map(String()).Any()},
	{"ToSlice", Index().Any()},
	{"ToBoolSlice", Index().Bool()},
	{"ToStringSlice", Index().String()},
	{"ToIntSlice", Index().Int()},
	{"ToInt64Slice", Index().Int64()},
	{"ToUintSlice", Index().Uint()},
	{"ToFloat64Slice", Index().Float64()},
	{"ToDurationSlice", Index().Qual("time", "Duration")},
}

var toSliceFuncs = []struct {
	typeName   string
	returnType *Statement
}{
	{"bool", Bool()},
	// {"time", Qual("time", "Time")},
	{"duration", Qual("time", "Duration")},
	{"int", Int()},
	{"int8", Int8()},
	{"int16", Int16()},
	{"int32", Int32()},
	{"int64", Int64()},
	{"uint", Uint()},
	{"uint8", Uint8()},
	{"uint16", Uint16()},
	{"uint32", Uint32()},
	{"uint64", Uint64()},
	{"float32", Float32()},
	{"float64", Float64()},
}

func main() {
	file := NewFile("cast")

	file.HeaderComment("Code generated by cast generator. DO NOT EDIT.")

	for _, fn := range toFuncs {
		if fn.name == "ToTimeInDefaultLocation" {
			toFuncWithParams(file, fn.name, fn.returnType, Id("location").Op("*").Qual("time", "Location"))
		} else {
			toFunc(file, fn.name, fn.returnType)
		}
	}

	for _, fn := range toSliceFuncs {
		toSliceEFunc(file, fn.typeName, fn.returnType)
	}

	fmt.Printf("%#v", file)
}

func toFunc(file *File, funcName string, returnType *Statement) {
	toFuncWithParams(file, funcName, returnType)
}

func toFuncWithParams(file *File, funcName string, returnType *Statement, args ...*Statement) {
	file.Comment(fmt.Sprintf("%s casts any value to a(n) %s type.", funcName, returnType.GoString()))

	varI := Id("i")

	arguments := []Code{varI.Clone().Any()}

	for _, arg := range args {
		arguments = append(arguments, arg)
	}

	file.Func().
		Id(funcName).Params(arguments...).Params(returnType).
		BlockFunc(func(g *Group) {
			varV := Id("v")

			arguments := []Code{varI}

			for _, arg := range args {
				arguments = append(arguments, (*arg)[0])
			}

			g.List(varV, Id("_")).Op(":=").Id(funcName + "E").Call(arguments...)
			g.Return(varV)
		})
}

func toSliceEFunc(file *File, typeName string, returnType *Statement) {
	funcName := "To" + strings.ToUpper(typeName[:1]) + typeName[1:] + "SliceE"
	sliceReturnType := Index().Add(returnType)

	file.Comment(fmt.Sprintf("%s casts any value to a(n) %s type.", funcName, sliceReturnType.GoString()))

	varI := Id("i")

	file.Func().
		Id(funcName).Params(varI.Clone().Any()).Params(sliceReturnType, Error()).
		BlockFunc(func(g *Group) {
			g.Return(Id("toSliceE").Types(returnType).Call(varI))
		})
}
