// package down_time -- счётчик обратного времени в мсек
package down_time

import (
	"context"
	"fmt"

	// "log"
	"sync"
	"time"

	"wartank/pkg/components/parsetime"
	"wartank/pkg/components/safebool"
	"wartank/pkg/components/safeint"
	"wartank/pkg/types"
)

const (
	спатьИнтервал = time.Millisecond * 100 // Малый интервал сна в 100 мсек
)

// ВремОбрат -- счётчик обратного времени для игровой зоны (анга, база, битва и т.п.)
type ВремОбрат struct {
	сцена types.ИСцена

	парсер *parsetime.ParseTime // Парсер значения

	времяЗнач    *safeint.SafeInt   // Фактическое значение счётчика
	времПорог    *safeint.SafeInt   // Целевое время срабатывания
	еслиРаботает *safebool.SafeBool // Признак работы

	канТик   chan int // Канал секундных интервалов сна (для отображения)
	канВызов chan int // Канал для отправки сигналов (для верхнего уровня)

	кнт      context.Context // Контекст для счётчика времени
	фнОтмена func()          // Функция отмены контекста для счётчика времени

	блок sync.RWMutex
}

// НовВремОбрат -- возвращает новый *CountTime
func НовВремОбрат(сцена types.ИСцена, знач int) *ВремОбрат {
	if сцена == nil {
		panic("НовВремОбрат(): ИСцена == nil")
	}
	кнт, фнОтмена := context.WithCancel(сцена.Кнт())
	сам := &ВремОбрат{
		сцена:        сцена,
		времяЗнач:    safeint.NewSafeInt(),
		канТик:       make(chan int, 5),
		канВызов:     make(chan int, 2),
		еслиРаботает: safebool.NewSafeBool(),
		парсер:       parsetime.NewParseTime(),
		времПорог:    safeint.NewSafeInt(),
		кнт:          кнт,
		фнОтмена:     фнОтмена,
	}
	знач = int(time.Now().UTC().Unix()) + знач
	сам.времПорог.Уст(знач)
	сам.еслиРаботает.Уст()
	go сам.сделатьТик()
	go сам.пуск()
	go сам.закрыть()
	return сам
}

// Запускает тикер для секундных интервалов
func (сам *ВремОбрат) сделатьТик() {
	спатьСчёт := 0 // Счётчик сна (10 периодов -- 1 секунда)
	for {
		select {
		case <-сам.кнт.Done(): // Отмена контекста бота
			// log._rintf("CountTime.makeTick(): отмена контекста бота\n")
			return
		default:
			if !сам.еслиРаботает.Получ() {
				сам.фнОтмена()
				return
			}

			if спатьСчёт >= 10 {
				сам.канТик <- 1
				спатьСчёт = 0
			}
			time.Sleep(спатьИнтервал)
			спатьСчёт++
		}
	}
}

// Главный цикл обратного отсчёта
func (sf *ВремОбрат) пуск() {
	for range sf.канТик {
		time.Sleep(time.Millisecond * 100)
		timeNow := time.Now().UTC().Unix()
		if sf.времПорог.Get() > int(timeNow) {
			continue
		}
		close(sf.канВызов)
		sf.фнОтмена()
		return
	}
}

// Стоп -- останавливает работу cчётчика
func (sf *ВремОбрат) Стоп() {
	sf.еслиРаботает.Сброс()
}

// Получ -- возвращает число оставшихся сек
func (sf *ВремОбрат) Получ() int {
	return sf.времяЗнач.Get()
}

// устанавливает число оставшихся сек
func (sf *ВремОбрат) parse(val string) error {
	sf.блок.Lock()
	defer sf.блок.Unlock()
	if val == "" {
		return fmt.Errorf("CountTime.parse(): val is empty")
	}
	sf.парсер.Parse(val)
	_val := sf.парсер.Hour().Get()*3600 + sf.парсер.Min().Get()*60 + sf.парсер.Min().Get()
	sf.времяЗнач.Уст(_val)
	_val = int(time.Now().UTC().Unix()) + sf.времяЗнач.Get()
	sf.времПорог.Уст(_val)
	return nil
}

// устанавливает число оставшихся сек
func (sf *ВремОбрат) set_val(val int) error {
	sf.блок.Lock()
	defer sf.блок.Unlock()
	if val < 0 {
		return fmt.Errorf("CountTime.set_val(): val(%v)<0", val)
	}
	sf.времяЗнач.Уст(val)
	{ // Обновить локальные счётчики
		if val < 60 {
			sf.парсер.Hour().Reset()
			sf.парсер.Min().Reset()
			sf.парсер.Sec().Set(val)
			return nil
		}
		if 60 < val && val < 3600 {
			sf.парсер.Hour().Reset()
			iMin := val / 60
			sf.парсер.Min().Set(iMin)
			val -= iMin * 60
			sf.парсер.Sec().Set(val)
			return nil
		}
		sf.парсер.Hour().Set(val / 3600)
		val -= sf.парсер.Hour().Get() * 3600
		sf.парсер.Min().Set(val / 60)
		val -= sf.парсер.Min().Get() * 60
		sf.парсер.Sec().Set(val)
		// val = int(time.Now().UTC().Unix()) + sf.val.Get()
	}
	return nil
}

// Стр -- возвращает строковое представление оставшихся сек
func (sf *ВремОбрат) Стр() string {
	sf.блок.RLock()
	defer sf.блок.RUnlock()
	timeNow := time.Now().UTC().Unix()
	val := sf.времПорог.Get() - int(timeNow)
	go sf.set_val(val)
	return sf.парсер.String()
}

// КаналСиг -- возвращает канал чтения тиков
func (sf *ВремОбрат) КаналСиг() <-chan int {
	return sf.канВызов
}

func (сам *ВремОбрат) закрыть() {
	<-сам.кнт.Done()
	сам.блок.Lock()
	defer сам.блок.Unlock()
	if !сам.еслиРаботает.Получ() {
		return
	}
	сам.еслиРаботает.Сброс()
	close(сам.канТик)
}
