package gen

import (
	"bytes"
	"fmt"
	"github.com/go-fed/activity/tools/defs"
	"strings"
)

func generatePropertyDefinitions(t *defs.PropertyType, this *defs.StructDef, i *defs.InterfaceDef, m map[*defs.PropertyType]*intermedDef) (fd []*defs.FunctionDef, sd []*defs.StructDef, s, d string) {
	if isAny(t) {
		fd, sd, s, d = generateAnyDefinitions(t, this, i)
	} else if isSingleType(t) {
		fd, sd, s, d = generateSingleTypeDefinitions(t, this, i)
	} else {
		fd, sd, s, d = generateMultiTypeDefinitions(t, this, i, m)
	}
	if t.NaturalLanguageMap {
		fdMap, sdMap, sMap, dMap := generateNaturalLanguageMap(t, this, i)
		fd = append(fd, fdMap...)
		sd = append(sd, sdMap...)
		s += "\n" + sMap
		d += "\n" + dMap
	}
	return
}

func generateNaturalLanguageMap(t *defs.PropertyType, this *defs.StructDef, i *defs.InterfaceDef) (fd []*defs.FunctionDef, sd []*defs.StructDef, s, d string) {
	mapMember := &defs.StructMember{
		Name:    t.Name + "Map",
		Type:    "map[string]string",
		Comment: fmt.Sprintf("The '%sMap' value holds language-specific values for property '%s'", t.Name, t.Name),
	}
	this.M = append(this.M, mapMember)
	this.F = append(this.F, []*defs.MemberFunctionDef{
		{
			Name:    fmt.Sprintf("%sMapLanguages", strings.Title(t.Name)),
			Comment: fmt.Sprintf("%sMapLanguages returns all languages for this property's language mapping, or nil if there are none.", strings.Title(t.Name)),
			P:       this,
			Return:  []*defs.FunctionVarDef{{"l", "[]string"}},
			Body: func() string {
				var b bytes.Buffer
				b.WriteString(fmt.Sprintf("if t.%sMap == nil || len(t.%sMap) == 0 {\n", t.Name, t.Name))
				b.WriteString("return nil\n")
				b.WriteString("}\n")
				b.WriteString(fmt.Sprintf("for k := range t.%sMap {\n", t.Name))
				b.WriteString("l = append(l, k)\n")
				b.WriteString("}\n")
				b.WriteString("return\n")
				return b.String()
			},
		},
		{
			Name:    fmt.Sprintf("Get%sMap", strings.Title(t.Name)),
			Comment: fmt.Sprintf("Get%sMap retrieves the value of the property for the specified language, or an empty string if it does not exist", strings.Title(t.Name)),
			P:       this,
			Args:    []*defs.FunctionVarDef{{"l", "string"}},
			Return:  []*defs.FunctionVarDef{{"v", "string"}},
			Body: func() string {
				var b bytes.Buffer
				b.WriteString(fmt.Sprintf("if t.%sMap == nil {\n", t.Name))
				b.WriteString("return \"\"\n")
				b.WriteString("}\n")
				b.WriteString("ok := false\n")
				b.WriteString(fmt.Sprintf("v, ok = t.%sMap[l]\n", t.Name))
				b.WriteString("if !ok {\n")
				b.WriteString("return \"\"\n")
				b.WriteString("}\n")
				b.WriteString("return v\n")
				return b.String()
			},
		},
		{
			Name:    fmt.Sprintf("Set%sMap", strings.Title(t.Name)),
			Comment: fmt.Sprintf("Set%sMap sets the value of the property for the specified language", strings.Title(t.Name)),
			P:       this,
			Args:    []*defs.FunctionVarDef{{"l", "string"}, {"v", "string"}},
			Body: func() string {
				var b bytes.Buffer
				b.WriteString(fmt.Sprintf("if t.%sMap == nil {\n", t.Name))
				b.WriteString(fmt.Sprintf("t.%sMap = make(map[string]string)\n", t.Name))
				b.WriteString("}\n")
				b.WriteString(fmt.Sprintf("t.%sMap[l] = v\n", t.Name))
				return b.String()
			},
		},
	}...)
	i.F = append(i.F, []*defs.FunctionDef{
		{
			Name:    fmt.Sprintf("%sMapLanguages", strings.Title(t.Name)),
			Comment: fmt.Sprintf("%sMapLanguages returns all languages for this property's language mapping, or nil if there are none.", strings.Title(t.Name)),
			Return:  []*defs.FunctionVarDef{{"l", "[]string"}},
		},
		{
			Name:    fmt.Sprintf("Get%sMap", strings.Title(t.Name)),
			Comment: fmt.Sprintf("Get%sMap retrieves the value of the property for the specified language, or an empty string if it does not exist", strings.Title(t.Name)),
			Args:    []*defs.FunctionVarDef{{"l", "string"}},
			Return:  []*defs.FunctionVarDef{{"v", "string"}},
		},
		{
			Name:    fmt.Sprintf("Set%sMap", strings.Title(t.Name)),
			Comment: fmt.Sprintf("Set%sMap sets the value of the property for the specified language", strings.Title(t.Name)),
			Args:    []*defs.FunctionVarDef{{"l", "string"}, {"v", "string"}},
		},
	}...)
	var b bytes.Buffer
	b.WriteString("// Begin generation by generateNaturalLanguageMap\n")
	b.WriteString(fmt.Sprintf("if k == \"%sMap\" {\n", t.Name))
	b.WriteString("if vMap, ok := v.(map[string]interface{}); ok {\n")
	b.WriteString("val := make(map[string]string)\n")
	b.WriteString("for k, iVal := range vMap {\n")
	b.WriteString("if sVal, ok := iVal.(string); ok {\n")
	b.WriteString("val[k] = sVal\n")
	b.WriteString("}\n")
	b.WriteString("}\n")
	b.WriteString(fmt.Sprintf("t.%sMap = val\n", t.Name))
	b.WriteString("handled = true\n")
	b.WriteString("}\n")
	b.WriteString("}\n")
	b.WriteString("// End generation by generateNaturalLanguageMap\n")
	d = b.String()
	var bs bytes.Buffer
	bs.WriteString("// Begin generation by generateNaturalLanguageMap\n")
	bs.WriteString(fmt.Sprintf("if t.%sMap != nil && len(t.%sMap) >= 0 {\n", t.Name, t.Name))
	bs.WriteString(fmt.Sprintf("m[\"%sMap\"] = t.%sMap\n", t.Name, t.Name))
	bs.WriteString("}\n")
	bs.WriteString("// End generation by generateNaturalLanguageMap\n")
	s = bs.String()
	return
}

func generateAnyDefinitions(t *defs.PropertyType, this *defs.StructDef, i *defs.InterfaceDef) (fd []*defs.FunctionDef, sd []*defs.StructDef, s, d string) {
	if t.Functional {
		return generateFunctionalAnyDefinition(t, this, i)
	} else {
		return generateNonFunctionalAnyDefinition(t, this, i)
	}
}

func generateFunctionalAnyDefinition(t *defs.PropertyType, this *defs.StructDef, i *defs.InterfaceDef) (fd []*defs.FunctionDef, sd []*defs.StructDef, s, d string) {
	titleName := strings.Title(t.Name)
	anyMember := &defs.StructMember{
		Name:    cleanName(t.Name),
		Type:    "interface{}",
		Comment: fmt.Sprintf("The functional '%s' value can hold any type, but only a single value", t.Name),
	}
	this.M = append(this.M, anyMember)
	this.F = append(this.F, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("Get%s", titleName),
		Comment: fmt.Sprintf("Get%s returns the value for %s", titleName, t.Name),
		P:       this,
		Return:  []*defs.FunctionVarDef{{"v", anyMember.Type}},
		Body: func() string {
			var b bytes.Buffer
			b.WriteString(fmt.Sprintf("return t.%s\n", anyMember.Name))
			return b.String()
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("Set%s", titleName),
		Comment: fmt.Sprintf("Set%s sets the value of %s", titleName, t.Name),
		P:       this,
		Args:    []*defs.FunctionVarDef{{"v", anyMember.Type}},
		Body: func() string {
			var b bytes.Buffer
			b.WriteString(fmt.Sprintf("t.%s = v\n", anyMember.Name))
			return b.String()
		},
	})
	i.F = append(i.F, []*defs.FunctionDef{
		{
			Name:    fmt.Sprintf("Get%s", titleName),
			Comment: fmt.Sprintf("Get%s returns the value for %s", titleName, t.Name),
			Return:  []*defs.FunctionVarDef{{"v", anyMember.Type}},
		},
		{
			Name:    fmt.Sprintf("Set%s", titleName),
			Comment: fmt.Sprintf("Set%s sets the value of %s", titleName, t.Name),
			Args:    []*defs.FunctionVarDef{{"v", anyMember.Type}},
		},
	}...)
	var b bytes.Buffer
	b.WriteString("// Begin generation by generateFunctionalAnyDefinition\n")
	b.WriteString(fmt.Sprintf("if k == \"%s\" {\n", t.Name))
	b.WriteString(fmt.Sprintf("t.%s = v\n", anyMember.Name))
	b.WriteString("handled = true\n")
	b.WriteString("}\n")
	b.WriteString("// End generation by generateFunctionalAnyDefinition\n")
	d = b.String()
	var bs bytes.Buffer
	bs.WriteString("// Begin generation by generateFunctionalAnyDefinition\n")
	bs.WriteString(fmt.Sprintf("if t.%s != nil {\n", anyMember.Name))
	bs.WriteString(fmt.Sprintf("m[\"%s\"] = t.%s\n", t.Name, anyMember.Name))
	bs.WriteString("}\n")
	bs.WriteString("// End generation by generateFunctionalAnyDefinition\n")
	s = bs.String()
	return
}

func generateNonFunctionalAnyDefinition(t *defs.PropertyType, this *defs.StructDef, i *defs.InterfaceDef) (fd []*defs.FunctionDef, sd []*defs.StructDef, s, d string) {
	titleName := strings.Title(t.Name)
	anyMember := &defs.StructMember{
		Name:    cleanName(t.Name),
		Type:    "[]interface{}",
		Comment: fmt.Sprintf("The '%s' value can hold any type and any number of values", t.Name),
	}
	this.M = append(this.M, anyMember)
	this.F = append(this.F, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("%sLen", titleName),
		Comment: fmt.Sprintf("%sLen determines the number of elements able to be used for the Get%s and Remove%s functions", titleName, titleName, titleName),
		P:       this,
		Return:  []*defs.FunctionVarDef{{"l", "int"}},
		Body: func() string {
			return fmt.Sprintf("return len(t.%s)\n", anyMember.Name)
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("Get%s", titleName),
		Comment: fmt.Sprintf("Get%s returns the value for the specified index", titleName),
		P:       this,
		Args:    []*defs.FunctionVarDef{{"index", "int"}},
		Return:  []*defs.FunctionVarDef{{"v", deSlice(anyMember.Type)}},
		Body: func() string {
			var b bytes.Buffer
			b.WriteString(fmt.Sprintf("return t.%s[index]\n", anyMember.Name))
			return b.String()
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("Append%s", titleName),
		Comment: fmt.Sprintf("Append%s adds a value to the back of %s", titleName, t.Name),
		P:       this,
		Args:    []*defs.FunctionVarDef{{"v", deSlice(anyMember.Type)}},
		Body: func() string {
			var b bytes.Buffer
			b.WriteString(fmt.Sprintf("t.%s = append(t.%s, v)\n", anyMember.Name, anyMember.Name))
			return b.String()
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("Prepend%s", titleName),
		Comment: fmt.Sprintf("Prepend%s adds a value to the front of %s", titleName, t.Name),
		P:       this,
		Args:    []*defs.FunctionVarDef{{"v", deSlice(anyMember.Type)}},
		Body: func() string {
			var b bytes.Buffer
			b.WriteString(fmt.Sprintf("t.%s = append(%s{v}, t.%s...)\n", anyMember.Name, anyMember.Type, anyMember.Name))
			return b.String()
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("Remove%s", titleName),
		Comment: fmt.Sprintf("Remove%s deletes the value from the specified index", titleName),
		P:       this,
		Args:    []*defs.FunctionVarDef{{"index", "int"}},
		Body: func() string {
			sliceVar := fmt.Sprintf("t.%s", anyMember.Name)
			var b bytes.Buffer
			b.WriteString(fmt.Sprintf("copy(%s[index:], %s[index+1:])\n", sliceVar, sliceVar))
			b.WriteString(fmt.Sprintf("%s[len(%s)-1] = nil\n", sliceVar, sliceVar))
			b.WriteString(fmt.Sprintf("%s = %s[:len(%s)-1]\n", sliceVar, sliceVar, sliceVar))
			return b.String()
		},
	})
	i.F = append(i.F, []*defs.FunctionDef{
		{
			Name:    fmt.Sprintf("%sLen", titleName),
			Comment: fmt.Sprintf("%sLen determines the number of elements able to be used for the Get%s and Remove%s functions", titleName, titleName, titleName),
			Return:  []*defs.FunctionVarDef{{"l", "int"}},
		},
		{
			Name:    fmt.Sprintf("Get%s", titleName),
			Comment: fmt.Sprintf("Get%s returns the value for the specified index", titleName),
			Args:    []*defs.FunctionVarDef{{"index", "int"}},
			Return:  []*defs.FunctionVarDef{{"v", deSlice(anyMember.Type)}},
		},
		{
			Name:    fmt.Sprintf("Append%s", titleName),
			Comment: fmt.Sprintf("Append%s adds a value to the back of %s", titleName, t.Name),
			Args:    []*defs.FunctionVarDef{{"v", deSlice(anyMember.Type)}},
		},
		{
			Name:    fmt.Sprintf("Prepend%s", titleName),
			Comment: fmt.Sprintf("Prepend%s adds a value to the front of %s", titleName, t.Name),
			Args:    []*defs.FunctionVarDef{{"v", deSlice(anyMember.Type)}},
		},
		{
			Name:    fmt.Sprintf("Remove%s", titleName),
			Comment: fmt.Sprintf("Remove%s deletes the value from the specified index", titleName),
			Args:    []*defs.FunctionVarDef{{"index", "int"}},
		},
	}...)
	tmpVar := fmt.Sprintf("tmp%sSlice", titleName)
	var b bytes.Buffer
	b.WriteString("// Begin generation by generateNonFunctionalAnyDefinition\n")
	b.WriteString(fmt.Sprintf("if k == \"%s\" {\n", t.Name))
	b.WriteString(fmt.Sprintf("if %s, ok := v.([]interface{}); ok {\n", tmpVar))
	b.WriteString(fmt.Sprintf("t.%s = %s\n", anyMember.Name, tmpVar))
	b.WriteString("handled = true\n")
	b.WriteString("} else {\n")
	b.WriteString(fmt.Sprintf("t.%s = []interface{}{v}\n", anyMember.Name))
	b.WriteString("handled = true\n")
	b.WriteString("}\n")
	b.WriteString("}\n")
	b.WriteString("// End generation by generateNonFunctionalAnyDefinition\n")
	d = b.String()
	var bs bytes.Buffer
	bs.WriteString("// Begin generation by generateNonFunctionalAnyDefinition\n")
	bs.WriteString(fmt.Sprintf("if t.%s != nil {\n", anyMember.Name))
	bs.WriteString(fmt.Sprintf("if len(t.%s) == 1 {\n", anyMember.Name))
	bs.WriteString(fmt.Sprintf("m[\"%s\"] = t.%s[0]\n", t.Name, anyMember.Name))
	bs.WriteString("} else {\n")
	bs.WriteString(fmt.Sprintf("m[\"%s\"] = t.%s\n", t.Name, anyMember.Name))
	bs.WriteString("}\n")
	bs.WriteString("}\n")
	bs.WriteString("// End generation by generateNonFunctionalAnyDefinition\n")
	s = bs.String()
	return
}

func generateSingleTypeDefinitions(t *defs.PropertyType, this *defs.StructDef, i *defs.InterfaceDef) (fd []*defs.FunctionDef, sd []*defs.StructDef, s, d string) {
	if t.Functional {
		return generateFunctionalSingleTypeDefinition(t, this, i)
	} else {
		return generateNonFunctionalSingleTypeDefinition(t, this, i)
	}
}

func generateFunctionalSingleTypeDefinition(t *defs.PropertyType, this *defs.StructDef, i *defs.InterfaceDef) (fd []*defs.FunctionDef, sd []*defs.StructDef, s, d string) {
	member := &defs.StructMember{
		Name:    cleanName(t.Name),
		Type:    Type(t.Range[0]),
		Comment: fmt.Sprintf("The functional '%s' value holds a single type and a single value", t.Name),
	}
	titleName := strings.Title(t.Name)
	isPtrType := isPtrType(member.Type)
	isIRI := isIRIType(member.Type)
	returnType := deref(member.Type)
	if isIRI {
		returnType = member.Type
	}
	this.M = append(this.M, member)
	this.F = append(this.F, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("Has%s", titleName),
		Comment: fmt.Sprintf("Has%s determines whether the call to Get%s is safe", titleName, titleName),
		P:       this,
		Return:  []*defs.FunctionVarDef{{"ok", "bool"}},
		Body: func() string {
			return fmt.Sprintf("return t.%s != nil\n", member.Name)
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("Get%s", titleName),
		Comment: fmt.Sprintf("Get%s returns the value for %s", titleName, t.Name),
		P:       this,
		Return:  []*defs.FunctionVarDef{{"v", returnType}},
		Body: func() string {
			var b bytes.Buffer
			b.WriteString(fmt.Sprintf("return "))
			if isPtrType && !isIRI {
				b.WriteString("*")
			}
			b.WriteString(fmt.Sprintf("t.%s\n", member.Name))
			return b.String()
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("Set%s", titleName),
		Comment: fmt.Sprintf("Set%s sets the value of %s", titleName, t.Name),
		P:       this,
		Args:    []*defs.FunctionVarDef{{"v", returnType}},
		Body: func() string {
			var b bytes.Buffer
			b.WriteString(fmt.Sprintf("t.%s = ", member.Name))
			if isPtrType && !isIRI {
				b.WriteString("&")
			}
			b.WriteString("v\n")
			return b.String()
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("HasUnknown%s", titleName),
		Comment: fmt.Sprintf("HasUnknown%s determines whether the call to GetUnknown%s is safe", titleName, titleName),
		P:       this,
		Return:  []*defs.FunctionVarDef{{"ok", "bool"}},
		Body: func() string {
			return fmt.Sprintf("return t.unknown_ != nil && t.unknown_[\"%s\"] != nil\n", t.Name)
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("GetUnknown%s", titleName),
		Comment: fmt.Sprintf("GetUnknown%s returns the unknown value for %s", titleName, t.Name),
		P:       this,
		Return:  []*defs.FunctionVarDef{{"v", "interface{}"}},
		Body: func() string {
			var b bytes.Buffer
			b.WriteString(fmt.Sprintf("return t.unknown_[\"%s\"]\n", t.Name))
			return b.String()
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("SetUnknown%s", titleName),
		Comment: fmt.Sprintf("SetUnknown%s sets the unknown value of %s", titleName, t.Name),
		P:       this,
		Args:    []*defs.FunctionVarDef{{"i", "interface{}"}},
		Body: func() string {
			var b bytes.Buffer
			b.WriteString("if t.unknown_ == nil {\n")
			b.WriteString("t.unknown_ = make(map[string]interface{})")
			b.WriteString("}\n")
			b.WriteString(fmt.Sprintf("t.unknown_[\"%s\"] = i\n", t.Name))
			return b.String()
		},
	})
	i.F = append(i.F, []*defs.FunctionDef{
		{
			Name:    fmt.Sprintf("Has%s", titleName),
			Comment: fmt.Sprintf("Has%s determines whether the call to Get%s is safe", titleName, titleName),
			Return:  []*defs.FunctionVarDef{{"ok", "bool"}},
		},
		{
			Name:    fmt.Sprintf("Get%s", titleName),
			Comment: fmt.Sprintf("Get%s returns the value for %s", titleName, t.Name),
			Return:  []*defs.FunctionVarDef{{"v", returnType}},
		},
		{
			Name:    fmt.Sprintf("Set%s", titleName),
			Comment: fmt.Sprintf("Set%s sets the value of %s", titleName, t.Name),
			Args:    []*defs.FunctionVarDef{{"v", returnType}},
		},
		{
			Name:    fmt.Sprintf("HasUnknown%s", titleName),
			Comment: fmt.Sprintf("HasUnknown%s determines whether the call to GetUnknown%s is safe", titleName, titleName),
			Return:  []*defs.FunctionVarDef{{"ok", "bool"}},
		},
		{
			Name:    fmt.Sprintf("GetUnknown%s", titleName),
			Comment: fmt.Sprintf("GetUnknown%s returns the unknown value for %s", titleName, t.Name),
			Return:  []*defs.FunctionVarDef{{"v", "interface{}"}},
		},
		{
			Name:    fmt.Sprintf("SetUnknown%s", titleName),
			Comment: fmt.Sprintf("SetUnknown%s sets the unknown value of %s", titleName, t.Name),
			Args:    []*defs.FunctionVarDef{{"i", "interface{}"}},
		},
	}...)
	d = Deserialize(t.Range[0], t.Name, member.Name, false)
	s = Serialize(t.Range[0], t.Name, member.Name, false)
	return
}

func generateNonFunctionalSingleTypeDefinition(t *defs.PropertyType, this *defs.StructDef, i *defs.InterfaceDef) (fd []*defs.FunctionDef, sd []*defs.StructDef, s, d string) {
	isIRI := isIRIType(Type(t.Range[0]))
	memberType := deref(Type(t.Range[0]))
	returnType := memberType
	if isIRI {
		memberType = Type(t.Range[0])
		returnType = memberType
	}
	member := &defs.StructMember{
		Name:    cleanName(t.Name),
		Type:    fmt.Sprintf("[]%s", memberType),
		Comment: fmt.Sprintf("The '%s' value holds a single type and any number of values", t.Name),
	}
	titleName := strings.Title(t.Name)
	this.M = append(this.M, member)
	this.F = append(this.F, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("%sLen", titleName),
		Comment: fmt.Sprintf("%sLen determines the number of elements able to be used for the Get%s and Remove%s functions", titleName, titleName, titleName),
		P:       this,
		Return:  []*defs.FunctionVarDef{{"l", "int"}},
		Body: func() string {
			return fmt.Sprintf("return len(t.%s)\n", member.Name)
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("Get%s", titleName),
		Comment: fmt.Sprintf("Get%s returns the value for the specified index", titleName),
		P:       this,
		Args:    []*defs.FunctionVarDef{{"index", "int"}},
		Return:  []*defs.FunctionVarDef{{"v", returnType}},
		Body: func() string {
			var b bytes.Buffer
			b.WriteString(fmt.Sprintf("return t.%s[index]\n", member.Name))
			return b.String()
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("Append%s", titleName),
		Comment: fmt.Sprintf("Append%s adds a value to the back of %s", titleName, t.Name),
		P:       this,
		Args:    []*defs.FunctionVarDef{{"v", returnType}},
		Body: func() string {
			var b bytes.Buffer
			b.WriteString(fmt.Sprintf("t.%s = append(t.%s, v)\n", member.Name, member.Name))
			return b.String()
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("Prepend%s", titleName),
		Comment: fmt.Sprintf("Prepend%s adds a value to the front of %s", titleName, t.Name),
		P:       this,
		Args:    []*defs.FunctionVarDef{{"v", returnType}},
		Body: func() string {
			var b bytes.Buffer
			b.WriteString(fmt.Sprintf("t.%s = append(%s{v}, t.%s...)\n", member.Name, member.Type, member.Name))
			return b.String()
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("Remove%s", titleName),
		Comment: fmt.Sprintf("Remove%s deletes the value from the specified index", titleName),
		P:       this,
		Args:    []*defs.FunctionVarDef{{"index", "int"}},
		Body: func() string {
			sliceVar := fmt.Sprintf("t.%s", member.Name)
			var b bytes.Buffer
			b.WriteString(fmt.Sprintf("copy(%s[index:], %s[index+1:])\n", sliceVar, sliceVar))
			if isPtrType(deSlice(member.Type)) {
				b.WriteString(fmt.Sprintf("%s[len(%s)-1] = nil\n", sliceVar, sliceVar))
			}
			b.WriteString(fmt.Sprintf("%s = %s[:len(%s)-1]\n", sliceVar, sliceVar, sliceVar))
			return b.String()
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("HasUnknown%s", titleName),
		Comment: fmt.Sprintf("HasUnknown%s determines whether the call to GetUnknown%s is safe", titleName, titleName),
		P:       this,
		Return:  []*defs.FunctionVarDef{{"ok", "bool"}},
		Body: func() string {
			return fmt.Sprintf("return t.unknown_ != nil && t.unknown_[\"%s\"] != nil\n", t.Name)
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("GetUnknown%s", titleName),
		Comment: fmt.Sprintf("GetUnknown%s returns the unknown value for %s", titleName, t.Name),
		P:       this,
		Return:  []*defs.FunctionVarDef{{"v", "interface{}"}},
		Body: func() string {
			var b bytes.Buffer
			b.WriteString(fmt.Sprintf("return t.unknown_[\"%s\"]\n", t.Name))
			return b.String()
		},
	}, &defs.MemberFunctionDef{
		Name:    fmt.Sprintf("SetUnknown%s", titleName),
		Comment: fmt.Sprintf("SetUnknown%s sets the unknown value of %s", titleName, t.Name),
		P:       this,
		Args:    []*defs.FunctionVarDef{{"i", "interface{}"}},
		Body: func() string {
			var b bytes.Buffer
			b.WriteString("if t.unknown_ == nil {\n")
			b.WriteString("t.unknown_ = make(map[string]interface{})")
			b.WriteString("}\n")
			b.WriteString(fmt.Sprintf("t.unknown_[\"%s\"] = i\n", t.Name))
			return b.String()
		},
	})
	i.F = append(i.F, []*defs.FunctionDef{
		{
			Name:    fmt.Sprintf("%sLen", titleName),
			Comment: fmt.Sprintf("%sLen determines the number of elements able to be used for the Get%s and Remove%s functions", titleName, titleName, titleName),
			Return:  []*defs.FunctionVarDef{{"l", "int"}},
		},
		{
			Name:    fmt.Sprintf("Get%s", titleName),
			Comment: fmt.Sprintf("Get%s returns the value for the specified index", titleName),
			Args:    []*defs.FunctionVarDef{{"index", "int"}},
			Return:  []*defs.FunctionVarDef{{"v", returnType}},
		},
		{
			Name:    fmt.Sprintf("Append%s", titleName),
			Comment: fmt.Sprintf("Append%s adds a value to the back of %s", titleName, t.Name),
			Args:    []*defs.FunctionVarDef{{"v", returnType}},
		},
		{
			Name:    fmt.Sprintf("Prepend%s", titleName),
			Comment: fmt.Sprintf("Prepend%s adds a value to the front of %s", titleName, t.Name),
			Args:    []*defs.FunctionVarDef{{"v", returnType}},
		},
		{
			Name:    fmt.Sprintf("Remove%s", titleName),
			Comment: fmt.Sprintf("Remove%s deletes the value from the specified index", titleName),
			Args:    []*defs.FunctionVarDef{{"index", "int"}},
		},
		{
			Name:    fmt.Sprintf("HasUnknown%s", titleName),
			Comment: fmt.Sprintf("HasUnknown%s determines whether the call to GetUnknown%s is safe", titleName, titleName),
			Return:  []*defs.FunctionVarDef{{"ok", "bool"}},
		},
		{
			Name:    fmt.Sprintf("GetUnknown%s", titleName),
			Comment: fmt.Sprintf("GetUnknown%s returns the unknown value for %s", titleName, t.Name),
			Return:  []*defs.FunctionVarDef{{"v", "interface{}"}},
		},
		{
			Name:    fmt.Sprintf("SetUnknown%s", titleName),
			Comment: fmt.Sprintf("SetUnknown%s sets the unknown value of %s", titleName, t.Name),
			Args:    []*defs.FunctionVarDef{{"i", "interface{}"}},
		},
	}...)
	d = Deserialize(t.Range[0], t.Name, member.Name, true)
	s = Serialize(t.Range[0], t.Name, member.Name, true)
	return
}

func generateMultiTypeDefinitions(t *defs.PropertyType, this *defs.StructDef, i *defs.InterfaceDef, m map[*defs.PropertyType]*intermedDef) (fd []*defs.FunctionDef, sd []*defs.StructDef, s, d string) {
	if t.Functional {
		return generateFunctionalMultiTypeDefinition(t, this, i, m)
	} else {
		return generateNonFunctionalMultiTypeDefinition(t, this, i, m)
	}
}

func generateFunctionalMultiTypeDefinition(t *defs.PropertyType, this *defs.StructDef, i *defs.InterfaceDef, intermedMap map[*defs.PropertyType]*intermedDef) (fd []*defs.FunctionDef, sd []*defs.StructDef, s, d string) {
	titleName := strings.Title(t.Name)
	var intermed *defs.StructDef
	if i, ok := intermedMap[t]; ok {
		intermed = i.S
	} else {
		double := false
		for k := range intermedMap {
			if k.Name == t.Name {
				intermedMap[k] = generateIntermediateTypeDefinition([]*defs.PropertyType{k, t})
				intermed = intermedMap[k].S
				double = true
				break
			}
		}
		if !double {
			intermedMap[t] = generateIntermediateTypeDefinition([]*defs.PropertyType{t})
			intermed = intermedMap[t].S
		}
	}
	thisIntermed := &defs.StructMember{
		Name:    cleanName(t.Name),
		Type:    "*" + intermed.Typename,
		Comment: fmt.Sprintf("The functional '%s' value could have multiple types, but only a single value", t.Name),
	}
	this.M = append(this.M, thisIntermed)
	for idx, r := range t.Range {
		r := r
		memberName := cleanName(Name(r))
		memberType := Type(r)
		isIRI := isIRIType(memberType)
		retKind := deref(memberType)
		if isIRI {
			retKind = memberType
		}
		typeExtensionName := strings.Title(Name(r))
		if defs.IsOnlyOtherPropertyBesidesIRI(idx, t.Range) {
			typeExtensionName = ""
		}
		this.F = append(this.F, []*defs.MemberFunctionDef{
			{
				Name:    fmt.Sprintf("Is%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Is%s%s determines whether the call to Get%s%s is safe", titleName, typeExtensionName, titleName, typeExtensionName),
				P:       this,
				Return:  []*defs.FunctionVarDef{{"ok", "bool"}},
				Body: func() string {
					return fmt.Sprintf("return t.%s != nil && t.%s.%s != nil\n", thisIntermed.Name, thisIntermed.Name, memberName)
				},
			},
			{
				Name:    fmt.Sprintf("Get%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Get%s%s returns the value safely if Is%s%s returned true", titleName, typeExtensionName, titleName, typeExtensionName),
				P:       this,
				Return:  []*defs.FunctionVarDef{{"v", retKind}},
				Body: func() string {
					var b bytes.Buffer
					b.WriteString("return ")
					if isPtrType(Type(r)) && !isIRI {
						b.WriteString("*")
					}
					b.WriteString(fmt.Sprintf("t.%s.%s\n", thisIntermed.Name, memberName))
					return b.String()
				},
			},
			{
				Name:    fmt.Sprintf("Set%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Set%s%s sets the value of %s to be of %s type", titleName, typeExtensionName, cleanName(t.Name), retKind),
				P:       this,
				Args:    []*defs.FunctionVarDef{{"v", retKind}},
				Body: func() string {
					var b bytes.Buffer
					b.WriteString(fmt.Sprintf("t.%s = &%s{%s:", thisIntermed.Name, intermed.Typename, memberName))
					if isPtrType(Type(r)) && !isIRI {
						b.WriteString("&")
					}
					b.WriteString("v}\n")
					return b.String()
				},
			},
		}...)
		i.F = append(i.F, []*defs.FunctionDef{
			{
				Name:    fmt.Sprintf("Is%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Is%s%s determines whether the call to Get%s%s is safe", titleName, typeExtensionName, titleName, typeExtensionName),
				Return:  []*defs.FunctionVarDef{{"ok", "bool"}},
			},
			{
				Name:    fmt.Sprintf("Get%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Get%s%s returns the value safely if Is%s%s returned true", titleName, typeExtensionName, titleName, typeExtensionName),
				Return:  []*defs.FunctionVarDef{{"v", retKind}},
			},
			{
				Name:    fmt.Sprintf("Set%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Set%s%s sets the value of %s to be of %s type", titleName, typeExtensionName, cleanName(t.Name), retKind),
				Args:    []*defs.FunctionVarDef{{"v", retKind}},
			},
		}...)
	}
	var b bytes.Buffer
	b.WriteString("// Begin generation by generateFunctionalMultiTypeDefinition\n")
	b.WriteString(fmt.Sprintf("if k == \"%s\" {\n", t.Name))
	b.WriteString(fmt.Sprintf("t.%s, err = deserialize%s(v)\n", thisIntermed.Name, strings.Title(intermed.Typename)))
	b.WriteString("if err != nil {\nreturn err\n}\n")
	b.WriteString("handled = true\n")
	b.WriteString("}\n")
	b.WriteString("// End generation by generateFunctionalMultiTypeDefinition\n")
	d = b.String()
	var bs bytes.Buffer
	bs.WriteString("// Begin generation by generateFunctionalMultiTypeDefinition\n")
	bs.WriteString(fmt.Sprintf("if t.%s != nil {\n", thisIntermed.Name))
	bs.WriteString(fmt.Sprintf("if v, err := serialize%s(t.%s); err == nil {\n", strings.Title(intermed.Typename), thisIntermed.Name))
	bs.WriteString(fmt.Sprintf("m[\"%s\"] = v\n", t.Name))
	bs.WriteString("} else {\n")
	bs.WriteString("return m, err\n")
	bs.WriteString("}\n")
	bs.WriteString("}\n")
	bs.WriteString("// End generation by generateFunctionalMultiTypeDefinition\n")
	s = bs.String()
	generateUnknownFunctions(t, this, thisIntermed, intermed)
	return
}

func generateNonFunctionalMultiTypeDefinition(t *defs.PropertyType, this *defs.StructDef, i *defs.InterfaceDef, intermedMap map[*defs.PropertyType]*intermedDef) (fd []*defs.FunctionDef, sd []*defs.StructDef, s, d string) {
	titleName := strings.Title(t.Name)
	var intermed *defs.StructDef
	if i, ok := intermedMap[t]; ok {
		intermed = i.S
	} else {
		double := false
		for k := range intermedMap {
			if k.Name == t.Name {
				intermedMap[k] = generateIntermediateTypeDefinition([]*defs.PropertyType{k, t})
				intermed = intermedMap[k].S
				double = true
				break
			}
		}
		if !double {
			intermedMap[t] = generateIntermediateTypeDefinition([]*defs.PropertyType{t})
			intermed = intermedMap[t].S
		}
	}
	thisIntermed := &defs.StructMember{
		Name:    cleanName(t.Name),
		Type:    fmt.Sprintf("[]*%s", intermed.Typename),
		Comment: fmt.Sprintf("The '%s' value could have multiple types and values", t.Name),
	}
	this.M = append(this.M, thisIntermed)
	for idx, r := range t.Range {
		r := r
		memberName := cleanName(Name(r))
		memberType := Type(r)
		isIRI := isIRIType(memberType)
		retKind := deref(memberType)
		if isIRI {
			retKind = memberType
		}
		typeExtensionName := strings.Title(Name(r))
		if defs.IsOnlyOtherPropertyBesidesIRI(idx, t.Range) {
			typeExtensionName = ""
		}
		if idx == 0 {
			this.F = append(this.F, &defs.MemberFunctionDef{
				Name:    fmt.Sprintf("%sLen", titleName),
				Comment: fmt.Sprintf("%sLen determines the number of elements able to be used for the Is%s%s, Get%s%s, and Remove%s%s functions", titleName, titleName, typeExtensionName, titleName, typeExtensionName, titleName, typeExtensionName),
				P:       this,
				Return:  []*defs.FunctionVarDef{{"l", "int"}},
				Body: func() string {
					return fmt.Sprintf("return len(t.%s)\n", thisIntermed.Name)
				},
			})
			i.F = append(i.F, &defs.FunctionDef{
				Name:    fmt.Sprintf("%sLen", titleName),
				Comment: fmt.Sprintf("%sLen determines the number of elements able to be used for the Is%s%s, Get%s%s, and Remove%s%s functions", titleName, titleName, typeExtensionName, titleName, typeExtensionName, titleName, typeExtensionName),
				Return:  []*defs.FunctionVarDef{{"l", "int"}},
			})
		}
		this.F = append(this.F, []*defs.MemberFunctionDef{
			{
				Name:    fmt.Sprintf("Is%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Is%s%s determines whether the call to Get%s%s is safe for the specified index", titleName, typeExtensionName, titleName, typeExtensionName),
				P:       this,
				Args:    []*defs.FunctionVarDef{{"index", "int"}},
				Return:  []*defs.FunctionVarDef{{"ok", "bool"}},
				Body: func() string {
					return fmt.Sprintf("return t.%s[index].%s != nil\n", thisIntermed.Name, memberName)
				},
			},
			{
				Name:    fmt.Sprintf("Get%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Get%s%s returns the value safely if Is%s%s returned true for the specified index", titleName, typeExtensionName, titleName, typeExtensionName),
				P:       this,
				Args:    []*defs.FunctionVarDef{{"index", "int"}},
				Return:  []*defs.FunctionVarDef{{"v", retKind}},
				Body: func() string {
					var b bytes.Buffer
					b.WriteString("return ")
					if isPtrType(Type(r)) && !isIRI {
						b.WriteString("*")
					}
					b.WriteString(fmt.Sprintf("t.%s[index].%s\n", thisIntermed.Name, memberName))
					return b.String()
				},
			},
			{
				Name:    fmt.Sprintf("Append%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Append%s%s adds to the back of %s a %s type", titleName, typeExtensionName, cleanName(t.Name), retKind),
				P:       this,
				Args:    []*defs.FunctionVarDef{{"v", retKind}},
				Body: func() string {
					var b bytes.Buffer
					b.WriteString(fmt.Sprintf("t.%s = append(t.%s, &%s{%s:", thisIntermed.Name, thisIntermed.Name, intermed.Typename, memberName))
					if isPtrType(Type(r)) && !isIRI {
						b.WriteString("&")
					}
					b.WriteString("v})\n")
					return b.String()
				},
			},
			{
				Name:    fmt.Sprintf("Prepend%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Prepend%s%s adds to the front of %s a %s type", titleName, typeExtensionName, cleanName(t.Name), retKind),
				P:       this,
				Args:    []*defs.FunctionVarDef{{"v", retKind}},
				Body: func() string {
					var b bytes.Buffer
					b.WriteString(fmt.Sprintf("t.%s = append([]*%s{&%s{%s:", thisIntermed.Name, intermed.Typename, intermed.Typename, memberName))
					if isPtrType(Type(r)) && !isIRI {
						b.WriteString("&")
					}
					b.WriteString(fmt.Sprintf("v}}, t.%s...)\n", thisIntermed.Name))
					return b.String()
				},
			},
			{
				Name:    fmt.Sprintf("Remove%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Remove%s%s deletes the value from the specified index", titleName, typeExtensionName),
				P:       this,
				Args:    []*defs.FunctionVarDef{{"index", "int"}},
				Body: func() string {
					sliceVar := fmt.Sprintf("t.%s", thisIntermed.Name)
					var b bytes.Buffer
					b.WriteString(fmt.Sprintf("copy(%s[index:], %s[index+1:])\n", sliceVar, sliceVar))
					b.WriteString(fmt.Sprintf("%s[len(%s)-1] = %s\n", sliceVar, sliceVar, "nil"))
					b.WriteString(fmt.Sprintf("%s = %s[:len(%s)-1]\n", sliceVar, sliceVar, sliceVar))
					return b.String()
				},
			},
		}...)
		i.F = append(i.F, []*defs.FunctionDef{
			{
				Name:    fmt.Sprintf("Is%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Is%s%s determines whether the call to Get%s%s is safe for the specified index", titleName, typeExtensionName, titleName, typeExtensionName),
				Args:    []*defs.FunctionVarDef{{"index", "int"}},
				Return:  []*defs.FunctionVarDef{{"ok", "bool"}},
			},
			{
				Name:    fmt.Sprintf("Get%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Get%s%s returns the value safely if Is%s%s returned true for the specified index", titleName, typeExtensionName, titleName, typeExtensionName),
				Args:    []*defs.FunctionVarDef{{"index", "int"}},
				Return:  []*defs.FunctionVarDef{{"v", retKind}},
			},
			{
				Name:    fmt.Sprintf("Append%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Append%s%s adds to the back of %s a %s type", titleName, typeExtensionName, cleanName(t.Name), retKind),
				Args:    []*defs.FunctionVarDef{{"v", retKind}},
			},
			{
				Name:    fmt.Sprintf("Prepend%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Prepend%s%s adds to the front of %s a %s type", titleName, typeExtensionName, cleanName(t.Name), retKind),
				Args:    []*defs.FunctionVarDef{{"v", retKind}},
			},
			{
				Name:    fmt.Sprintf("Remove%s%s", titleName, typeExtensionName),
				Comment: fmt.Sprintf("Remove%s%s deletes the value from the specified index", titleName, typeExtensionName),
				Args:    []*defs.FunctionVarDef{{"index", "int"}},
			},
		}...)
	}
	var b bytes.Buffer
	b.WriteString("// Begin generation by generateNonFunctionalMultiTypeDefinition\n")
	b.WriteString(fmt.Sprintf("if k == \"%s\" {\n", t.Name))
	b.WriteString("if tmpMap, ok := v.(map[string]interface{}); ok {\n")
	b.WriteString(fmt.Sprintf("tmp, err := deserialize%s(tmpMap)\n", strings.Title(intermed.Typename)))
	b.WriteString("if err != nil {\nreturn err\n}\n")
	b.WriteString(fmt.Sprintf("t.%s = []*%s{tmp}\n", thisIntermed.Name, intermed.Typename))
	b.WriteString("handled = true\n")
	b.WriteString("} else if tmpSlice, ok := v.([]interface{}); ok {\n")
	b.WriteString(fmt.Sprintf("t.%s, err = deserializeSlice%s(tmpSlice)\n", thisIntermed.Name, strings.Title(intermed.Typename)))
	b.WriteString("if err != nil {\nreturn err\n}\n")
	b.WriteString("handled = true\n")
	b.WriteString("} else {\n")
	b.WriteString(fmt.Sprintf("tmp := &%s{}\n", intermed.Typename))
	b.WriteString("err = tmp.Deserialize(v)\n")
	b.WriteString("if err != nil {\nreturn err\n}\n")
	b.WriteString(fmt.Sprintf("t.%s = []*%s{tmp}\n", thisIntermed.Name, intermed.Typename))
	b.WriteString("handled = true\n")
	b.WriteString("}\n")
	b.WriteString("}\n")
	b.WriteString("// End generation by generateNonFunctionalMultiTypeDefinition\n")
	d = b.String()
	var bs bytes.Buffer
	bs.WriteString("// Begin generation by generateNonFunctionalMultiTypeDefinition\n")
	bs.WriteString(fmt.Sprintf("if v, err := serializeSlice%s(t.%s); err == nil && v != nil {\n", strings.Title(intermed.Typename), thisIntermed.Name))
	bs.WriteString("if len(v) == 1 {\n")
	bs.WriteString(fmt.Sprintf("m[\"%s\"] = v[0]\n", t.Name))
	bs.WriteString("} else {\n")
	bs.WriteString(fmt.Sprintf("m[\"%s\"] = v\n", t.Name))
	bs.WriteString("}\n")
	bs.WriteString("} else if err != nil {\n")
	bs.WriteString("return m, err\n")
	bs.WriteString("}\n")
	bs.WriteString("// End generation by generateNonFunctionalMultiTypeDefinition\n")
	s = bs.String()
	generateUnknownFunctions(t, this, thisIntermed, intermed)
	return
}

type intermedDef struct {
	Name string
	S    *defs.StructDef
	F    []*defs.FunctionDef
}

func generateIntermediateTypeDefinition(types []*defs.PropertyType) (d *intermedDef) {
	t := types[0]
	d = &intermedDef{}
	d.Name = fmt.Sprintf("%sIntermediateType", cleanName(t.Name))
	d.S = &defs.StructDef{
		Typename: d.Name,
		Comment:  fmt.Sprintf("%sIntermediateType will only have one of its values set at most", cleanName(t.Name)),
		M:        []*defs.StructMember{{"unknown_", "interface{}", "An unknown value."}},
	}
	d.S.F = []*defs.MemberFunctionDef{
		{
			Name:    "Deserialize",
			Comment: "Deserialize takes an interface{} and attempts to create a valid intermediate type.",
			P:       d.S,
			Args:    []*defs.FunctionVarDef{{"i", "interface{}"}},
			Return:  []*defs.FunctionVarDef{{"err", "error"}},
			Body: func() string {
				bHasType := false
				for _, t := range types {
					for _, r := range t.Range {
						if r.T != nil {
							bHasType = true
							break
						}
					}
				}
				var b bytes.Buffer
				b.WriteString("matched := false\n")
				b.WriteString("if m, ok := i.(map[string]interface{}); ok {\n")
				if bHasType {
					b.WriteString("if tv, ok := m[\"type\"]; ok {\n")
					b.WriteString("var types []string\n")
					b.WriteString("if tvs, ok := tv.([]interface{}); ok {\n")
					b.WriteString("for _, tvi := range tvs {\n")
					b.WriteString("if typeString, ok := tvi.(string); ok {\n")
					b.WriteString("types = append(types, typeString)\n")
					b.WriteString("}\n")
					b.WriteString("}\n")
					b.WriteString("} else if typeString, ok := tv.(string); ok {\n")
					b.WriteString("types = append(types, typeString)\n")
					b.WriteString("}\n")
					alreadyMember := make(map[string]bool)
					for _, t := range types {
						for _, r := range t.Range {
							kind := cleanName(Name(r))
							if alreadyMember[kind] {
								continue
							}
							alreadyMember[kind] = true
							if r.T != nil {
								var resolve string
								if isALinkType(r.T) {
									resolve = resolveLinkName
								} else if isAnObjectType(r.T) {
									resolve = resolveObjectName
								} else {
									panic("Unknown resolution function: not object and not link")
								}
								b.WriteString("if !matched {\n")
								b.WriteString("for _, kind := range types {\n")
								b.WriteString(fmt.Sprintf("if t.%s, ok = %s(kind).(%sType); t.%s != nil && ok {\n", cleanName(Name(r)), resolve, Name(r), cleanName(Name(r))))
								b.WriteString(fmt.Sprintf("err = t.%s.Deserialize(m)\n", cleanName(Name(r))))
								b.WriteString("matched = true\n")
								b.WriteString("break\n")
								b.WriteString("}\n")
								b.WriteString("}\n")
								b.WriteString("}\n")
							}
						}
					}
					b.WriteString("} else {\n")
					b.WriteString("t.unknown_ = m\n")
					b.WriteString("}\n")
				} else {
					b.WriteString("err = fmt.Errorf(\"Given map but nothing to do with it for this type: %v\", m)")
				}
				b.WriteString("} else if i != nil {\n")
				alreadyMember := make(map[string]bool)
				for _, t := range types {
					for _, r := range t.Range {
						kind := cleanName(Name(r))
						if alreadyMember[kind] {
							continue
						}
						alreadyMember[kind] = true
						if r.V != nil {
							b.WriteString("if !matched {\n")
							b.WriteString(fmt.Sprintf("t.%s, err = %s(i)\n", cleanName(Name(r)), r.V.DeserializeFn.Name))
							b.WriteString("if err != nil {\n")
							b.WriteString(fmt.Sprintf("t.%s = nil\n", cleanName(Name(r))))
							b.WriteString("} else {\n")
							b.WriteString("matched = true\n")
							b.WriteString("}\n")
							b.WriteString("}\n")
						}
					}
				}
				b.WriteString("}\n")
				b.WriteString("if !matched {\n")
				b.WriteString(fmt.Sprintf("t.unknown_ = %s(i)\n", unknownValueDeserializeFnName))
				b.WriteString("}\n")
				b.WriteString("return\n")
				return b.String()
			},
		},
		{
			Name:    "Serialize",
			Comment: "Serialize turns this object into an interface{}.",
			P:       d.S,
			Return:  []*defs.FunctionVarDef{{"i", "interface{}"}, {"err", "error"}},
			Body: func() string {
				var b bytes.Buffer
				alreadyMember := make(map[string]bool)
				for _, t := range types {
					for _, r := range t.Range {
						kind := cleanName(Name(r))
						if alreadyMember[kind] {
							continue
						}
						alreadyMember[kind] = true
						isIRI := isIRIType(Type(r))
						deref := "*"
						if isIRI {
							deref = ""
						}
						b.WriteString(fmt.Sprintf("if t.%s != nil {\n", cleanName(Name(r))))
						if r.T != nil {
							b.WriteString(fmt.Sprintf("i, err = t.%s.Serialize()\n", cleanName(Name(r))))
							b.WriteString("return\n")
						} else if r.V != nil {
							b.WriteString(fmt.Sprintf("i = %s(%st.%s)\n", r.V.SerializeFn.Name, deref, cleanName(Name(r))))
							b.WriteString("return\n")
						} else {
							b.WriteString(fmt.Sprintf("i = t.%s\n", cleanName(Name(r))))
							b.WriteString("return\n")
						}
						b.WriteString("}\n")
					}
				}
				b.WriteString(fmt.Sprintf("i = %s(t.unknown_)\n", unknownValueSerializeFnName))
				b.WriteString("return")
				return b.String()
			},
		},
	}
	d.F = append(d.F, []*defs.FunctionDef{
		{
			Name:    fmt.Sprintf("deserialize%s", strings.Title(d.S.Typename)),
			Comment: fmt.Sprintf("deserialize%s will accept a map to create a %s", d.S.Typename, d.S.Typename),
			Args:    []*defs.FunctionVarDef{{"in", "interface{}"}},
			Return:  []*defs.FunctionVarDef{{"t", "*" + d.S.Typename}, {"err", "error"}},
			Body: func() string {
				var b bytes.Buffer
				b.WriteString(fmt.Sprintf("tmp := &%s{}\n", d.S.Typename))
				b.WriteString("err = tmp.Deserialize(in)\n")
				b.WriteString("return tmp, err\n")
				return b.String()
			},
		},
		{
			Name:    fmt.Sprintf("deserializeSlice%s", strings.Title(d.S.Typename)),
			Comment: fmt.Sprintf("deserializeSlice %s will accept a slice to create a slice of %s", d.S.Typename, d.S.Typename),
			Args:    []*defs.FunctionVarDef{{"in", "[]interface{}"}},
			Return:  []*defs.FunctionVarDef{{"t", "[]*" + d.S.Typename}, {"err", "error"}},
			Body: func() string {
				var b bytes.Buffer
				b.WriteString("for _, i := range in {\n")
				b.WriteString(fmt.Sprintf("tmp := &%s{}\n", d.S.Typename))
				b.WriteString("err = tmp.Deserialize(i)\n")
				b.WriteString("if err != nil {\nreturn\n}\n")
				b.WriteString("t = append(t, tmp)\n")
				b.WriteString("}\n")
				b.WriteString("return\n")
				return b.String()
			},
		},
		{
			Name:    fmt.Sprintf("serialize%s", strings.Title(d.S.Typename)),
			Comment: fmt.Sprintf("serialize%s will accept a %s to create a map", d.S.Typename, d.S.Typename),
			Args:    []*defs.FunctionVarDef{{"t", "*" + d.S.Typename}},
			Return:  []*defs.FunctionVarDef{{"i", "interface{}"}, {"err", "error"}},
			Body: func() string {
				var b bytes.Buffer
				b.WriteString("i, err = t.Serialize()\n")
				b.WriteString("return\n")
				return b.String()
			},
		},
		{
			Name:    fmt.Sprintf("serializeSlice%s", strings.Title(d.S.Typename)),
			Comment: fmt.Sprintf("serializeSlice%s will accept a slice of %s to create a slice result", d.S.Typename, d.S.Typename),
			Args:    []*defs.FunctionVarDef{{"s", "[]*" + d.S.Typename}},
			Return:  []*defs.FunctionVarDef{{"out", "[]interface{}"}, {"err", "error"}},
			Body: func() string {
				var b bytes.Buffer
				b.WriteString("for _, t := range s {\n")
				b.WriteString("v, err := t.Serialize()\n")
				b.WriteString("if err != nil {\nreturn nil, err\n}\n")
				b.WriteString("out = append(out, v)\n")
				b.WriteString("}\n")
				b.WriteString("return\n")
				return b.String()
			},
		},
	}...)
	alreadyMember := make(map[string]bool)
	for _, t := range types {
		for _, r := range t.Range {
			kind := cleanName(Name(r))
			if alreadyMember[kind] {
				continue
			}
			alreadyMember[kind] = true
			d.S.M = append(d.S.M, &defs.StructMember{
				Name:    kind,
				Type:    Type(r),
				Comment: fmt.Sprintf("Stores possible %s type for %s property", Type(r), t.Name),
			})
		}
	}
	return
}

func generateUnknownFunctions(t *defs.PropertyType, this *defs.StructDef, thisIntermed *defs.StructMember, intermed *defs.StructDef) {
	titleName := strings.Title(t.Name)
	this.F = append(this.F, []*defs.MemberFunctionDef{
		{
			Name:    fmt.Sprintf("HasUnknown%s", titleName),
			Comment: fmt.Sprintf("HasUnknown%s determines whether the call to GetUnknown%s is safe", titleName, titleName),
			P:       this,
			Return:  []*defs.FunctionVarDef{{"ok", "bool"}},
			Body: func() string {
				var b bytes.Buffer
				b.WriteString(fmt.Sprintf("return t.%s != nil && t.%s", thisIntermed.Name, thisIntermed.Name))
				if !t.Functional {
					b.WriteString("[0]")
				}
				b.WriteString(".unknown_ != nil\n")
				return b.String()
			},
		}, &defs.MemberFunctionDef{
			Name:    fmt.Sprintf("GetUnknown%s", titleName),
			Comment: fmt.Sprintf("GetUnknown%s returns the unknown value for %s", titleName, t.Name),
			P:       this,
			Return:  []*defs.FunctionVarDef{{"v", "interface{}"}},
			Body: func() string {
				var b bytes.Buffer
				b.WriteString(fmt.Sprintf("return t.%s", thisIntermed.Name))
				if !t.Functional {
					b.WriteString("[0]")
				}
				b.WriteString(".unknown_\n")
				return b.String()
			},
		}, &defs.MemberFunctionDef{
			Name:    fmt.Sprintf("SetUnknown%s", titleName),
			Comment: fmt.Sprintf("SetUnknown%s sets the unknown value of %s", titleName, t.Name),
			P:       this,
			Args:    []*defs.FunctionVarDef{{"i", "interface{}"}},
			Body: func() string {
				var b bytes.Buffer
				b.WriteString("if t.unknown_ == nil {\n")
				b.WriteString("t.unknown_ = make(map[string]interface{})")
				b.WriteString("}\n")
				b.WriteString(fmt.Sprintf("tmp := &%s{}\n", intermed.Typename))
				b.WriteString("tmp.unknown_ = i\n")
				b.WriteString(fmt.Sprintf("t.%s = ", thisIntermed.Name))
				if t.Functional {
					b.WriteString("tmp\n")
				} else {
					b.WriteString(fmt.Sprintf("append(t.%s, tmp)\n", thisIntermed.Name))
				}
				return b.String()
			},
		},
	}...)
}

func isSingleType(t *defs.PropertyType) bool {
	return len(t.Range) == 1
}

func isAny(t *defs.PropertyType) bool {
	for _, r := range t.Range {
		if r.Any {
			return true
		}
	}
	return false
}
