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

### BEGIN INIT INFO
# Provides: screenrotation
# Required-Start:    $all
# Required-Stop:
# Default-Start:     4 5
# Default-Stop:      0 1 2 3 6
# Short-Description: Start screen rotation daemons
### END INIT INFO

import sys
import os
import time
import atexit
import signal
import subprocess

from evdev import InputDevice
from select import select


INITVALUE = 999999

  
class Daemon:
    """
    A generic daemon class.
    Usage: subclass the Daemon class and override the run() method
    """
    def __init__(self, pidfile = '/var/run/screenDaemon.pid', stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
        self.stdin = stdin
        self.stdout = stdout
        self.stderr = stderr
        self.pidfile = pidfile

    def daemonize(self):
        """
        do the UNIX double-fork magic
        """
        # Check for a pidfile to see if the daemon already runs
        if os.path.exists(self.pidfile):
            exit(0)
            #raise RuntimeError('Already Running!')

        # do first fork
        try:
            if os.fork() > 0:
                exit(0)
                #raise SystemExit(0) # exit first parent
        except OSError as e:
            exit(1)
            #raise RuntimeError('fork #1 failed: %d (%s)\n' % (e.errno, e.strerror))
     
        # decouple from parent environment
        os.chdir("/") 
        os.setsid() 
        os.umask(0) 
 
        # do second fork
        try: 
            if os.fork() > 0:
                exit(0)
                #raise SystemExit(0) # exit from second parent
        except OSError as e:
            exit(1)
            #raise RuntimeError('fork #2 failed: %d (%s)\n' % (e.errno, e.strerror))
 
        # redirect standard file descriptors
        sys.stdout.flush()
        sys.stderr.flush()

        with open(self.stdin, 'rb', 0) as si:
            os.dup2(si.fileno(), sys.stdin.fileno())
        with open(self.stdout, 'wb', 0) as so:
            os.dup2(so.fileno(), sys.stdout.fileno())
        with open(self.stderr, 'wb', 0) as se:
            os.dup2(se.fileno(), sys.stderr.fileno())
 
        # write pidfile
        with open(self.pidfile, 'w') as pf:
            print(os.getpid(), file=pf)
 
        # Arrange to have the PID file removeed on exit/signal
        atexit.register(lambda: os.remove(self.pidfile))

        signal.signal(signal.SIGTERM, self.__sigterm_handler)

    @staticmethod
    def __sigterm_handler(signo, frame):
        exit(0)
        #raise SystemExit(0)
     
    def start(self):
        """
        Start the daemon
        """
        try:
            # Start the daemon
            self.daemonize()
        except RuntimeError as e:
            print(e, file=sys.stderr)
            exit(0)
            #raise SystemError(0)
 
        self.run()
 
    def stop(self):
        """
        Stop the daemon
        """
        try:
            if os.path.exists(self.pidfile):
                with open(self.pidfile) as fp:
                    os.kill(int(fp.read()), signal.SIGTERM)
            else:
                print('Not Running!', file=sys.stderr)
                exit(1)
                #raise SystemError(1)
        except OSError as e:
            if 'No such process' in str(e) and os.path.exists(self.pidfile):
                os.remove(self.pidfile)
 
    def restart(self):
        """
        Restart the daemon
        """
        self.stop()
        self.start()
 
    def run(self):
        """
        You should override this method when you subclass Daemon. It will be called after the process has been
        daemonized by start() or restart().
        """    

class MyDaemon(Daemon):

    def getDevName(self):
        devname = ""
        if os.path.exists("/proc/bus/input/devices"):
            devfd = open("/proc/bus/input/devices", "r", encoding="utf-8")
            devlist = devfd.readlines()
            devlist2 = "".join(devlist).split("\n\n")
            for dev in devlist2:
                if "Sysfs" in dev and all(map(lambda key : True if key in dev else False, ['C_Gsensor'])):
                    for line in filter(lambda str : str.startswith("H: Handlers="), dev.split("\n")):

                        for unit in line.split("=")[1].split(" "):
                            if unit.startswith("event"):
                                devname = "/dev/input/" + unit
        sys.stdout.write('{}  Daemon monitor with {}\n'.format(time.ctime(), devname))
        return devname

    def beforeRun(self):
        # get current screen status
        currentStatus = ""
        self.monitors = []
        spAnother = subprocess.Popen(['xrandr'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = True)
        for line in ((spAnother.stdout.read()).decode()).split('\n'):
            if " connected " in line:
#                sys.stdout.write('{}  xrandr output: {}\n'.format(time.ctime(), line))
                if line.count("left") == 2:
                    currentStatus = 'left'
                elif line.count("right") == 2:
                    currentStatus = 'right'
                elif line.count("inverted") == 2:
                    currentStatus = 'inverted'
                else:
                    currentStatus = 'normal'

                monitor_name = line[: line.find(" ")]
                self.monitors.append(monitor_name)
        return currentStatus

    def run(self):
        self.monitors = []
        sys.stdout.write('{}  Daemon started with pid {}\n'.format(time.ctime(), os.getpid()))

        # get event greatwall

        devname = self.getDevName()

        currentStatus = ""
        kernelStatus = currentStatus

        while True:
#            sys.stdout.write('{}  ---------------oneshot-----------start----------------------!\n'.format(time.ctime()))

            command = "find /sys/devices/platform/ -type f -name 'gsensor_info'"
            gwsp = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = True)
            gwpath = bytes.decode(gwsp.stdout.read().strip())
            if os.path.exists(gwpath):
                currentStatus = self.beforeRun()
                kernelStatus = currentStatus
                gwsp2 = subprocess.Popen("cat " + gwpath, stdout=subprocess.PIPE, shell = True)
                result = bytes.decode(gwsp2.stdout.read())
                gwx = gwy = gwz = -1
                for l in result.split("\n"):
                    if ":" not in l:
               	        continue
                    if "axis_x" in l:
               	        gwx = int(l.split(":")[1])
                    if "axis_y" in l:
               	        gwy = int(l.split(":")[1])
                    if "axis_z" in l:
               	        gwz = int(l.split(":")[1])
                if gwx >= 768 and gwx <= 1280:
                    kernelStatus = "left"
                elif gwx >= 2816 and gwx <= 3328:
                    kernelStatus = "right"
                else:
               	    kernelStatus = "normal"

            elif os.path.exists("/sys/class/hwmon/hwmon2/xyz_input"):
                currentStatus = self.beforeRun()
                kernelStatus = currentStatus
                # get display status from kernel
                sp = subprocess.Popen("cat /sys/class/hwmon/hwmon2/xyz_input", stdout=subprocess.PIPE, shell = True)
                ## coordinate value is [x, y, z] default [0, -500, 0]
                value = sp.stdout.read()
                if value:
                    coordinate = list(map(int, value.decode().strip().split(' ')))
                else:
                    coordinate = [0, -500, 0]
                sys.stdout.write('{}  Kernel_input X:{}  Kernel_input Y:{}  Kernel_input Z:{}  !\n'.format(time.ctime(), coordinate[0], coordinate[1], coordinate[2]))
                if coordinate[0] in range(-360, 360) and coordinate[1] in range(-520, -305):
                    kernelStatus = 'normal'
                elif coordinate[0] in range(360, 508) and coordinate[1] in range(-305, 265):
                    kernelStatus = 'left'
                elif coordinate[0] in range(-360, 360) and coordinate[1] in range(265, 530):
                    kernelStatus = 'inverted'
                elif coordinate[0] in range(-517, -360) and coordinate[1] in range(-305, 265):
                    kernelStatus = 'right'

            elif os.path.exists("/sys/bus/iio/devices/iio:device0"):
                currentStatus = self.beforeRun()
                kernelStatus = currentStatus
                x = 0; y = -500; z = 0
                processx = subprocess.Popen("cat /sys/bus/iio/devices/iio\:device0/in_accel_x_raw", stdout=subprocess.PIPE, shell = True)
                processx.wait()
                valuex = processx.stdout.read()
 
                processy = subprocess.Popen("cat /sys/bus/iio/devices/iio\:device0/in_accel_y_raw", stdout=subprocess.PIPE, shell = True)
                processy.wait()
                valuey = processy.stdout.read()

#                sys.stdout.write('{}  Kernel_iio_debug X:{}  Kernel_iio_debug Y:{}  Kernel_iio_debug Z:{}  !\n'.format(time.ctime(), valuex, valuey, z))

                if valuex != b'' and valuey != b'':
                    x = int(valuex.decode().strip())
                    y = int(valuey.decode().strip())

                    if x < -360 and y in range(-305, 265):
                        kernelStatus = 'left'
                    elif x > 360 and y in range(-305, 265):
                        kernelStatus = 'right'
                    else:
                        kernelStatus = 'normal'

                #processz = subprocess.Popen("cat /sys/bus/iio/devices/iio:devices0/in_accel_z_raw", stdout=subprocess.PIPE, shell = True)
                #valuez = processz.stdout.read()
                #if valuez is not "":
                #    pass
#                sys.stdout.write('{}  Kernel_iio X:{}  Kernel_iio Y:{}  Kernel_iio Z:{}  !\n'.format(time.ctime(), x, y, z))

            elif devname:
                currentStatus = self.beforeRun()
                kernelStatus = currentStatus
                dev = InputDevice(devname)

                r, w, e = select([dev], [], [])
                for event in dev.read():
                    if (event.type == 3):
                        if (event.code == 0):
                            x = event.value
                        if (event.code == 1):
                            y = event.value
                        if (event.code == 2):
                            z = event.value
#                    sys.stdout.write('{}  Kernel_event X:{}  Kernel_event Y:{}  Kernel_event Z:{}  !\n'.format(time.ctime(), x, y, z))
                    if all((x <= 255, y <= 255, z <= 255)):
                        if z < 47 or z > 209:
                            if y < 47 or y > 209:
                                if x >= 128 and x < 209:
                                    kernelStatus = "normal"
                                elif x > 47 and x < 128:
                                    kernelStatus = "inverted"
                            elif x < 47 or x > 209:
                                if y >= 128 and y < 209:
                                    kernelStatus = "right"
                                elif y > 47 and y < 128:
                                    kernelStatus = "left"
                    else:
                        if all((x != INITVALUE,)):
                            if x >= 768 and x <= 1280:
                                kernelStatus = "left"
                            elif x >= 2816 and x <= 3328:
                                kernelStatus = "right"
                            else:
                                kernelStatus = "normal"
                            x = INITVALUE
                            y = INITVALUE
                            z = INITVALUE

            # if we should rotation screen
            if kernelStatus == currentStatus:
                pass
#                sys.stdout.write('{}  Current Screen Status:{}  Kernel Screen Status:{}  Not Changed!\n'.format(time.ctime(), currentStatus, kernelStatus))
            else:
                sys.stdout.write('{}  Current Screen Status:{}  Kernel Screen Status:{}  Changed!\n'.format(time.ctime(), currentStatus, kernelStatus))
                cmd = ""
                if (len(self.monitors) == 2):
                    cmd = 'xrandr --output {} --rotate {} --output {} --rotate {}'.format(self.monitors[0], kernelStatus, self.monitors[1], kernelStatus)
                elif (len(self.monitors) == 1):
                    cmd = 'xrandr --output {} --rotate {}'.format(self.monitors[0], kernelStatus)
                else:
                    cmd = 'xrandr -o {}'.format(kernelStatus)
                sys.stdout.write('{}  Run Cmd:{}\n'.format(time.ctime(), cmd))
                runsp = subprocess.run(cmd, stderr=subprocess.PIPE, shell = True)
                if runsp.returncode:
                    sys.stdout.write('{}  Screen Rotation Failed!\nError Status: {}\n'.format(time.ctime(), runsp.stderr.decode()))
                else:
                    sys.stdout.write('{}  Screen Rotation Success!\n'.format(time.ctime()))
                    time.sleep(3)
                    #runmouse = subprocess.run('xdotool mousemove_relative 1 1', stderr=subprocess.PIPE, shell = True)
                    #if runmouse.returncode:
                    #    sys.stdout.write('{}  Mouse Move Failed!\nError Status: {}\n'.format(time.ctime(), runmouse.stderr.decode()))
                    #else:
                    #    sys.stdout.write('{}  Mouse Move Success!\n'.format(time.ctime()))

#            sys.stdout.write('{}  ---------------oneshot-----------end----------------------!\n\n'.format(time.ctime()))

            sys.stdout.flush()

            time.sleep(4)
 
if __name__ == "__main__":
    #pf = os.path.join(os.path.expanduser('~'), '.config/screenDaemon.pid')
    #log = os.path.join(os.path.expanduser('~'), '.config/screenDaemon.log')
    pf = "/var/run/screenDaemon.pid"
    log = "/var/log/screenDaemon.log"
    daemon = MyDaemon(pidfile = pf, stdout = log, stderr = log)

    if len(sys.argv) > 2:
        print('Usage: {} [start|stop|restart]'.format(sys.argv[0]), file=sys.stderr)
        exit(0)
        #raise SystemExit(0)

    if len(sys.argv) == 1 or 'start' == sys.argv[1]:

        daemon.start()
    elif 'stop' == sys.argv[1]:
        daemon.stop()
    elif 'restart' == sys.argv[1]:
        daemon.restart()
    else:
        print("usage: %s start|stop|restart" % sys.argv[0], file=sys.stderr)
        exit(0)
        #raise SystemExit(0)
