package shared

import (
	"bytes"
	"errors"
	"io"

	"golang.org/x/net/context"

	pb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/plugin"
	"github.com/sirupsen/logrus"
)

type DeviceModelServer struct {
	Impl DeviceModel
	pb.UnimplementedPluginServer
}

func (m *DeviceModelServer) Unmarshal(
	ctx context.Context,
	req *pb.UnmarshalRequest) (*pb.UnmarshalResponse, error) {
	err := m.Impl.Unmarshal(req.GetJson(), req.GetPath())
	if err != nil {
		return &pb.UnmarshalResponse{Valid: false}, err
	}
	return &pb.UnmarshalResponse{Valid: true}, err
}

func (m *DeviceModelServer) SetNode(
	ctx context.Context,
	req *pb.SetNodeRequest) (*pb.SetNodeResponse, error) {
	err := m.Impl.SetNode(req.GetPath(), req.GetValue())
	return &pb.SetNodeResponse{Valid: true}, err
}

func (m *DeviceModelServer) GetNode(
	ctx context.Context,
	req *pb.GetNodeRequest) (*pb.GetNodeResponse, error) {
	nodes, err := m.Impl.GetNode(req.GetPath(), req.RequestForIntendedState)
	return &pb.GetNodeResponse{Nodes: nodes}, err
}

func (m *DeviceModelServer) DeleteNode(
	ctx context.Context,
	req *pb.DeleteNodeRequest) (*pb.DeleteNodeResponse, error) {
	err := m.Impl.DeleteNode(req.GetPath())
	return &pb.DeleteNodeResponse{Valid: true}, err
}

func (m *DeviceModelServer) Model(
	ctx context.Context,
	req *pb.ModelRequest) (*pb.ModelResponse, error) {
	model, err := m.Impl.Model(req.GetFilterReadOnly())
	return &pb.ModelResponse{Json: model}, err
}

func (m *DeviceModelServer) Diff(
	ctx context.Context,
	req *pb.DiffRequest) (*pb.DiffResponse, error) {
	notification, err := m.Impl.Diff(req.GetOriginal(), req.GetModified())
	return &pb.DiffResponse{Notification: notification}, err
}

func (m *DeviceModelServer) ValidateChange(
	ctx context.Context,
	req *pb.ValidateChangeRequest) (*pb.ValidateChangeResponse, error) {
	model, err := m.Impl.ValidateChange(req.GetOperation(), req.GetPath(), req.GetValue())
	return &pb.ValidateChangeResponse{Model: model}, err
}

func (m *DeviceModelServer) PruneConfigFalse(
	ctx context.Context,
	req *pb.PruneConfigFalseRequest) (*pb.PruneConfigFalseResponse, error) {
	model, err := m.Impl.PruneConfigFalse(req.GetValue())
	return &pb.PruneConfigFalseResponse{Model: model}, err
}

func (m *DeviceModelServer) SchemaTreeGzip(
	req *pb.SchemaTreeGzipRequest,
	stream pb.Plugin_SchemaTreeGzipServer) error {
	buffer := make([]byte, int(MB))
	schema, err := m.Impl.SchemaTreeGzip()
	if err != nil {
		return err
	}

	schemaReader := bytes.NewReader(schema)

	for {
		n, err := schemaReader.Read(buffer)
		if err != nil {
			if errors.Is(err, io.EOF) {
				logrus.Println(err)
			}
			break
		}
		logrus.WithField("n", n).Trace("read bytes")
		payload := &pb.Payload{Chunk: buffer[:n]}
		err = stream.Send(payload)
		if err != nil {
			return err
		}
	}

	return nil
}
