"""
Python part of radio playout (pypo)
"""


import importlib
import locale
import logging
import os
import re
import signal
import sys
import telnetlib
import time
from datetime import datetime
from optparse import OptionParser

from api_clients.version1 import AirtimeApiClient as ApiClient
from configobj import ConfigObj

try:
    from queue import Queue
except ImportError:  # Python 2.7.5 (CentOS 7)
    from queue import Queue

from threading import Lock

from . import pure
from .listenerstat import ListenerStat
from .pypofetch import PypoFetch
from .pypofile import PypoFile
from .pypoliquidsoap import PypoLiquidsoap
from .pypomessagehandler import PypoMessageHandler
from .pypopush import PypoPush
from .recorder import Recorder
from .timeout import ls_timeout

LOG_PATH = "/var/log/airtime/pypo/pypo.log"
LOG_LEVEL = logging.INFO
logging.captureWarnings(True)

# Set up command-line options
parser = OptionParser()

# help screen / info
usage = "%prog [options]" + " - python playout system"
parser = OptionParser(usage=usage)

# Options
parser.add_option(
    "-v",
    "--compat",
    help="Check compatibility with server API version",
    default=False,
    action="store_true",
    dest="check_compat",
)

parser.add_option(
    "-t",
    "--test",
    help="Do a test to make sure everything is working properly.",
    default=False,
    action="store_true",
    dest="test",
)

parser.add_option(
    "-b",
    "--cleanup",
    help="Cleanup",
    default=False,
    action="store_true",
    dest="cleanup",
)

parser.add_option(
    "-c",
    "--check",
    help="Check the cached schedule and exit",
    default=False,
    action="store_true",
    dest="check",
)

# parse options
(options, args) = parser.parse_args()

LIQUIDSOAP_MIN_VERSION = "1.1.1"

PYPO_HOME = "/var/tmp/airtime/pypo/"


def configure_environment():
    os.environ["HOME"] = PYPO_HOME
    os.environ["TERM"] = "xterm"


configure_environment()

# need to wait for Python 2.7 for this..
logging.captureWarnings(True)

# configure logging
try:
    # Set up logging
    logFormatter = logging.Formatter(
        "%(asctime)s [%(module)s] [%(levelname)-5.5s]  %(message)s"
    )
    rootLogger = logging.getLogger()
    rootLogger.setLevel(LOG_LEVEL)
    logger = rootLogger

    fileHandler = logging.handlers.RotatingFileHandler(
        filename=LOG_PATH, maxBytes=1024 * 1024 * 30, backupCount=8
    )
    fileHandler.setFormatter(logFormatter)
    rootLogger.addHandler(fileHandler)

    consoleHandler = logging.StreamHandler()
    consoleHandler.setFormatter(logFormatter)
    rootLogger.addHandler(consoleHandler)
except Exception as e:
    print("Couldn't configure logging: {}".format(e))
    sys.exit(1)

# loading config file
try:
    config = ConfigObj("/etc/airtime/airtime.conf")
except Exception as e:
    logger.error("Error loading config file: %s", e)
    sys.exit(1)


class Global:
    def __init__(self, api_client):
        self.api_client = api_client

    def selfcheck(self):
        return self.api_client.is_server_compatible()

    def test_api(self):
        self.api_client.test()


def keyboardInterruptHandler(signum, frame):
    logger = logging.getLogger()
    logger.info("\nKeyboard Interrupt\n")
    sys.exit(0)


@ls_timeout
def liquidsoap_get_info(telnet_lock, host, port, logger):
    logger.debug("Checking to see if Liquidsoap is running")
    try:
        telnet_lock.acquire()
        tn = telnetlib.Telnet(host, port)
        msg = "version\n"
        tn.write(msg.encode("utf-8"))
        tn.write("exit\n".encode("utf-8"))
        response = tn.read_all().decode("utf-8")
    except Exception as e:
        logger.error(e)
        return None
    finally:
        telnet_lock.release()

    return get_liquidsoap_version(response)


def get_liquidsoap_version(version_string):
    m = re.match(r"Liquidsoap (\d+.\d+.\d+)", version_string)

    if m:
        return m.group(1)
    else:
        return None

    if m:
        current_version = m.group(1)
        return pure.version_cmp(current_version, LIQUIDSOAP_MIN_VERSION) >= 0
    return False


def liquidsoap_startup_test(telnet_lock, ls_host, ls_port):

    liquidsoap_version_string = liquidsoap_get_info(
        telnet_lock, ls_host, ls_port, logger
    )
    while not liquidsoap_version_string:
        logger.warning(
            "Liquidsoap doesn't appear to be running!, " + "Sleeping and trying again"
        )
        time.sleep(1)
        liquidsoap_version_string = liquidsoap_get_info(
            telnet_lock, ls_host, ls_port, logger
        )

    while pure.version_cmp(liquidsoap_version_string, LIQUIDSOAP_MIN_VERSION) < 0:
        logger.warning(
            "Liquidsoap is running but in incorrect version! "
            + "Make sure you have at least Liquidsoap %s installed"
            % LIQUIDSOAP_MIN_VERSION
        )
        time.sleep(1)
        liquidsoap_version_string = liquidsoap_get_info(
            telnet_lock, ls_host, ls_port, logger
        )

    logger.info("Liquidsoap version string found %s" % liquidsoap_version_string)


def run():
    logger.info("###########################################")
    logger.info("#             *** pypo  ***               #")
    logger.info("#   Liquidsoap Scheduled Playout System   #")
    logger.info("###########################################")

    # Although all of our calculations are in UTC, it is useful to know what timezone
    # the local machine is, so that we have a reference for what time the actual
    # log entries were made
    logger.info("Timezone: %s" % str(time.tzname))
    logger.info("UTC time: %s" % str(datetime.utcnow()))

    signal.signal(signal.SIGINT, keyboardInterruptHandler)

    api_client = ApiClient()
    g = Global(api_client)

    while not g.selfcheck():
        time.sleep(5)

    success = False
    while not success:
        try:
            api_client.register_component("pypo")
            success = True
        except Exception as e:
            logger.error(str(e))
            time.sleep(10)

    telnet_lock = Lock()

    ls_host = config["pypo"]["ls_host"]
    ls_port = config["pypo"]["ls_port"]

    liquidsoap_startup_test(telnet_lock, ls_host, ls_port)

    if options.test:
        g.test_api()
        sys.exit(0)

    pypoFetch_q = Queue()
    recorder_q = Queue()
    pypoPush_q = Queue()

    pypo_liquidsoap = PypoLiquidsoap(logger, telnet_lock, ls_host, ls_port)

    """
    This queue is shared between pypo-fetch and pypo-file, where pypo-file
    is the consumer. Pypo-fetch will send every schedule it gets to pypo-file
    and pypo will parse this schedule to determine which file has the highest
    priority, and retrieve it.
    """
    media_q = Queue()

    # Pass only the configuration sections needed; PypoMessageHandler only needs rabbitmq settings
    pmh = PypoMessageHandler(pypoFetch_q, recorder_q, config["rabbitmq"])
    pmh.daemon = True
    pmh.start()

    pfile = PypoFile(media_q, config["pypo"])
    pfile.daemon = True
    pfile.start()

    pf = PypoFetch(
        pypoFetch_q, pypoPush_q, media_q, telnet_lock, pypo_liquidsoap, config["pypo"]
    )
    pf.daemon = True
    pf.start()

    pp = PypoPush(pypoPush_q, telnet_lock, pypo_liquidsoap, config["pypo"])
    pp.daemon = True
    pp.start()

    recorder = Recorder(recorder_q)
    recorder.daemon = True
    recorder.start()

    stat = ListenerStat(config)
    stat.daemon = True
    stat.start()

    # Just sleep the main thread, instead of blocking on pf.join().
    # This allows CTRL-C to work!
    while True:
        time.sleep(1)

    logger.info("System exit")