package main

import (
	"log"
	"sync"
	"time"
)

type DistEvent interface {
	GetId() int64
	GetDate() *time.Time
}

// distList maintain a nextSettingsFile list up-to-date.
type distList struct {
	List  []DistEvent
	Lock  sync.RWMutex
	Timer *time.Timer
}

func (l *distList) TimerNextEvent() *time.Timer {
	l.Lock.RLock()
	defer l.Lock.RUnlock()

	var min *time.Time

	for _, f := range l.List {
		if min == nil || f.GetDate().Before(*min) {
			min = f.GetDate()
		}
	}

	if min == nil {
		return nil
	}

	return time.NewTimer(time.Until(*min))
}

func (l *distList) AddEvent(nsf DistEvent, firstItemChanged func(DistEvent)) {
	l.Lock.Lock()

	istop := len(l.List)
	for i, n := range l.List {
		if n.GetId() == nsf.GetId() {
			l.Lock.Unlock()
			return
		} else if n.GetDate().After(*nsf.GetDate()) {
			istop = i
			break
		}
	}

	l.List = append(l.List, nsf)
	copy(l.List[istop+1:], l.List[istop:])
	l.List[istop] = nsf

	l.Lock.Unlock()

	if istop == 0 {
		l.ResetTimer()
		if firstItemChanged != nil {
			firstItemChanged(nsf)
		}
	}
}

func (l *distList) DelEvent(id int64, firstItemChanged func(DistEvent)) {
	l.Lock.Lock()

	istop := len(l.List)
	for i, n := range l.List {
		if n.GetId() == id {
			istop = i
			break
		}
	}

	if istop == len(l.List)-1 {
		l.List = l.List[:istop]
	} else if istop != len(l.List) {
		l.List = append(l.List[:istop], l.List[istop+1:]...)
	}

	l.Lock.Unlock()

	if istop == 0 {
		l.ResetTimer()
		if firstItemChanged != nil {
			if len(l.List) == 0 {
				firstItemChanged(nil)
			} else {
				firstItemChanged(l.List[0])
			}
		}
	}
}

func (l *distList) ResetTimer() {
	l.Lock.RLock()
	defer l.Lock.RUnlock()

	if len(l.List) == 0 {
		l.Timer.Reset(time.Minute)
	} else {
		l.Timer.Reset(time.Until(*l.List[0].GetDate()))
	}
}

func (l *distList) Pop() DistEvent {
	l.Lock.Lock()
	defer l.Lock.Unlock()

	if len(l.List) == 0 {
		return nil
	}

	if time.Now().Before(*l.List[0].GetDate()) {
		return nil
	}

	ret := l.List[0]
	l.List = l.List[1:]
	return ret
}

func (l *distList) Print() {
	l.Lock.RLock()
	defer l.Lock.RUnlock()

	for n, i := range l.List {
		log.Printf("#%d: %v", n, i)
	}
}
