# Модуль машины состояний (конечный автомат)

import speaker # Для воспроизведения
import listener # Для распознавание речи
import datetime # Для времени
from pyowm import OWM  # Использование OpenWeatherMap для получения данных о погоде (pip install pyowm)
import random 
import traceback  # вывод traceback без остановки работы программы при отлове исключений


machine = None # Конечный автомат
current_state = None # Текущее состояние
weather_api_key = None # Ключ для OpenWeatherMap
myName = None # Имя ассистента
   
def init(assistant_name):
    """Инициализирует конечный автомат"""
    global machine 
    global current_state
    global weather_api_key
    global myName
    
    listener.init_listener() # Инициализация распозавания

    myName = assistant_name

    with open("weatherApiKey.txt", "r") as key:
        weather_api_key = key.read()

    current_state = "Начало"
    machine = {
        "Начало":{ 
            assistant_name.lower(): switchToListen
        },
        "Слушаю задание":{ 
            "время": switchToTime,
            "погода": switchToWeather,
            "привет": switchToHello, 
            "здравствуй": switchToHello, 
            "доброе утро": switchToHello,
            "добрый день": switchToHello, 
            "добрый вечер": switchToHello, 
            "доброй ночи": switchToHello,
            "пока": switchToBye,
            "до встречи": switchToBye,
            "перезапуск": switchToReboot,
            "завершение работы": turnOff
        },

    }
    switchToHello()
    speaker.speak(f"Ассистент {myName} начинает работу")


def work():
    """Обрабатывает конечный автомат"""
    global current_state

    while(True):
        #word = input().lower() # Пока считаем с клавиатуры предложение
        word = listener.recognize() # Слушаем и распознаем
        # Если ассистент знает такое слово для перехода
        if (word in machine[current_state].keys()):
            # Выполняем действия и переход в новое состояние
            current_state = machine[current_state][word]()
        elif (current_state != 'Начало'):
        # иначе переспросим
            speaker.speak("Я не понял, пожалуйста повторите")
            print("Сейчас автомат в: " + current_state + ". Я услышал: " + word)



# Переход на "Слушаю задание"
def switchToListen():
    speaker.speak("Слушаю")
    return "Слушаю задание"

# Переход на "Говорю время" и возвращаюсь в "Начало"
def switchToTime():
    now = datetime.datetime.now()
    time = f'Сейчас {now.hour} '
    if now.hour == 0 or 5 <= now.hour <= 20:
        time += 'часов '
    if now.hour == 1 or now.hour == 21:
        time += 'час '
    if 2 <= now.hour % 20 <= 4: 
        time += 'часАА ' # тянем слог

    time += str(now.minute)
    if now.minute % 10 == 1 and now.minute != 11:
        time += ' минута'
    elif 5 <= now.minute <= 20 or 25 <= now.minute <= 30 or 35 <= now.minute <= 40 or 45 <= now.minute <= 50 or 55 <= now.minute:
        time += ' минут'
    else:
        time += ' минуты'

    speaker.speak(time)
    return "Начало"

def _precipitation_today_message(precipitations:list):
    # Собираем время для проверки что осадки в будущем сегодня
    today = datetime.datetime.now().replace(hour = 23, minute = 59, second = 59) # Зададим конец дня
    for precipitation in precipitations:
        # Время осадков
        precipitation_time = datetime.datetime.fromisoformat(precipitation.reference_time('iso')).replace(tzinfo=None) # во избежания ошибок сравнения относительного и абсолютного времени
        # Если осадки в будущем сегодня 
        if datetime.datetime.now() <= precipitation_time <= today:
            return f'Обещают {precipitation.detailed_status} в {precipitation_time.hour} часов' # Достаточно сказать об одних осадках
    return '' # Если осадков подходящих не нашлось - пустая строка
            
# Переход на "Вопрос локации погоды"
def switchToWeather():

    location = "Murino,RU"
    try:
        open_weather_map = OWM(weather_api_key)
        open_weather_map.config['language'] = 'ru' # Язык результатов

        # запрос данных о текущем состоянии погоды
        now = open_weather_map.weather_manager().weather_at_place(location)
        forecast = open_weather_map.weather_manager().forecast_at_place(location, '3h') # 3h вернет недельный, так как daily не работает 

    except:
        speaker.speak("Извините. Ошибка запроса к серверу погоды")
        traceback.print_exc() # Вывод трейсбека для отладки
        return "Начало"

    # разбивание данных на части для удобства работы с ними
    status = now.weather.detailed_status
    temperature = int(now.weather.temperature('celsius')["temp"])
    
    # Начинаем собирать строку с погодой
    weather = f"Сейчас {status} {temperature} градусов."

    # Если в прогнозе есть дождь
    if forecast.will_have_rain():
        weather += _precipitation_today_message(forecast.when_rain())
    # Если в прогнозе есть снег
    if forecast.will_have_snow():
        weather += _precipitation_today_message(forecast.when_rain())
        
    speaker.speak(weather)
    return "Начало"

# Переход на приветствие и на "Начало"
def switchToHello():
    helloWords = ["Привет", "Здравствуй", "Доброго времени суток"]
    random_index = random.randint(0, len(helloWords))

    # Если выпадает больше нуля - берем готовую
    if (random_index > 0):
        hi = helloWords[random_index-1]
    else:
    # Иначе генерируем время суток
        now = datetime.datetime.now()
        if (now.hour < 5 or now.hour > 21):
            hi = "Доброй ночи"
        elif (now.hour < 12):
            hi = "Доброе утро"
        elif (now.hour < 16):
            hi = "Добрый день"
        else:
            hi = "Добрый вечер"

    speaker.speak(hi)

    return "Начало"

# Переход на приветствие и на "Начало"
def switchToBye():
    helloWords = ["Пока", "Удачи", "Я всегда тут, буду ждать"]
    speaker.speak(random.choice(helloWords))

    return "Начало"

# Переход на запрос модуля для перезагрузки и на "Начало":
def switchToReboot():
    init(myName)

    return "Начало"

# Завершение работы
def turnOff():
    raise SystemExit