# Imports
from datetime import datetime
from platform import system
import obspython as obs
import time

# Detect if the os is windows
windows = system() == "Windows"
if windows:
    # Import winsound
    from winsound import Beep

# Init variables
source_name = ""
scene_name = ""
switch_scene = False
diff = 0
hours_visible = True
mins_visible = True
secs_visible = True
end_beep = False

# The time formatter
def format_time(secs):
    global hours_visible
    global mins_visible
    global secs_visible
    
    mins = secs // 60
    secs = secs % 60
    hours = mins // 60
    mins = mins % 60

    output = ""
    if hours_visible:
        output += "{:02d}".format(hours)
    if mins_visible:
        if hours_visible:
            output += ":"
        output += "{:02d}".format(mins)
    if secs_visible:
        if hours_visible or mins_visible:
            output += ":"
        output += "{:02d}".format(secs)
        
    return output

# The stopwatch
def stopwatch():
    global source_name
    global diff
    global switch_scene
    global scene_name
    global end_beep
    global windows
    global end_countdown_time

    now_seconds = round(time.time())
    diff = end_countdown_time - now_seconds
        
    if diff == 0:
        obs.timer_remove(stopwatch)
        obs.remove_current_callback()
        if switch_scene == True:
            if scene_name != "":
                obs.obs_frontend_set_current_scene(obs.obs_get_source_by_name(scene_name))
        if end_beep and windows:
            Beep(1000, 200)
    elif diff < 0:
        obs.timer_remove(stopwatch)
        obs.remove_current_callback()
        diff = 0
        
    source = obs.obs_get_source_by_name(source_name)
    try:
        settings = obs.obs_data_create()
        obs.obs_data_set_string(settings, "text", str(format_time(diff)))
        obs.obs_source_update(source, settings)
        obs.obs_data_release(settings)
        obs.obs_source_release(source)
    except UnboundLocalError:
        pass
    
# Update the scene
def scene_updater():
    global source_name

    source = obs.obs_get_source_by_name(source_name)
    obs.obs_source_release(source)
    
# Apply the settings
def settings_updater(settings):
    global source_name
    global end_countdown_time
    global diff
    global switch_scene
    global scene_name
    global hours_visible
    global mins_visible
    global secs_visible
    global end_beep
    global windows

    source_name = obs.obs_data_get_string(settings, "source")
    switch_scene = obs.obs_data_get_bool(settings, "switch_scene")
    scene_name = obs.obs_data_get_string(settings, "end_scene")
    hours_visible = obs.obs_data_get_bool(settings, "hours_visible")
    mins_visible = obs.obs_data_get_bool(settings, "mins_visible")
    secs_visible = obs.obs_data_get_bool(settings, "secs_visible")
    if windows:
        end_beep = obs.obs_data_get_bool(settings, "end_beep")
    
    now = datetime.now()
    now_seconds = (int(now.strftime("%H"))*3600)+(int(now.strftime("%M"))*60)+int(now.strftime("%S"))
    end_countdown_time = round(time.time()) - now_seconds + (int(obs.obs_data_get_int(settings, "end_countdown_hour"))*3600) + (int(obs.obs_data_get_int(settings, "end_countdown_minute"))*60)
    
    if round(time.time()) >= end_countdown_time or obs.obs_data_get_bool(settings, "force_next_day"):
        end_countdown_time += 86400
        
# Restart the timer and apply the settings
def restart_timer_pressed(props, prop):
    global settings_global

    obs.timer_remove(stopwatch)
    obs.timer_add(stopwatch, 200)
    settings_updater(settings_global)

# Description of the script
def script_description():
    global windows
    
    if windows:
        return "Text source: This source will be the timer\nLive update: Apply the settings instantly\nEnd hour and minute: The time in 24-hour on which the timer will end\nHours/Minutes/Seconds visible: Show/hide the hours, minutes and/or seconds\nSwitch to scene: If enabled the scene will be switched to the selected scene\nEnd scene: The scene which will be switched to when the timer ends if 'Switch to scene' is enabled\nEnd beep: Plays a short tone when the timer finishes\nUpdate / (Re)start timer Timer: Applies the settings and (re)starts the timer"
    else:
        return "Text source: This source will be the timer\nLive update: Apply the settings instantly\nEnd hour and minute: The time in 24-hour on which the timer will end\nHours/Minutes/Seconds visible: Show/hide the hours, minutes and/or seconds\nSwitch to scene: If enabled the scene will be switched to the selected scene\nEnd scene: The scene which will be switched to when the timer ends if 'Switch to scene' is enabled\nUpdate / (Re)start timer Timer: Applies the settings and (re)starts the timer"
    
# Update the settings
def script_update(settings):
    global settings_global
    settings_global = settings
    
    if obs.obs_data_get_bool(settings, "live_update"):
        settings_updater(settings)
        
    obs.timer_remove(scene_updater)
    if source_name != "":
        obs.timer_add(scene_updater, 1000)

# Delete the timers when the script is unloaded
def script_unload():
    global source_name
    
    obs.timer_remove(stopwatch)
    obs.timer_remove(scene_updater)

    source = obs.obs_get_source_by_name(source_name)
    obs.obs_source_release(source)

# Set the default settings
def script_defaults(settings):
    global settings_global
    global windows

    obs.obs_data_set_bool(settings, "live_update", True)
    obs.obs_data_set_bool(settings, "switch_scene", False)
    obs.obs_data_set_bool(settings, "hours_visible", True)
    obs.obs_data_set_bool(settings, "mins_visible", True)
    obs.obs_data_set_bool(settings, "secs_visible", True)
    obs.obs_data_set_bool(settings, "force_next_day", False)
    
    if windows:
        obs.obs_data_set_bool(settings, "end_beep", False)

    settings_global = settings

# Setup the script settings
def script_properties():
    global windows
    
    props = obs.obs_properties_create()
    
    # Add live update setting
    obs.obs_properties_add_bool(props, "live_update", "Live update")

    # Add sources select dropdown
    p = obs.obs_properties_add_list(props, "source", "Text Source", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING)

    # Make a list of all the text sources
    sources = obs.obs_enum_sources()

    if sources is not None:
        for source in sources:
            name = obs.obs_source_get_name(source)
            source_id = obs.obs_source_get_unversioned_id(source)
            if source_id == "text_gdiplus" or source_id == "text_ft2_source":
                name = obs.obs_source_get_name(source)
                obs.obs_property_list_add_string(p, name, name)
        obs.source_list_release(sources)

    # Add more properties
    obs.obs_properties_add_int(props, "end_countdown_hour", "End hour:", 0, 23, 1)
    obs.obs_properties_add_int(props, "end_countdown_minute", "End minute:", 0, 59, 1)
    obs.obs_properties_add_bool(props, "force_next_day", "Force next day")

    obs.obs_properties_add_bool(props, "hours_visible", "Hours visible")
    obs.obs_properties_add_bool(props, "mins_visible", "Mins visible")
    obs.obs_properties_add_bool(props, "secs_visible", "Secs visible")

    obs.obs_properties_add_bool(props, "switch_scene", "Switch to scene")

    # Add scenes select dropdown
    s = obs.obs_properties_add_list(props, "end_scene", "End Scene", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING)

    # Make a list off all the scenes
    scenes = obs.obs_frontend_get_scenes()
    if scenes is not None:
        for scene in scenes:
            name = obs.obs_source_get_name(scene)
            obs.obs_property_list_add_string(s, name, name)
        obs.source_list_release(scenes)
    
    # If the os is windows add a beep option
    if windows:
        obs.obs_properties_add_bool(props, "end_beep", "End beep")
    
    # Add the start button
    obs.obs_properties_add_button(props, "restart_button", "Update / (Re)start Timer", restart_timer_pressed)

    return props