#!/usr/bin/env python3

import atexit
import re
import socket
import subprocess
import time
import os
import signal
import sys
from datetime import datetime

VLC_HOST = "127.0.0.1"
VLC_PORT = 9294

LOG_LEVEL = 0

PIDFILE = "/var/run/phatbeatd.pid"
LOGFILE = "/var/log/phatbeatd.log"
ERRFILE = "/var/log/phatbeatd.err"

SHUTDOWN_HOLD_TIME = 3

LOG_INFO = 0
LOG_WARN = 1
LOG_FAIL = 2
LOG_DEBUG = 3

def log(msg, level=LOG_INFO):
    if level < LOG_LEVEL: return

    sys.stdout.write(str(datetime.now()))
    sys.stdout.write(": ")
    sys.stdout.write(msg)
    sys.stdout.write("\n")
    sys.stdout.flush()

class VLC():

    def __init__(self, host="127.0.0.1", port=9294):
        self.host = host
        self.port = port
        self.current_stream = None
        self.current_state = None
        self.connecting = False
        self.socket = None

    def send(self, command):
        if self.connecting:
            return False

        log("Sending command: {}".format(command), level=LOG_INFO)
        command_string = command + "\n"

        if sys.version_info[0] >= 3:
            command_string = command_string.encode("utf-8")

        try:
            self.socket.send(command_string)

        except socket.error:
            log("Failed to send command to VLC", level=LOG_WARN)
            if self.connect():
                self.send(command)

    def recv(self, length):
        value = self.socket.recv(8192)

        if sys.version_info[0] >= 3:
            value = value.decode("utf-8")

        return value

    def communicate(self, command, response_length=8192):
        self.send(command)
        return self.recv(response_length)

    def get_current_stream(self):
        self.send("status")
        status = self.recv(8192)

        result = re.search("input:\ (.*)\ ", status)
        state = re.search("state\ (.*)\ ", status)

        if state is not None:
            self.current_state = state.group(1)

        if result is not None:
            self.current_stream = result.group(1)
        else:
            self.current_stream = None

        return self.current_stream

    def connect(self):
        if self.connecting:
            return

        self.connecting = True

        if self.socket is not None:
            self.socket.close()

        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        for attempt in range(10):
            try:
                log("Attempting to connect to VLC on {host}:{port}".format(host=self.host, port=self.port))
                self.socket.connect((self.host, self.port))
                log("Connection successful!", level=LOG_INFO)
                self.connecting = False
                return True

            except socket.error:
                time.sleep(1)

        log("Connection failed!", level=LOG_FAIL)
        self.connecting = False
        return False


if __name__ == "__main__":
    stdin = None
    stdout = None
    stderr = None
    fpid = None
    daemonize = True
    pid = None

    def _signal_handler(signal, frame):
        _cleanup()
        sys.exit(0)

    def _cleanup():
        log("Exiting cleanly")

        try:
            os.remove(PIDFILE)
        except OSError:
            pass

        if stdin is not None: stdin.close()
        if stdout is not None: stdout.close()
        if stderr is not None: stderr.close()

    if daemonize:
        try:
            pid = os.fork()
            if pid > 0:
                sys.exit(0)

        except OSError as e:
            log("Fork #1 failed: {} ({})".format(e.errno, e.strerror))
            sys.exit(1)

        os.chdir("/")
        os.setsid()
        os.umask(0)

        try:
            pid = os.fork()
            if pid > 0:
                sys.exit(0)

        except OSError as e:
            log("Fork #2 failed: {} ({})".format(e.errno, e.strerror))
            sys.exit(1)

    pid = os.getpid()
    fpid = open(PIDFILE, 'w')
    fpid.write(str(pid))
    fpid.close()

    log("PHAT BEAT {} running with PID: {}".format("daemon" if daemonize else "process",pid))

    stdin = open("/dev/null", 'r')
    stdout = open(LOGFILE, 'a+')
    stderr = open(ERRFILE, 'a+')

    os.dup2(stdin.fileno(), sys.stdin.fileno())
    os.dup2(stdout.fileno(), sys.stdout.fileno())
    os.dup2(stderr.fileno(), sys.stderr.fileno())

    vlc = VLC(host=VLC_HOST, port=VLC_PORT)

    if vlc.connect():
        import phatbeat
        phatbeat.set_all(0,128,0,0.1)
        phatbeat.show()
        time.sleep(1)
        phatbeat.clear()
        phatbeat.show()

        @phatbeat.on(phatbeat.BTN_VOLDN)
        def pb_volume_down(pin):
            log(vlc.communicate("voldown"))

        @phatbeat.on(phatbeat.BTN_VOLUP)
        def pb_volume_up(pin):
            log(vlc.communicate("volup"))

        @phatbeat.on(phatbeat.BTN_PLAYPAUSE)
        def pb_play_pause(pin):
            log(vlc.communicate("pause"))
            time.sleep(0.1)
            phatbeat.clear()
            phatbeat.show()

        @phatbeat.on(phatbeat.BTN_FASTFWD)
        def pb_fast_forward(pin):
            log(vlc.communicate("next"))

        @phatbeat.on(phatbeat.BTN_REWIND)
        def pb_rewind(pin):
            log(vlc.communicate("prev"))

        @phatbeat.on(phatbeat.BTN_ONOFF)
        def perform_shutdown(pin):
            log(vlc.communicate("pause"))
            os.system("sudo shutdown -h now")

        signal.signal(signal.SIGINT, _signal_handler)
        signal.signal(signal.SIGTERM, _signal_handler)
        signal.pause()

        _cleanup()
        sys.exit(0)

    else:
        _cleanup()
        sys.exit(1)
