package shared

import (
	"context"

	"google.golang.org/grpc"

	mnepb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement"
	pb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/plugin"
	"github.com/hashicorp/go-plugin"
	gpb "github.com/openconfig/gnmi/proto/gnmi"
)

var Handshake = plugin.HandshakeConfig{
	ProtocolVersion:  1,
	MagicCookieKey:   "GOSDN_PLUGIN_MAGIC_COOKIE",
	MagicCookieValue: "woux6tn7gbsm53ipb3w4zxb59qd3se43hnqeh5bieynzvfchchktsd32pbjqwuxq",
}

var PluginMap = map[string]plugin.Plugin{
	"deviceModel": &DeviceModelPlugin{},
}

type DeviceModel interface {
	// TODO: It should be possible to pass methods like Unmarshal, SetNode,
	// GetNode, etc. ytypes.Unmarshal-|Set-|GetOptions
	Unmarshal(json []byte, path *gpb.Path) error
	SetNode(path *gpb.Path, value *gpb.TypedValue) error
	GetNode(path *gpb.Path, requestForIntendedState bool) ([]*gpb.Notification, error)
	DeleteNode(path *gpb.Path) error
	Model(filterReadOnly bool) ([]byte, error)
	Diff(original, modified []byte) (*gpb.Notification, error)
	SchemaTreeGzip() ([]byte, error)
	ValidateChange(operation mnepb.ApiOperation, path *gpb.Path, value *gpb.TypedValue) ([]byte, error)
	PruneConfigFalse(value []byte) ([]byte, error)
}

// DeviceModelPlugin implements a hashicorp gRPC plugin.
type DeviceModelPlugin struct {
	plugin.Plugin
	Impl DeviceModel
}

func (p *DeviceModelPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
	pb.RegisterPluginServer(s, &DeviceModelServer{Impl: p.Impl})
	return nil
}

func (p *DeviceModelPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
	return &DeviceModelClient{client: pb.NewPluginClient(c)}, nil
}
