package dht

import (
	"sync"
	"time"

	"github.com/anacrolix/dht/krpc"
)

// Transaction keeps track of a message exchange between nodes, such as a
// query message and a response message.
type Transaction struct {
	remoteAddr       Addr
	t                string
	onResponse       func(krpc.Msg)
	onTimeout        func()
	onSendError      func(error)
	querySender      func() error
	queryResendDelay func() time.Duration

	mu          sync.Mutex
	gotResponse bool
	timer       *time.Timer
	retries     int
	lastSend    time.Time
}

func (t *Transaction) handleResponse(m krpc.Msg) {
	t.mu.Lock()
	t.gotResponse = true
	t.mu.Unlock()
	t.onResponse(m)
}

func (t *Transaction) key() transactionKey {
	return transactionKey{
		t.remoteAddr.String(),
		t.t,
	}
}

func (t *Transaction) startResendTimer() {
	t.timer = time.AfterFunc(t.queryResendDelay(), t.resendCallback)
}

func (t *Transaction) resendCallback() {
	t.mu.Lock()
	defer t.mu.Unlock()
	if t.gotResponse {
		return
	}
	if t.retries == 2 {
		go t.onTimeout()
		return
	}
	t.retries++
	if err := t.sendQuery(); err != nil {
		go t.onSendError(err)
		return
	}
	if t.timer.Reset(t.queryResendDelay()) {
		panic("timer should have fired to get here")
	}
}

func (t *Transaction) sendQuery() error {
	if err := t.querySender(); err != nil {
		return err
	}
	t.lastSend = time.Now()
	return nil
}
