package socket

import (
	
	
)

// deadline is a resettable read deadline for [MagicConn]. Its wait channel is
// closed when the deadline elapses (or is set to a past time) and reopened when
// the deadline is cleared. It mirrors the deadline behavior net.Conn requires:
// a zero time means no deadline, and a past time fires immediately.
//
// The zero deadline is not usable; call init first.
type deadline struct {
	mu     sync.Mutex
	timer  *time.Timer
	cancel chan struct{}
}

func ( *deadline) () {
	.cancel = make(chan struct{})
}

// wait returns a channel that is closed when the deadline elapses. A nil
// deadline never fires.
func ( *deadline) () <-chan struct{} {
	.mu.Lock()
	defer .mu.Unlock()
	return .cancel
}

// set updates the deadline. A zero t clears it; a t in the past fires
// immediately.
func ( *deadline) ( time.Time) {
	.mu.Lock()
	defer .mu.Unlock()

	if .timer != nil {
		.timer.Stop()
		.timer = nil
	}

	 := isClosed(.cancel)

	switch {
	case .IsZero():
		// Clear: ensure an open channel.
		if  {
			.cancel = make(chan struct{})
		}
	case !.After(time.Now()):
		// Already past: fire now.
		if ! {
			close(.cancel)
		}
	default:
		// Future: re-arm with a fresh channel and a timer.
		if  {
			.cancel = make(chan struct{})
		}
		 := .cancel
		.timer = time.AfterFunc(time.Until(), func() {
			.mu.Lock()
			defer .mu.Unlock()
			if .cancel ==  && !isClosed() {
				close()
			}
		})
	}
}

func isClosed( chan struct{}) bool {
	select {
	case <-:
		return true
	default:
		return false
	}
}

// timeoutError is the net.Error returned by [MagicConn.ReadFrom] when the read
// deadline elapses. It reports Timeout and Temporary so callers (quic-go) can
// distinguish it from a fatal error.
type timeoutError struct{}

func (timeoutError) () string   { return "i/o timeout" }
func (timeoutError) () bool   { return true }
func (timeoutError) () bool { return true }