import obspython as S  # studio
from types import SimpleNamespace
from ctypes import *
from ctypes.util import find_library

obsffi = CDLL(find_library("obs"))
G = SimpleNamespace()
G.enabled = False
G.tick = 16
G.update = 300
G.delay = 0
G.lock = False
OBS_FADER_LOG = 2
G.duration = 0
G.noise = -100
G.duration_local = 0
G.release_local = 0
G.source = ""
G.volmeter = "not yet initialized volmeter instance"
G.release = 0
G.threshold = -40

def wrap(funcname, restype, argtypes):
    """Simplify wrapping ctypes functions in obsffi"""
    func = getattr(obsffi, funcname)
    func.restype = restype
    func.argtypes = argtypes
    globals()["g_" + funcname] = func


class Source(Structure):
    pass


class Volmeter(Structure):
    pass


volmeter_callback_t = CFUNCTYPE(
    None, c_void_p, POINTER(c_float), POINTER(c_float), POINTER(c_float)
)
wrap("obs_get_source_by_name", POINTER(Source), argtypes=[c_char_p])
wrap("obs_source_release", None, argtypes=[POINTER(Source)])
wrap("obs_volmeter_create", POINTER(Volmeter), argtypes=[c_int])
wrap("obs_volmeter_destroy", None, argtypes=[POINTER(Volmeter)])
wrap(
    "obs_volmeter_add_callback",
    None,
    argtypes=[POINTER(Volmeter), volmeter_callback_t, c_void_p],
)
wrap(
    "obs_volmeter_remove_callback",
    None,
    argtypes=[POINTER(Volmeter), volmeter_callback_t, c_void_p],
)
wrap(
    "obs_volmeter_attach_source",
    c_bool,
    argtypes=[POINTER(Volmeter), POINTER(Source)],
)


@volmeter_callback_t
def volmeter_callback(data, mag, peak, input):
    G.noise = float(peak[0])

def script_defaults(settings):
     if S.obs_data_get_int(settings, "update") is None:
        S.obs_data_set_int(settings, "update", 300)
     if S.obs_data_get_int(settings, "release") is None:
        S.obs_data_set_int(settings, "release", 300)
     if S.obs_data_get_int(settings, "delay") is None:
        S.obs_data_set_int(settings, "delay", 0)
     if S.obs_data_get_string(settings, "source") is None:
        S.obs_data_set_string(settings, "source", "Desktop")
     if S.obs_data_get_double(settings, "thr") is None:
        S.obs_data_set_double(settings,"thr",-40)


def script_load(settings):
    """If you want the script to be always active, then delete this part"""
    S.obs_data_set_bool(settings, "enabled", False)

def script_properties():
    props = S.obs_properties_create()
    S.obs_properties_add_bool(props, "enabled", "Enabled")
    S.obs_properties_add_int(props, "delay", "Start Delay (ms)", 0, 8000,10)
    S.obs_properties_add_int(props, "update", "Update Time (ms)", G.tick, 8000,10)
    S.obs_properties_add_int(props, "release", "Release Time (ms)", 0, 8000,10)
    S.obs_properties_add_text(props, "source", "Audio Source \n Name",S.OBS_TEXT_DEFAULT)
    S.obs_properties_add_float_slider(props,"thr","Threshold (db)",-100.0,0,0.01)
    return props

def script_save(settings):
    script_update(settings)

def script_update(settings):
    if S.obs_data_get_bool(settings, "enabled") is not G.enabled:
        G.enabled = S.obs_data_get_bool(settings, "enabled")
    if S.obs_data_get_int(settings, "update") is not G.update:
        G.update = S.obs_data_get_int(settings, "update")
    if S.obs_data_get_int(settings, "delay") is not G.delay:
        G.delay = S.obs_data_get_int(settings, "delay")
    if S.obs_data_get_int(settings, "release") is not G.release:
        G.release = S.obs_data_get_int(settings, "release")
    if S.obs_data_get_string(settings, "source") is not G.source:
        G.source = S.obs_data_get_string(settings, "source")
    if S.obs_data_get_double(settings, "thr") is not G.threshold:
        G.threshold = S.obs_data_get_double(settings, "thr")


def event_loop():
    if not G.enabled:
        return
    if G.duration > G.delay:
        if not G.lock:
            print("Initializing, if this message shows up more than once, make sure the source name is correct")
            source = g_obs_get_source_by_name(G.source.encode("utf-8"))
            G.volmeter = g_obs_volmeter_create(OBS_FADER_LOG)
            g_obs_volmeter_add_callback(G.volmeter, volmeter_callback, None)
            if g_obs_volmeter_attach_source(G.volmeter, source):
                g_obs_source_release(source)
                G.lock = True
                print("Attached to source")
                return
        if not S.obs_frontend_recording_active():
             return
        G.duration_local += G.tick
        if G.duration_local >= G.update :
            if G.noise >= G.threshold:
                G.release_local = 0
                if S.obs_frontend_recording_paused():
                    S.obs_frontend_recording_pause(False)
                    print("Resume recording")
            else:
                G.release_local += G.update
                if G.release_local >= G.release :
                    G.release_local = 0
                    if not S.obs_frontend_recording_paused():
                        S.obs_frontend_recording_pause(True)
                        print("Pause recording")
            G.duration_local = G.tick
    else:
        G.duration += G.tick


def script_unload():
    g_obs_volmeter_remove_callback(G.volmeter, volmeter_callback, None)
    g_obs_volmeter_destroy(G.volmeter)


S.timer_add(event_loop, G.tick)