package utils

import (
	"errors"
	"fmt"
	"log"
	"math"
	"strconv"
	"strings"
	"time"
)

var TimezoneLocation *time.Location

type ITimer struct {
	*time.Timer
	name      string
	exited    bool
	isRunning bool
	d         time.Duration
	fn        func()
}

func itimerDefaultFunc() {}

func NewTimer(d time.Duration) *ITimer {
	tm := &ITimer{
		Timer:     time.NewTimer(d),
		exited:    false,
		isRunning: true,
		d:         d,
		fn:        itimerDefaultFunc,
	}
	tm.Pause()
	return tm
}

func (tm *ITimer) SetName(nm string) {
	tm.name = nm
}

func (tm *ITimer) Name() string {
	return tm.name
}

func (tm *ITimer) Exit() bool {
	tm.exited = tm.Timer.Reset(0)
	return tm.exited
}

func (tm *ITimer) IsExit() bool {
	return tm.exited
}

func (tm *ITimer) reset() bool {
	if tm.exited {
		return false
	}
	tm.isRunning = true
	return tm.Timer.Reset(tm.d)
}

func (tm *ITimer) Pause() bool {
	tm.isRunning = false
	return tm.Timer.Reset(time.Duration(math.MaxInt64))
}

func (tm *ITimer) Continue() bool {
	tm.isRunning = true
	return tm.reset()
}

func (tm *ITimer) IsRunning() bool {
	if tm.exited {
		return false
	}
	return tm.isRunning
}

func (tm *ITimer) SetFunc(fn func()) {
	tm.fn = fn
}

func (tm *ITimer) NowExecFunc() {
	if tm.fn != nil {
		go tm.fn()
	}
	tm.reset()
}

func (tm *ITimer) Run() {
	for {
		select {
		case <-tm.C:
			if tm.IsExit() {
				tm.isRunning = false
				return
			}
			tm.reset()
			if tm.fn != nil {
				go tm.fn()
			}
		}
	}
}

//format time like java, such as: yyyy-MM-dd HH:mm:ss

func TimeFormat(t time.Time, format string) string {

	//year
	if strings.ContainsAny(format, "y") {

		year := strconv.Itoa(t.Year())

		if strings.Count(format, "yy") == 1 && strings.Count(format, "y") == 2 {
			format = strings.Replace(format, "yy", year[2:], 1)
		} else if strings.Count(format, "yyyy") == 1 && strings.Count(format, "y") == 4 {
			format = strings.Replace(format, "yyyy", year, 1)
		} else {
			log.Fatalln("format year error! please 'yyyy' or 'yy'")
		}
	}

	//month
	if strings.ContainsAny(format, "M") {

		var month string

		if int(t.Month()) < 10 {
			month = "0" + strconv.Itoa(int(t.Month()))
		} else {
			month = strconv.Itoa(int(t.Month()))
		}

		if strings.Count(format, "MM") == 1 && strings.Count(format, "M") == 2 {
			format = strings.Replace(format, "MM", month, 1)
		} else {
			log.Fatalln("format month error! please 'MM'")
		}
	}

	//day
	if strings.ContainsAny(format, "d") {

		var day string

		if t.Day() < 10 {
			day = "0" + strconv.Itoa(t.Day())
		} else {
			day = strconv.Itoa(t.Day())
		}

		if strings.Count(format, "dd") == 1 && strings.Count(format, "d") == 2 {
			format = strings.Replace(format, "dd", day, 1)
		} else {
			log.Fatalln("format day error! please 'dd'")
		}
	}

	//hour
	if strings.ContainsAny(format, "H") {

		var hour string

		if t.Hour() < 10 {
			hour = "0" + strconv.Itoa(t.Hour())
		} else {
			hour = strconv.Itoa(t.Hour())
		}

		if strings.Count(format, "HH") == 1 && strings.Count(format, "H") == 2 {
			format = strings.Replace(format, "HH", hour, 1)
		} else {
			log.Fatalln("format hour error! please 'HH'")
		}
	}

	//minute
	if strings.ContainsAny(format, "m") {

		var minute string

		if t.Minute() < 10 {
			minute = "0" + strconv.Itoa(t.Minute())
		} else {
			minute = strconv.Itoa(t.Minute())
		}
		if strings.Count(format, "mm") == 1 && strings.Count(format, "m") == 2 {
			format = strings.Replace(format, "mm", minute, 1)
		} else {
			log.Fatalln("format minute error! please 'mm'")
		}
	}

	//second
	if strings.ContainsAny(format, "s") {

		var second string

		if t.Second() < 10 {
			second = "0" + strconv.Itoa(t.Second())
		} else {
			second = strconv.Itoa(t.Second())
		}

		if strings.Count(format, "ss") == 1 && strings.Count(format, "s") == 2 {
			format = strings.Replace(format, "ss", second, 1)
		} else {
			log.Fatalln("format second error! please 'ss'")
		}
	}

	return format
}

//2007-11-23 10:02:14/20071123100214
func TimeParse(str string) (time.Time, error) {
	ll := len(str)
	if ll == 19 {
		loc, _ := time.LoadLocation("Local")
		return time.ParseInLocation("2006-01-02 15:04:05", str, loc)
	} else if ll == 14 {
		loc, _ := time.LoadLocation("Local")
		return time.ParseInLocation("20060102150405", str, loc)
	}
	return time.Time{}, errors.New("str len is error")
}

//17:02:03/170203
func TimeParseHHmmss(str string) (time.Time, error) {
	ll := len(str)
	if ll != 8 && ll != 6 {
		return time.Time{}, errors.New("input str is error")
	}
	if ll == 8 {
		return TimeParse(TimeFormat(time.Now(), "yyyy-MM-dd "+str))
	}
	return TimeParse(TimeFormat(time.Now(), "yyyyMMdd"+str))
}

//17:02/1702
func TimeParseHHmm(str string) (time.Time, error) {
	ll := len(str)
	if ll != 5 && ll != 4 {
		return time.Time{}, errors.New("input str is error")
	}
	if ll == 5 {
		return TimeParse(TimeFormat(time.Now(), "yyyy-MM-dd "+str+":00"))
	}
	return TimeParse(TimeFormat(time.Now(), "yyyyMMdd"+str+"00"))
}

//2007-05-69/20070569
func TimeParseyyyyMMdd(str string) (time.Time, error) {
	ll := len(str)
	if ll != 10 && ll != 8 {
		return time.Time{}, errors.New("input str is error")
	}
	if ll == 10 {
		return TimeParse(TimeFormat(time.Now(), str+" 00:00:00"))
	}
	return TimeParse(TimeFormat(time.Now(), str+"000000"))
}

//计算时间差返回秒级  样例: T1 = 16:00 t2 = 08:00
func Timelen(t1 string, t2 string) (int, error) {
	var time1 int64
	var time2 int64
	len1 := len(t1)
	if len1 != 4 && len1 != 5 {
		return 0, errors.New("input str is error")
	}
	len2 := len(t2)
	if len2 != 4 && len2 != 5 {
		return 0, errors.New("input str is error")
	}

	tem1, _ := TimeParseHHmm(t1)
	time1 = tem1.Unix()

	tem2, _ := TimeParseHHmm(t2)
	time2 = tem2.Unix()

	timelen := time1 - time2
	return int(timelen), nil
}

//获取某一天的0点时间
func GetZeroTime(d time.Time) time.Time {
	now := time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, d.Location())
	return now
}

//返回格式:例如2016-05-01 00:00:00的时间戳到2016-06-01 00:00:00的时间戳
func TimeReturnMonth(now time.Time) (firstMonth string, lastMonth string) {
	currentYear, currentMonth, _ := now.Date()
	currentLocation := now.Location()
	firstOfMonth := time.Date(currentYear, currentMonth, 1, 0, 0, 0, 0, currentLocation)
	lastOfMonth := firstOfMonth.AddDate(0, 1, 0).Add(time.Second * -1)
	return TimeFormat(firstOfMonth, "yyyyMMdd"), TimeFormat(lastOfMonth, "yyyyMMdd")
}

// GetCrossDays 获取两个时间点之间的天数
func GetCrossDays(t1, t2 time.Time) (days int) {
	var (
		y1, d1, y2, d2 int
		m1, m2         time.Month
		date1, date2   time.Time
	)

	days = 0
	if t1.Unix() >= t2.Unix() {
		return
	}
	y1, m1, d1 = t1.Date()
	y2, m2, d2 = t2.Date()
	date1 = time.Date(y1, m1, d1, 0, 0, 0, 0, time.UTC)
	date2 = time.Date(y2, m2, d2, 0, 0, 0, 0, time.UTC)
	days = int(date2.Sub(date1).Hours() / 24)
	return
}

// 获取时间点的日期，以年月日字符类型数据返回
func GetYMDString(t time.Time) (ymd string) {
	var (
		y1, d1 int
		m1     time.Month
	)
	y1, m1, d1 = t.Date()
	ymd = fmt.Sprintf("%04d", y1) + fmt.Sprintf("%02d", int(m1)) + fmt.Sprintf("%02d", d1)
	return
}
func ParseWeekIndex(date time.Time) int {
	dateStr := date.Format("2006-01-02")
	dateStr = dateStr[:8] + "01"
	firstDay, _ := time.ParseInLocation("2006-01-02", dateStr, TimezoneLocation)
	firstWeekDay := firstDay.Weekday()
	weekDay := date.Weekday()
	for i := 1; i < 6; i++ {
		date = date.AddDate(0, 0, -7)
		if date.Before(firstDay) {
			if weekDay >= firstWeekDay {
				return i
			}
			return i + 1
		}
	}
	return 0
}

// 获取线段上两个时间点之间的Duration
func GetDuration(startTime, endTime time.Time, EffFactor float64) (duration time.Duration) {
	var seconds float64
	if endTime.Unix() <= startTime.Unix() {
		duration = 0
		return
	}
	if EffFactor <= 0 {
		EffFactor = 1
	}
	seconds = endTime.Sub(startTime).Seconds() * EffFactor
	duration = time.Duration(seconds) * time.Second
	return
}

// GenerateTimeFormatForAcura 生成讴歌工单流水号中日期的格式
func GenerateTimeFormatForAcura(t time.Time) (dateStr string, err error) {
	year, month, day := t.Date()
	YearStr, exist := YEAR_MAP[year]
	if !exist {
		err = errors.New("年份映射值不存在")
		return
	}
	monthStr, exist := MONTH_MAP[int(month)]
	if !exist {
		err = errors.New("月份映射值不存在")
		return
	}
	dayStr, exist := DAY_MAP[day]
	if !exist {
		err = errors.New("天映射值不存在")
		return
	}
	return YearStr + monthStr + dayStr, nil
}

//获取两个时间，中间的所有年月日 + 时 集合
func GetHoursData(startTime, endTime time.Time) (data []string, err error) {
	if startTime.Unix() > endTime.Unix() {
		return nil, errors.New("开始时间大于结束时间!")
	}
	for {
		if startTime.Unix() > endTime.Unix() {
			return data, nil
		}
		year, month, day := startTime.Date()
		hour := startTime.Hour()
		nowTime := time.Date(year, month, day, hour, 0, 0, 0, startTime.Location())
		keyTime := TimeFormat(nowTime, "yyyy-MM-dd HH:mm")
		data = append(data, keyTime)
		startTime = nowTime.Add(1 * time.Hour)
	}
}

