// +build ignore

package main

import (
	"bytes"
	"go/format"
	"io"
	"log"
	"os"
	"text/template"
)

type types struct {
	BasicKinds []string
	BasicProps []string
	Types      []string
}

func main() {
	typesList := types{
		BasicKinds: []string{
			"Bool",
			"Int",
			"Int8",
			"Int16",
			"Int32",
			"Int64",
			"Uint",
			"Uint8",
			"Uint16",
			"Uint32",
			"Uint64",
			"Uintptr",
			"Float32",
			"Float64",
			"Complex64",
			"Complex128",
			"String",
			"UnsafePointer",
			"UntypedBool",
			"UntypedInt",
			"UntypedRune",
			"UntypedFloat",
			"UntypedComplex",
			"UntypedString",
			"UntypedNil",
		},

		BasicProps: []string{
			"Boolean",
			"Integer",
			"Unsigned",
			"Float",
			"Complex",
			"String",
			"Untyped",
			"Ordered",
			"Numeric",
			"ConstType",
		},

		Types: []string{
			"Basic",
			"Array",
			"Slice",
			"Struct",
			"Pointer",
			"Tuple",
			"Signature",
			"Interface",
			"Map",
			"Chan",
			"Named",
		},
	}

	simplePredicateFile, err := os.Create("simplePredicates.go")
	if err != nil {
		log.Fatal(err)
	}
	writeCode(simplePredicateFile, typesList)
}

func generateCode(tmplText string, typeList types) []byte {
	tmpl := template.Must(template.New("code").Parse(tmplText))
	var code bytes.Buffer
	tmpl.Execute(&code, typeList)
	prettyCode, err := format.Source(code.Bytes())
	if err != nil {
		panic(err)
	}
	return prettyCode
}

func writeCode(output io.Writer, typeList types) {
	code := generateCode(`// Code generated by simplePredicates_generate.go; DO NOT EDIT

package typep

import (
	"go/types"
)

// Simple 1-to-1 type predicates via type assertion.

{{ range .Types }}
// Is{{.}} reports whether a given type has *types.{{.}} type.
func Is{{.}}(typ types.Type) bool {
	_, ok := typ.(*types.{{.}})
	return ok
}
{{ end }}

// *types.Basic predicates for the info field.

{{ range .BasicProps }}
// Has{{.}}Prop reports whether typ is a *types.Basic has Is{{.}} property.
func Has{{.}}Prop(typ types.Type) bool {
	if typ, ok := typ.(*types.Basic); ok {
		return typ.Info()&types.Is{{.}} != 0
	}
	return false
}
{{ end }}

// *types.Basic predicates for the kind field.

{{ range .BasicKinds }}
// Has{{.}}Kind reports whether typ is a *types.Basic with its kind set to types.{{.}}.
func Has{{.}}Kind(typ types.Type) bool {
	if typ, ok := typ.(*types.Basic); ok {
		return typ.Kind() == types.{{.}}
	}
	return false
}
{{ end }}
`, typeList)
	output.Write(code)
}
