package parser_time

import (
	"fmt"
	"strings"
	"sync"
	"wartank/pkg/alias"
	"wartank/pkg/components/product/parser_time/parse_hour"
	"wartank/pkg/components/product/parser_time/parse_min"
	"wartank/pkg/components/product/parser_time/parse_sec"
	"wartank/pkg/types"
)

/*
	Выковыривает из строки время и потокобезопасно хранит его
*/

// ПарсерВремя -- потокобезопасный ковырятор строки времени
type ПарсерВремя struct {
	всегоМилСек alias.МилСек     // Числовое значение хранимого времени
	час         types.ИПарсерЧас // Часы метки времени
	мин         types.ИПарсерМин // Минуты метки времени
	сек         types.ИПарсерСек // Секунды метки времени

	блок sync.RWMutex
}

// НовПарсерВремя -- возвращает новый *ParseTime
func НовПарсерВремя() *ПарсерВремя {
	сам := &ПарсерВремя{
		час: parse_hour.НовПарсерЧас(),
		мин: parse_min.НовПарсерМинут(),
		сек: parse_sec.НовПарсерСекунд(),
	}
	_ = types.ИПарсерВремя(сам)
	return сам
}

// Сброс -- сбрасывает значение интервала времени по требованию
func (сам *ПарсерВремя) Сброс() {
	сам.час.Сброс()
	сам.мин.Сброс()
	сам.сек.Сброс()
}

// ПолучМилСек -- возвращает общее число секунд
func (сам *ПарсерВремя) ПолучМилСек() alias.МилСек {
	сам.блок.RLock()
	defer сам.блок.RUnlock()
	return сам.всегоМилСек
}

// Уст -- разбирает строковое представление на части
func (сам *ПарсерВремя) Уст(стрВремя alias.Время) error {
	сам.блок.Lock()
	defer сам.блок.Unlock()
	if стрВремя == "" {
		return fmt.Errorf("CountTime.Set(): val is empty")
	}
	сам.Сброс()
	списВремя := strings.Split(string(стрВремя), ":")
	фнСекУст := func(стрВремя alias.Время) error {
		if ош := сам.сек.Уст(стрВремя); ош != nil {
			return fmt.Errorf("ПарсерВремя.Уст().фнСекУст(): при установке секунд, ош=\n\t%w", ош)
		}
		return nil
	}
	фнМинУст := func() error {
		стрСек := alias.Время(списВремя[1])
		if ош := фнСекУст(стрСек); ош != nil {
			return fmt.Errorf("ПарсерВремя.Уст().фнМинУст(): при установке секунд, ош=\n\t%w", ош)
		}
		стрМин := alias.Время(списВремя[0])
		if ош := сам.мин.Уст(стрМин); ош != nil {
			return fmt.Errorf("ПарсерВремя.Уст().фнМинУст(): при установке минут, ош=\n\t%w", ош)
		}
		return nil
	}
	фнЧасУст := func() error {
		стрСек := alias.Время(списВремя[2])
		if ош := фнСекУст(стрСек); ош != nil {
			return fmt.Errorf("ПарсерВремя.Уст().фнЧасУст(): при установке секунд, ош=\n\t%w", ош)
		}
		стрМин := alias.Время(списВремя[1])
		if ош := сам.мин.Уст(стрМин); ош != nil {
			return fmt.Errorf("ПарсерВремя.Уст().фнЧасУст(): при установке минут, ош=\n\t%w", ош)
		}
		стрЧас := alias.Время(списВремя[0])
		if ош := сам.час.Уст(стрЧас); ош != nil {
			return fmt.Errorf("ПарсерВремя.Уст().фнЧасУст(): при установке часов, ош=\n\t%w", ош)
		}

		return nil
	}

	// Разбить время, перевести в секунды
	switch len(списВремя) {
	case 1: // Только секунды
		стрСек := alias.Время(списВремя[0])
		if ош := фнСекУст(стрСек); ош != nil {
			return ош
		}
	case 2: // Минуты, секунды
		if ош := фнМинУст(); ош != nil {
			return ош
		}
	case 3:
		if ош := фнЧасУст(); ош != nil {
			return ош
		}
	default:
		return fmt.Errorf("ПарсерВремя.Уст(): сбойная строка времени(%q)", стрВремя)
	}
	сам.всегоМилСек = сам.час.ПолучМилСек() + сам.мин.ПолучМилСек() + сам.сек.ПолучМилСек()
	return nil
}

// Час -- возвращает хранимые часы
func (сам *ПарсерВремя) Час() types.ИПарсерЧас {
	return сам.час
}

// Мин -- возвращает хранимые минуты
func (сам *ПарсерВремя) Мин() types.ИПарсерМин {
	return сам.мин
}

// Сек -- возвращает хранимые секунды
func (сам *ПарсерВремя) Сек() types.ИПарсерСек {
	return сам.сек
}

// String -- возвращает хранимое время
func (сам *ПарсерВремя) String() string {
	res := сам.час.String() + ":" + сам.мин.String() + ":" + сам.сек.String()
	return res
}
