package main

import (
	spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
	"code.fbi.h-da.de/danet/gosdn/controller/customerrs"
	"github.com/google/uuid"
	gpb "github.com/openconfig/gnmi/proto/gnmi"
	"github.com/openconfig/goyang/pkg/yang"
	"github.com/openconfig/ygot/ygot"
	"github.com/openconfig/ygot/ytypes"
	log "github.com/sirupsen/logrus"
)

var PluginSymbol Csbi

type Csbi struct {
	schema *ytypes.Schema
	id     uuid.UUID
}

// SetNode injects schema specific model representation to the transport.
// Needed for type assertion.
func (csbi *Csbi) SetNode(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error {
	return ytypes.SetNode(schema, root.(*Device), path, val, opts...)
}

// Unmarshal injects schema specific model representation to the transport.
// Needed for type assertion.
func (csbi *Csbi) Unmarshal(bytes []byte, path *gpb.Path, goStruct ygot.GoStruct, opt ...ytypes.UnmarshalOpt) error {
	return unmarshal(csbi.Schema(), bytes, path, goStruct, opt...)
}

//unmarshal parses a gNMI response to a go struct.
func unmarshal(schema *ytypes.Schema, bytes []byte, path *gpb.Path, goStruct ygot.GoStruct, opt ...ytypes.UnmarshalOpt) error {
	defer func() {
		if r := recover(); r != nil {
			log.Error(r.(error))
		}
	}()

	// Load SBI definition
	root, err := ygot.DeepCopy(schema.Root)
	if err != nil {
		return err
	}
	validatedDeepCopy, ok := root.(ygot.GoStruct)
	if !ok {
		return &customerrs.InvalidTypeAssertionError{}
	}

	// returns the node we want to fill with the data contained in 'bytes',
	// using the specified 'path'.
	createdNode, _, err := ytypes.GetOrCreateNode(schema.RootSchema(), validatedDeepCopy, path)
	if err != nil {
		return err
	}
	validatedCreatedNode, ok := createdNode.(ygot.GoStruct)
	if !ok {
		return &customerrs.InvalidTypeAssertionError{}
	}

	if err := Unmarshal(bytes, validatedCreatedNode, opt...); err != nil {
		return err
	}

	opts := []ygot.MergeOpt{&ygot.MergeOverwriteExistingFields{}}
	return ygot.MergeStructInto(goStruct, validatedDeepCopy, opts...)
}

func (csbi *Csbi) Schema() *ytypes.Schema {
	schema, err := Schema()
	csbi.schema = schema
	if err != nil {
		log.Fatal(err)
	}
	return schema
}

func (csbi *Csbi) SchemaTreeGzip() []byte {
	return ySchema
}

// SetID sets the ID of the cSBI to the provided UUID
func (csbi *Csbi) SetID(id uuid.UUID) {
	csbi.id = id
}

// ID returns the Southbound's UUID
func (csbi *Csbi) ID() uuid.UUID {
	return csbi.id
}

// Type returns the Southbound's type
func (csbi *Csbi) Type() spb.Type {
	return spb.Type_TYPE_PLUGIN
}

// Name returns the Name of the Southbound
func (csbi *Csbi) Name() string {
	return "plugin"
}

func SchemaTreeGzip() []byte {
	return ySchema
}
