{{ $data := . -}}
// Copyright © 2019 - 2020 Oscar Campos <oscar.campos@thepimpam.com>
// Copyright © 2017 - William Edwards
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

{{/* create wrapper boilerplate */}}
package {{ $data.Package }}

// ==================================================================
// This file was autogenerated by PimPam GDNative-Go binding tools
// Please do not modify this file, any change will be lost
// ==================================================================

import (
    "fmt"

    "git.alfi.li/gamelang/gdnative-go/gdnative"
)

// used for FreeFunc
var emptyFreeFunc = func(_ string) {}

{{ range $className, $class := $data.Classes -}}
// {{ $className }}Wrapper is a wrapper over {{ $className }} that will register it with in godot
type {{ $className }}Wrapper struct {
    class *{{ $className }}
}

// LookupInstance implements the gdnative.InstanceRegistry interface for {{ $className }}Wrapper
func (w *{{ $className }}Wrapper) LookupInstance(instance string) *{{ $className }}Wrapper {

    wrapper, ok := {{ $className }}Instances[instance]
    if !ok {
        return nil
    }

    return wrapper
}

// {{ $className }}Instances is an internal registry of instances for our custom types
var {{ $className }}Instances = map[string]*{{ $className }}Wrapper{}

{{ if $class.Methods -}}
// handle{{ $className }} handles calls from Godot to this instance methods
func handle{{ $className }}(object gdnative.Object, methodData, userData string, numArgs int, args []gdnative.Variant) gdnative.Variant {

    // lookup instance on registry, if it does not exists return nil
	instance, ok := {{ $className }}Instances[userData]
	if !ok {
        gdnative.Log.Warning(fmt.Sprintf("could not find instance %s on registry", userData))
		return gdnative.NewVariantNil()
	}

    // find the right method and execute it or return an empty nil value and log it
    switch methodData {
        {{ range $i, $method := $class.Methods -}}
        {{ if $method.Alias -}}
        case "{{ $method.Alias }}":
        {{ else -}}
        case "{{ $method.GodotName }}":
        {{ end -}}
            {{ range $i, $arg := $method.Arguments -}}
            {{ $arg.Name }} := args[{{ $i }}].{{ $arg.ConvertFunction }}
            {{ end -}}

            {{ if $method.HasReturns -}}
            value := instance.class.{{ $method.FunctionCallWithParams }}
            return {{ $method.NewVariantType }}
            {{ else -}}
            instance.class.{{ $method.FunctionCallWithParams }}
            return gdnative.NewVariantNil()
            {{ end -}}
        {{ end -}}
    }

    // if we are here it means the method being called is unknown to us
    gdnative.Log.Warning(fmt.Sprintf("could not find method %s on instance %s", methodData, userData))
    return gdnative.NewVariantNil()
}
{{ end -}}

// nativeScriptInit{{ $className }} will run upon NativeScript initialization and its
// responsible for registering all our classes within Godot
func nativeScriptInit{{ $className }}() {

    // define an instance creation function, it will be called by Godot
    constructor := gdnative.CreateConstructor("{{ if $class.Alias }}{{ $class.Alias }}{{ else }}{{ $className }}{{ end }}", func(object gdnative.Object, methodData string) string {
        // create a new value of this wrapper type
        {{ if $class.HasConstructor -}}
        instance := {{ $className }}Wrapper{
            class: {{ $class.Constructor }}(),
        }
        {{ else -}}
        instance := {{ $className }}Wrapper{
            class: ${{ $className }}{},
        }
        {{ end -}}

        // use the pointer address as instance ID
        instanceID := fmt.Sprintf("{{ $className }}Wrapper_%p", &instance)
        {{ $className }}Instances[instanceID] = &instance

        // return the instance ID to Godot
        return instanceID
    })

    // define an instance destruction function, it will be called by Godot
    destructor := gdnative.CreateDestructor("{{ if $class.Alias }}{{ $class.Alias }}{{ else }}{{ $className }}{{ end }}", func(object gdnative.Object, methodData, userData string) {
        {{ if $class.HasDestructor -}}
        {{ $class.Destructor }}()
        {{ end -}}
        delete({{ $className }}Instances, userData)
    })

    // define methods attached to the instance
    methods := []gdnative.Method{
        {{ range $methodName, $method := $class.Methods -}}
        gdnative.NewGodotMethod("{{ if $class.Alias }}{{ $class.Alias }}{{ else }}{{ $className }}{{ end }}", "{{ if not $method.Alias }}{{ $method.GodotName }}{{ else }}{{ $method.Alias }}{{ end }}", handle{{ $className }}),
        {{ end -}}
    }

    // define properties attached to the instance
    properties := []gdnative.Property{
        {{ range $i, $property := $class.Properties -}}
        
        
        

        gdnative.NewGodotProperty(
            "{{ if $class.Alias }}{{ $class.Alias }}{{ else }}{{ $className }}{{ end }}", 
            "{{ if $property.Alias }}{{ $property.Alias }}{{ else }}{{ $property.Name }}{{ end }}", 
            "{{ $property.Hint }}", "{{ $property.HintString }}", 
            "{{ $property.Usage }}", "{{ $property.RsetType }}", 
            &gdnative.InstancePropertySet{
                SetFunc: func(object gdnative.Object, classProperty, instanceString string, property gdnative.Variant) {

                    class, ok := SimpleClassInstances[instanceString]
                    if !ok {
                        panic(fmt.Sprintf("Set property %s does not exists on instance %s registry", classProperty, instanceString))
                    }

                    class.class.{{ $property.Name }} = {{ $property.SetConvert }}
                },
                MethodData: "{{ if $class.Alias }}{{ $class.Alias }}{{ else }}{{ $className }}{{ end }}::{{ if $property.Alias }}{{ $property.Alias }}{{ else }}{{ $property.Name }}{{ end }}",
                FreeFunc: emptyFreeFunc,
            }, 
            &gdnative.InstancePropertyGet{
                GetFunc: func(object gdnative.Object, classProperty, instanceString string) gdnative.Variant {

                    class, ok := SimpleClassInstances[instanceString]
                    if !ok {
                        panic(fmt.Sprintf("Get property %q does not exists on instance %q registry", classProperty, instanceString))
                    }

                    return {{ $property.GetConvert }}
                },
                MethodData: "{{ if $class.Alias }}{{ $class.Alias }}{{ else }}{{ $className }}{{ end }}::{{ if $property.Alias }}{{ $property.Alias }}{{ else }}{{ $property.Name }}{{ end }}",
                FreeFunc: emptyFreeFunc,
            },
        ),
        {{ end -}}
    }

    // signals attached to the instance
    signals := []gdnative.GDSignal{
        {{ range $i, $signal := $class.Signals -}}
            {{ if $class.Alias -}}
        gdnative.NewGodotSignal("{{ $class.Alias }}", {{ $signal.Name }}, {{ $signal.Args }}, {{ $signal.Defaults }}),
            {{ else -}}
        gdnative.NewGodotSignal("{{ $className }}", {{ $signal.Name }}, {{ $signal.Args }}, {{ $signal.Defaults }}),
            {{ end -}}
        {{ end -}}
    }

    // register a new class within Godot
	gdnative.RegisterNewGodotClass(false, "{{ if $class.Alias }}{{ $class.Alias }}{{ else }}{{ $className }}{{ end }}", "{{ if $class.GetBase }}{{ $class.GetBase }}{{ else }}{{ "Reference" }}{{ end }}", &constructor, &destructor, methods, properties, signals)
}
{{ end -}}{{/* range $className, $class := $data.Classes */ -}}

// The "init()" function is a special Go function that will be called when this library
// is initialized. Here we can register our Godot classes.
func init() {

    {{ $data.GDNativeInit }}
}
