package divwar

import (
	"fmt"
	// "log"
	"net/http"
	"strings"
	"sync"
	"time"

	"wartank/pkg/components/section"
	"wartank/pkg/types"
	"wartank/server/serv_bots/warbot/angar/division/divwar/divwarnet"
	"wartank/server/serv_bots/warbot/angar/division/divwar/divwaron"
	"wartank/server/serv_bots/warbot/angar/division/divwar/divwaron/divwarsound"
	"wartank/server/serv_bots/warbot/tank/tankstat/static_param"
)

/*
	Объект ожидания битвы дивизий
*/

// DivWar -- объект ожидания битвы дивизий
type DivWar struct {
	*section.Section
	server types.IServer
	bot    types.IServBot
	alarm  types.IStatParam
	net    *divwarnet.DivWarNet
	conn   *http.Client

	// Непосредственная битва
	divon *divwaron.DivWarOn
	login string // Для несоредственной битвы дивизий
	block sync.Mutex

	chDivWar chan int // Сигнал начала битвы дивизий

	sound *divwarsound.DivWarSound // Однопоточное проигрывание звука
}

// NewDivWar -- возвращает новый *DivWar
func NewDivWar(server types.IServer, bot types.IServBot) (*DivWar, error) {
	{ // Предусловия
		if server == nil {
			return nil, fmt.Errorf("NewDivWar(): IServer == nil")
		}
		if bot == nil {
			return nil, fmt.Errorf("NewDivWar(): IServBot nil")
		}
	}

	sf := &DivWar{
		server:   server,
		bot:      bot,
		alarm:    static_param.NewStaticParam("alarm"),
		chDivWar: make(chan int, 1),
		sound:    divwarsound.NewDivWarSound(),
		conn:     bot.BotNet().Conn(),
		login:    "prospero tank",
	}
	// sf.shotTimeFull.Set(8000) // 8000 msec
	var err error
	{ // ISection
		sf.Section, err = section.NewSection(server, `<span>до начала `)
		if err != nil {
			return nil, fmt.Errorf("NewDivWar(): in create *Section, err=\n\t%w", err)
		}
	}
	{ // Net
		sf.net, err = divwarnet.NewDivWarNet(server, bot)
		if err != nil {
			return nil, fmt.Errorf("NewDivWar(): при создании DivWarNet, err=\n\t%w", err)
		}
	}
	go sf.run()
	go sf.reservTick()
	return sf, nil
}

func (sf *DivWar) reservTick() {
	defer sf.CountDown().Stop()
	for {
		select {
		case <-sf.server.Done():
			return
		default:
			ct0 := sf.CountDown().Get()
			time.Sleep(time.Second * 7)
			ct1 := sf.CountDown().Get()
			if ct1 != ct0 {
				continue
			}
			if sf.divon != nil {
				continue
			}
			sf.chDivWar <- 1
		}
	}
}

// запускает в работу битву дивизий
func (sf *DivWar) run() {
	sf.chDivWar <- 1
	for {
		select {
		case <-sf.server.Done():
			sf.CountDown().Stop()
			return
		case <-sf.CountDown().ChanSig(): // Время обновить данные по сражению
			sf.findTimeCount()
			sf.upDivWar()
		case <-sf.chDivWar: // Сигнал к началу сражения
			sf.block.Lock()
			if sf.divon != nil {
				continue
			}
			sf.alarm.Set(1)
			sf.sound.Play()
			go sf.DivWar()               // Запустить цикл непосредственного сражения
			time.Sleep(time.Second * 10) // Задержка для звука на странице
			sf.alarm.Set(0)
		}
	}
}

// Ищет время до начала битвы дивизий
func (sf *DivWar) findTimeCount() {
	if err := sf.net.UpdateLst("Битва дивизий"); err != nil { // Здесь может уже обратный отсчёт перед сражением
		sf.chDivWar <- 1
		return
	}
	var (
		strOut    string
		lstDivWar = sf.GetLst()
		isFind    bool
		ind       int
	)
	for ind, strOut = range lstDivWar {
		if strings.Contains(strOut, `до начала: `) {
			ind++
			strOut = lstDivWar[ind]
			isFind = true
			break
		}
		if strings.Contains(strOut, `>ОБЫЧНЫЕ<`) { // Это уже битва
			sf.chDivWar <- 1
			return
		}
	}
	if !isFind { // Битва дивизий уже идёт
		sf.chDivWar <- 1
		return
	}
	lstTime := strings.Split(strOut, `<span>`)
	strTime := lstTime[1]
	lstTime = strings.Split(strTime, `</span>`)
	strTime = lstTime[0]
	if err := sf.CountDown().Parse(strTime); err != nil {
		// log._rintf("WARN DivWar.findTimeCount(): при установке времени ожидания битвы дивизий(%v)\n\terr=%v\n", strTime, err)
		return
	}
}

// При необходимости поднимает взвод в атаку, вызывается только если обнаружено приглашение (+)
func (sf *DivWar) upDivWar() {
	var (
		strOut    string
		lstDivWar = sf.GetLst()
		isFind    bool
	)
	for _, strOut = range lstDivWar {
		if strings.Contains(strOut, `>Взвод, подъем! В атаку!<`) {
			isFind = true
			break
		}
		if strings.Contains(strOut, `<div class="white medium cntr bold mb5">Вы в рядах участников</div>`) {
			// log._rintf("INFO DivWar.upDivWar(): уже зарегистрирован\n")
			return
		}
	}
	if !isFind {
		return
	}
	// Найдено приглашение на участие
	lstUp := strings.Split(strOut, `<a class="simple-but border" href="`)
	linkUp := lstUp[1]
	lstUp = strings.Split(linkUp, `"><span><span>Взвод, подъем! В атаку!</span></span></a>`)
	linkUp = "http://wartank.ru/" + lstUp[0]
	lstDivWar, err := sf.net.Get(linkUp)
	if err != nil {
		// log._rintf("ERRO DivWar.upDivWar(): при выполнении GET-команды на подъём в атаку, err=\n\t%v\n", err)
		return
	}
	if err = sf.Update(lstDivWar); err != nil {
		// log._rintf("ERRO DivWar.upDivWar(): при обновлении lstDivWar, err=\n\t%v\n", err)
	}
	// log._rintf("INFO DivWar.upDivWar(): регистрация прошла успешно\n")
}

// Ведёт сражение
func (sf *DivWar) DivWar() {
	defer func() {
		sf.divon = nil
		sf.block.Unlock()
		if err := sf.CountDown().Set(1); err != nil {
			panic(fmt.Errorf("DivWar.DivWar(): при установке CountDown, err=\n\t%w", err))
		}
		// log.Printf("INFO DivWar.DivWar(): сражение завершено\n")
	}()
	var err error
	sf.divon, err = divwaron.NewDivWarOn(sf.server, sf.bot) // IDivWarOn (онлайн)
	if err != nil {
		// log._rintf("ERRO DivWar.DivWarOn(): при создании IDivWarOn, err=\n\t%v\n", err)
		time.Sleep(time.Millisecond * 250)
		return
	}
	// Цикл ожидания окончания сражения
	for !sf.divon.IsEnd().Get() {
		time.Sleep(time.Second * 1)
	}

}

// Alarm -- возвращает признак начала сражения (для браузера)
func (sf *DivWar) Alarm() types.IStatParam {
	return sf.alarm
}
