package executor

import (
	"fmt"
	"sync"
	"time"
)

const (
	version = "0.1"

	TASK_EXECUTE_REASON_TIME = 0
	TASK_EXECUTE_REASON_INTERVAL = 1
	TASK_EXECUTE_REASON_TIMES = 2
)

type TaskPool struct {
	sync.Mutex
	tasks  map[int64]*task
	chQuit <-chan struct{}
	chStop chan struct{}
	once   sync.Once
	wg 	sync.WaitGroup
	loader IPoolLoader
}

func NewPool(chQuit <-chan struct{}, loader IPoolLoader) *TaskPool {
	tp:= &TaskPool{
		Mutex:  sync.Mutex{},
		tasks:  make(map[int64]*task),
		chQuit: chQuit,
		chStop: make(chan struct{}),
		loader: loader,
	}

	err := loader.LoadTo(tp)
	if nil != err {
		log(fmt.Sprintf("load pool failed. err=%v", err.Error()))
	}

	return tp
}

func (self *TaskPool) Exists(id int64) bool {
	self.Lock()
	_, ok := self.tasks[id]
	self.Unlock()
	return ok
}

func (self *TaskPool) AddTask(t *task) error {
	exists := self.Exists(t.Id)
	if exists {
		return ErrExistsAlready
	}

	self.Lock()
	self.tasks[t.Id] = t
	self.Unlock()

	return nil
}

func (self *TaskPool) RemoveTasks(ids ...int64) {
	for _, id := range ids {
		if t, ok := self.tasks[id]; ok {
			t.pause()
			delete(self.tasks, id)
		}
	}
}

func (self *TaskPool) PauseTasks(ids ...int64) {
	for _, id := range ids {
		if t, ok := self.tasks[id]; ok {
			t.pause()
		}
	}
}

func (self *TaskPool) ResumeTasks(ids ...int64) {
	for _, id := range ids {
		if t, ok := self.tasks[id]; ok {
			t.resume()
		}
	}
}

func (self *TaskPool) Tasks() map[int64]*task {
	return self.tasks
}

func (self *TaskPool) Start() {
	self.once.Do(func() {
		log("pool started")
		go self._run_impl()
	})
}

func (self *TaskPool) Stop() {
	err := self.loader.Serialize(self)
	if nil != err {
		log(fmt.Sprintf("save pool data failed. err=%v", err))
	}

	close(self.chStop)
	self.wg.Wait()
	log("pool stopped")
}

func (self *TaskPool) _run_impl() {
	tk := time.NewTicker(time.Second * 1)

	// execute a task
	execute := func(t *task, reason int) {
		self.wg.Add(1)
		go func(){
			if (reason == TASK_EXECUTE_REASON_INTERVAL || reason == TASK_EXECUTE_REASON_TIMES) &&
				t.Conf.ValidDelay() &&
				t.ExecutedTimes <= 0 {
				t.run(t.Conf.Delay)
			}else{
				t.run(0)
			}

			self.wg.Done()
			log(fmt.Sprintf("task<%v-%v> execute finished", t.Id, t.Conf.Name))
		}()
	}

	for {
		select {
		case <-self.chQuit:
			log("chQuit closed, quit")
			return
		case <-self.chStop:
			log("chStop closed, quit")
			return
		case <-tk.C:
			// for the sake of integrity of task executing
		} // select

		now := time.Now()
		for _, t := range self.tasks {
			if t.Conf.IsTimeFit(now) {
				log(fmt.Sprintf("pool schedule task<%v-%v> causz time fit. %v", t.Id, t.Conf.Name, now))
				execute(t, TASK_EXECUTE_REASON_TIME)
			}

			if t.Conf.ValidInterval() {
				if now.Unix() - t.LastActive >= t.Conf.Interval {
					log(fmt.Sprintf("pool schedule task<%v-%v> causz interval %v. %v", t.Id, t.Conf.Name, t.Conf.Interval, now))
					execute(t, TASK_EXECUTE_REASON_INTERVAL)
				}
			}else{
				if t.Conf.ValidTimes() {
					if t.ExecutedTimes < t.Conf.Times {
						log(fmt.Sprintf("pool schedule task<%v-%v> causz times %v. %v", t.Id, t.Conf.Name, t.Conf.Times, now))
						execute(t, TASK_EXECUTE_REASON_TIMES)
					}
				}
			}
		} // for
	}// for
}
