#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Cadence ALSA-Loop daemon
# Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# For a full copy of the GNU General Public License see the COPYING file

# ------------------------------------------------------------------------------------------------------------
# Imports (Global)

import os
import sys
from signal import signal, SIGINT, SIGTERM
from time import sleep

if True:
    from PyQt5.QtCore import QProcess
else:
    from PyQt4.QtCore import QProcess

# ------------------------------------------------------------------------------------------------------------
# Imports (Custom Stuff)

import jacklib

# --------------------------------------------------
# Auto re-activate if on good kernel

global reactivateCounter
reactivateCounter = -1
isKernelGood      = [int(i) for i in os.uname()[2].split("-", 1)[0].split(".", 2)[:2]] >= [3,8]

# --------------------------------------------------
# Global loop check

global doLoop, doRunNow, useZita, procIn, procOut
doLoop    = True
doRunNow  = True
useZita   = False
procIn    = QProcess()
procOut   = QProcess()
checkFile = "/tmp/.cadence-aloop-daemon.x"

# --------------------------------------------------
# Global JACK variables

global bufferSize, sampleRate, channels
bufferSize = 1024
sampleRate = 44100
channels   = 2

# --------------------------------------------------
# quit on SIGINT or SIGTERM

def signal_handler(sig, frame=0):
    global doLoop
    doLoop = False

# --------------------------------------------------
# listen to jack buffer-size and sample-rate changes

def buffer_size_callback(newBufferSize, arg):
    global doRunNow, bufferSize
    bufferSize = newBufferSize
    doRunNow = True
    return 0

def sample_rate_callback(newSampleRate, arg):
    global doRunNow, sampleRate
    sampleRate = newSampleRate
    doRunNow = True
    return 0

# --------------------------------------------------
# listen to jack2 master switch

def client_registration_callback(clientName, register, arg):
    global doLoop, doRunNow, reactivateCounter, useZita

    if clientName in (b"alsa2jack", b"jack2alsa") and not register:
        if doRunNow or not doLoop:
            return

        if isKernelGood:
            if reactivateCounter == -1:
                reactivateCounter = 0
                print("NOTICE: %s has been stopped, waiting 5 secs to reactivate" % ("zita-a2j/j2a" if useZita else "alsa_in/out"))
        elif doLoop:
            doLoop = False
            print("NOTICE: %s has been stopped, quitting now..." % ("zita-a2j/j2a" if useZita else "alsa_in/out"))

# --------------------------------------------------
# listen to jack shutdown

def shutdown_callback(arg):
    global doLoop
    doLoop = False

# --------------------------------------------------
# run alsa_in and alsa_out
def run_alsa_bridge():
    global reactivateCounter
    global bufferSize, sampleRate, channels
    global procIn, procOut
    global useZita

    if procIn.state() != QProcess.NotRunning:
        procIn.terminate()
        procIn.waitForFinished(1000)
    if procOut.state() != QProcess.NotRunning:
        procOut.terminate()
        procOut.waitForFinished(1000)

    reactivateCounter = -1

    if useZita:
        procIn.start("env",  ["JACK_SAMPLE_RATE=%i" % sampleRate, "JACK_PERIOD_SIZE=%i" % bufferSize, "zita-a2j", "-d", "hw:Loopback,1,0", "-r", "%i" % sampleRate, "-p", "%i" % bufferSize, "-j", "alsa2jack", "-c", "%i" % channels])
        procOut.start("env", ["JACK_SAMPLE_RATE=%i" % sampleRate, "JACK_PERIOD_SIZE=%i" % bufferSize, "zita-j2a", "-d", "hw:Loopback,1,1", "-r", "%i" % sampleRate, "-p", "%i" % bufferSize, "-j", "jack2alsa", "-c", "%i" % channels])
    else:
        procIn.start("env",  ["JACK_SAMPLE_RATE=%i" % sampleRate, "JACK_PERIOD_SIZE=%i" % bufferSize, "alsa_in",  "-d", "cloop", "%i" % sampleRate, "-p", "%i" % bufferSize, "-j", "alsa2jack", "-q", "1", "-c", "%i" % channels])
        procOut.start("env", ["JACK_SAMPLE_RATE=%i" % sampleRate, "JACK_PERIOD_SIZE=%i" % bufferSize, "alsa_out", "-d", "ploop", "%i" % sampleRate, "-p", "%i" % bufferSize, "-j", "jack2alsa", "-q", "1", "-c", "%i" % channels])

    if procIn.waitForStarted():
        sleep(1)
        jacklib.connect(client, "alsa2jack:capture_1", "system:playback_1")
        jacklib.connect(client, "alsa2jack:capture_2", "system:playback_2")

    if procOut.waitForStarted():
        sleep(1)
        jacklib.connect(client, "system:capture_1", "jack2alsa:playback_1")
        jacklib.connect(client, "system:capture_2", "jack2alsa:playback_2")

#--------------- main ------------------
if __name__ == '__main__':

    for i in range(len(sys.argv)):
        if i == 0: continue

        argv = sys.argv[i]

        if argv == "--zita":
            useZita = True
        elif argv.startswith("--channels="):
            chStr = argv.replace("--channels=", "")

            if chStr.isdigit():
                channels = int(chStr)

    # Init JACK client
    client = jacklib.client_open("cadence-aloop-daemon", jacklib.JackUseExactName, None)

    if not client:
        print("cadence-aloop-daemon is already running, delete \"/tmp/.cadence-aloop-daemon.x\" to close it")
        quit()

    if jacklib.JACK2:
        jacklib.set_client_registration_callback(client, client_registration_callback, None)

    jacklib.set_buffer_size_callback(client, buffer_size_callback, None)
    jacklib.set_sample_rate_callback(client, sample_rate_callback, None)
    jacklib.on_shutdown(client, shutdown_callback, None)
    jacklib.activate(client)

    # Quit when requested
    signal(SIGINT, signal_handler)
    signal(SIGTERM, signal_handler)

    # Get initial values
    sampleRate = jacklib.get_sample_rate(client)
    bufferSize = jacklib.get_buffer_size(client)

    # Create check file
    if not os.path.exists(checkFile):
        os.mknod(checkFile)

    # Keep running until told otherwise
    firstStart = True
    while doLoop and os.path.exists(checkFile):
        if doRunNow:
            if firstStart:
                firstStart = False
                print("cadence-aloop-daemon started, using %s and %i channels" % ("zita-a2j/j2a" if useZita else "alsa_in/out", channels))

            run_alsa_bridge()
            doRunNow = False

        elif isKernelGood and reactivateCounter >= 0:
            if reactivateCounter == 5:
                reactivateCounter = -1
                doRunNow = True
            else:
                reactivateCounter += 1

        sleep(1)

    # Close JACK client
    jacklib.deactivate(client)
    jacklib.client_close(client)

    if os.path.exists(checkFile):
        os.remove(checkFile)

    if procIn.state() != QProcess.NotRunning:
        procIn.terminate()
        procIn.waitForFinished(1000)
    if procOut.state() != QProcess.NotRunning:
        procOut.terminate()
        procOut.waitForFinished(1000)
