package closer

import (
	"context"
	"sync"
	"time"
)

// API describes the closer interface
type API interface {
	context.Context
	Close() error
}

// Closer closes goroutines and also implements context.Context, but does not
// implement deadline or value
type Closer struct {
	done      chan struct{}
	closed    bool
	closeLock *sync.Mutex
}

// Close closes the Closer
func (c *Closer) Close() (err error) {
	c.closeLock.Lock()
	if !c.closed {
		close(c.done)
		c.closed = true
	}
	c.closeLock.Unlock()
	return
}

// Deadline is not implemented
func (c *Closer) Deadline() (time.Time, bool) {
	return time.Time{}, false
}

// Done returns the done channel
func (c *Closer) Done() <-chan struct{} {
	return c.done
}

// Err returns error
// If Done is not yet closed, Err returns nil.
// If Done is closed, Err returns a non-nil error explaining why:
// Canceled if the context was canceled
// After Err returns a non-nil error, successive calls to Err return the same error.
func (c *Closer) Err() error {
	if c.done == nil {
		c.done = make(chan struct{})
	}

	select {
	case <-c.done:
		return context.Canceled
	default:
		return nil
	}
}

// Value is not implemented
func (c *Closer) Value(key interface{}) interface{} {
	return nil
}

// New instantiates a closer
func New() (c *Closer) {
	return &Closer{
		done:      make(chan struct{}),
		closeLock: &sync.Mutex{},
	}
}
