package crontab

import (
	"time"
)

type minuteNode struct {
}

func newMinuteNode() *minuteNode {
	return &minuteNode{}
}

type hourNode struct {
	Minute [60]*minuteNode
}

func (hour *hourNode) CreateMinute(nminute int) {
	min := &minuteNode{}
	hour.Minute[nminute] = min
}

func newHourNode() *hourNode {
	return &hourNode{}
}

type dayNode struct {
	IsClear bool
	Week    time.Weekday
	Hour    [24]*hourNode
}

func (day *dayNode) CreateHour(nhour int) {
	hour := &hourNode{}
	day.Hour[nhour] = hour
}

func newDayNode(curday *time.Time) *dayNode {
	day := &dayNode{}

	week := curday.Weekday()
	day.Week = week
	day.IsClear = true

	return day
}

type monthNode struct {
	MaxDay int
	First  *time.Time
	Day    [32]*dayNode
}

func (month *monthNode) CreateDay(nday int) {
	day := month.First.AddDate(0, 0, nday-1)
	month.Day[nday] = newDayNode(&day)
}

func newMonthNode(year, month int) *monthNode {
	Month := &monthNode{}
	First := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.Local)
	Month.First = &First
	Month.MaxDay = Month.First.AddDate(0, 1, -1).Day()
	return Month
}

type trieYear struct {
	Year  int
	Month [13]*monthNode
}

// CheckYear 跨年判断
func (ty *trieYear) CheckYear() bool {
	year := time.Now().Year()
	return ty.Year == year
}

func (ty *trieYear) clearHour() {

	for _, month := range ty.Month {
		if month != nil {
			for _, day := range month.Day {
				if day != nil {
					for i := 0; i <= 23; i++ {
						day.Hour[i] = nil
						day.IsClear = true
					}
				}

			}
		}

	}

}

func newTrieYear() *trieYear {
	ty := trieYear{}
	ty.Year = time.Now().Year()
	return &ty
}

// GetPlanTime 获取计划表
func (ty *trieYear) GetPlanTime(cron *Crontab, aftertime time.Time, count uint) []time.Time {

	now := aftertime
	nmonth := int(now.Month())
	nday := now.Day()
	nhour := now.Hour()
	nminute := now.Minute()

	var result []time.Time

	for i := 1; i <= 12; i++ {

		if i < nmonth {
			continue
		}

		month := ty.Month[i]
		if month != nil {

			for j := 1; j <= 31; j++ {

				if nmonth == i {
					if j < nday { // 获取当天的计划表
						continue
					}
				}

				day := month.Day[j]
				if day != nil {

					if day.IsClear {
						insertHour(cron, day) // 生成小时的计划表
					}

					for k := 0; k <= 23; k++ {

						if nmonth == i && nday == j {
							if k < nhour {
								continue
							}
						}

						hour := day.Hour[k]
						// 遍历24小时 生成需要的计划表
						if hour != nil {

							for n := 0; n <= 59; n++ {
								if nmonth == i && nday == j && nhour == k {
									if n < nminute {
										continue
									}
								}

								min := hour.Minute[n]
								if min != nil {

									curTime := time.Date(ty.Year, time.Month(i), j, k, n, 0, 0, time.Local)
									result = append(result, curTime)
									count-- // 统计完当前需要的计划任务后就结束
									if count <= 0 {
										ty.clearHour()
										return result
									}
								}
							}

						}

					}
				}
			}
		}
	}

	ty.clearHour()
	return result
}

// FromCrontab 从Crontab生成树
func (ty *trieYear) FromCrontab(cron *Crontab) {
	// 月的填充
	for _, month := range cron.month {

		left := month.left
		right := month.right

		for i := left; i <= right; i += month.per {
			curMonth := newMonthNode(ty.Year, i)
			ty.Month[i] = curMonth
			// 天的填充
			insertDay(cron, curMonth)
		}
	}
}

func filterDay(cron *Crontab, curday *dayNode) bool {

	for _, w := range cron.week {
		if w.isAll {
			return false
		}

		for n := w.left; n <= w.right; n += w.per {
			if n == int(curday.Week) {
				return false
			}
		}
	}
	return true
}

func insertDay(cron *Crontab, curMonth *monthNode) {
	for _, day := range cron.day {

		left := day.left
		if left < 0 {
			left += curMonth.MaxDay + 1
		}

		right := day.right
		if right < 0 {
			right += curMonth.MaxDay + 1
		}

		for j := left; j <= right; j += day.per {
			curMonth.CreateDay(j)
			curDay := curMonth.Day[j]

			if filterDay(cron, curDay) {
				curMonth.Day[j] = nil
			} else {
				// insertHour(cron, curDay)
			}
		}

	}
}

func insertHour(cron *Crontab, curDay *dayNode) {
	curDay.IsClear = false
	// 时的填充
	for _, hour := range cron.hour {

		left := hour.left
		right := hour.right

		for k := left; k <= right; k += hour.per {
			curDay.CreateHour(k)
			curHour := curDay.Hour[k]
			insertMinute(cron, curHour)
		}
	}
}

func insertMinute(cron *Crontab, curHour *hourNode) {
	for _, min := range cron.min {

		left := min.left
		right := min.right

		for l := left; l <= right; l += min.per {
			curHour.CreateMinute(l)
		}

	}
}
