package main

import (
	"sync"
	"sync/atomic"
	"time"
)

//WaitCond condition variable to
type WaitCond struct {
	//trapper chan struct{}
	chanval atomic.Value
	state   int64
	mux     *sync.Mutex
}

// Signal flag that this condition has been met
func (c *WaitCond) Signal() {
	if atomic.CompareAndSwapInt64(&c.state, 0, 1) {
		close(c.chanval.Load().(chan struct{}))
	}
}

// Reset the state of this condition
func (c *WaitCond) Reset() {
	c.mux.Lock()

	if c.IsSet() { // this check is to see if we need a new trapper, its been flipped
		c.chanval.Store(make(chan struct{}))
		atomic.CompareAndSwapInt64(&c.state, 1, 0)
	}

	c.mux.Unlock()
}

// Channel get the channel to wait on
func (c *WaitCond) Channel() chan struct{} {
	return c.chanval.Load().(chan struct{})
}

// IsSet Test if the signal is set
func (c *WaitCond) IsSet() bool {
	return atomic.LoadInt64(&c.state) > 0
}

// Wait hold on this condition, delay = 0 waits until the cond is met
func (c *WaitCond) Wait(delay time.Duration) bool {
	if c.IsSet() {
		return true
	}

	if delay == 0 {
		<-c.chanval.Load().(chan struct{})
		return true
	}

	select {
	case <-c.chanval.Load().(chan struct{}):
	case <-time.After(delay):
	}

	return c.IsSet()
}

// NewWaitCond make a new condition
func NewWaitCond() *WaitCond {
	ret := &WaitCond{state: 0, mux: &sync.Mutex{}}
	ret.chanval.Store(make(chan struct{}))
	return ret
}
