package flow

import (
	"sync"
	"time"
)

// Hooks for node life cycle
type Hooks struct {
	sync.Mutex
	hooks []Hook
}

// Hook funcs to handle certain events on the flow
type Hook struct {
	Wait   func(ID string, triggerTime time.Time)
	Start  func(ID string, triggerTime time.Time)
	Finish func(ID string, triggerTime time.Time, res interface{})
	Error  func(ID string, triggerTime time.Time, err error)
	Any    func(name string, ID string, triggerTime time.Time, extra ...interface{})
}

// Trigger a hook
func (hs *Hooks) Trigger(name string, ID string, extra ...Data) {
	hs.Lock()
	defer hs.Unlock()

	for _, h := range hs.hooks {
		if h.Any != nil {
			h.Any(name, ID, time.Now(), extra...)
		}
		switch name {
		case "Wait":
			if h.Wait != nil {
				h.Wait(ID, time.Now())
			}
		case "Start":
			if h.Start != nil {
				h.Start(ID, time.Now())
			}
		case "Finish":
			if h.Finish != nil {
				h.Finish(ID, time.Now(), extra[0])
			}
		case "Error":
			if h.Error != nil {
				h.Error(ID, time.Now(), extra[0].(error))
			}
		}
	}
}

func (hs *Hooks) wait(ID string)             { hs.Trigger("Wait", ID) }
func (hs *Hooks) start(ID string)            { hs.Trigger("Start", ID) }
func (hs *Hooks) finish(ID string, res Data) { hs.Trigger("Finish", ID, res) }
func (hs *Hooks) error(ID string, err error) { hs.Trigger("Error", ID, err) }

// Attach attach a hook
func (hs *Hooks) Attach(h Hook) {
	hs.Lock()
	defer hs.Unlock()
	hs.hooks = append(hs.hooks, h)

}
