package comms

import (
	"encoding/gob"
	"io"
	"log"
)

type GOBChannel struct {
	// can be any connection, including the ssh connnection before it is
	// passed on to SSH during initialization of converge to agent communication
	Peer    io.ReadWriter
	Encoder *gob.Encoder
	Decoder *gob.Decoder
}

func NewGOBChannel(conn io.ReadWriter) GOBChannel {
	return GOBChannel{
		Peer:    conn,
		Encoder: gob.NewEncoder(conn),
		Decoder: gob.NewDecoder(conn),
	}
}

// Asynchronous send and receive on a single connection is guaranteed to preserver ordering of
// messages. We use asynchronous to void blocking indefinitely or depending on network timeouts.

func (channel GOBChannel) SendAsync(obj any, done chan<- any, errors chan<- error) {
	go func() {
		err := channel.Send(obj)
		if err != nil {
			errors <- err
		} else {
			done <- true
		}
	}()
}

func (channel GOBChannel) ReceiveAsync(result chan<- any, errors chan<- error) {
	go func() {
		value, err := channel.Receive()
		if err != nil {
			errors <- err
		} else {
			result <- value
		}
	}()
}

func (channel GOBChannel) Send(object any) error {
	err := channel.Encoder.Encode(ConvergeMessage{Value: object})
	if err != nil {
		log.Printf("Encoding error %v", err)
	}
	return err
}

func (channel GOBChannel) Receive() (any, error) {
	var target ConvergeMessage
	err := channel.Decoder.Decode(&target)
	return target.Value, err
}