#!/usr/bin/python3
# coding: utf8
# LAVA Controls Copyright 2022 LAVA Controls - Stephen Loeckle

import argparse
import binascii
import codecs
import collections
import configparser
import datetime
import inspect
import logging
import os
import platform
import serial
import socket
import sys
import traceback
from time import time, sleep
from multiprocessing import Process, Queue, current_process
import lavacontrolscorelib as lccore
import lavacontrolsneclib as lcnec
import lavacontrolssamsunglib as lcsam

osplatform = platform.system().lower()

if osplatform == 'linux':
    import pwd
    uid = pwd.getpwnam('lavacontrols')[2]
    os.setuid(uid)
    lavacontrolsconfigfile = ('/etc/lavacontrols/lavacontrols.conf')
elif osplatform == 'windows':
    application_path = os.getcwd()
    lavacontrolsconfigfile = ('{}\\conf\\lavacontrols.conf'.format(application_path))

outputargs = {}

outputargs['debugoutput'] = 0
outputargs['macaddressoutput'] = 0
outputargs['modelserialoutput'] = 0
outputargs['firmwareoutput'] = 0
outputargs['tempoutput'] = 0
outputargs['inputoutput'] = 0
outputargs['tileoutput'] = 0
outputargs['poweroutput'] = 0
outputargs['volumeoutput'] = 0

PORT = 7142

def DisplayWorker(sysinfo,  outputargs, ip=None, port=None, socketid=0, q=None, id=None):

    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    print(gotthefunc, msg)
    
    name = current_process().name
    ts = time()
    s = {}
    s[socketid] = None
    output = {}
    
#    while True:
#        if sysinfo['SYSTEM']['CONNECTION'] == 'ip':
#            try:
#                s[socketid] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#                s[socketid].settimeout(30)
#                s[socketid].connect((ip, 7142))
#            except:
#                msg = ('Network socket busy or in error state. Trying again.')
#                print(gotthefunc, msg)
#                err = traceback.format_exc()
#                msg = ('Error in {} Detail: {}'.format(gotthefunc,err))
#                print(gotthefunc, msg)
#                sleep(1)
#                pass
#            else:
#                break
#        elif sysinfo['SYSTEM']['CONNECTION'] == 'serial':
#            try:
#                s[socketid] = serial.Serial(sysinfo['SYSTEM']['SERIALPORT'], baudrate=9600, bytesize=8, parity=serial.PARITY_NONE, stopbits=1, timeout=5)
#            except:
#                msg = ('Serial port busy or in error state. Trying again.')
#                print(gotthefunc, msg)
#                err = traceback.format_exc()
#                msg = ('Error in {} Detail: {}'.format(gotthefunc,err))
#                print(gotthefunc, msg)
#                sleep(1)
#                pass
#            else:
#                break

    try:
        print('checking make')
        displayid, make = lccore.checkmake(sysinfo, ip, id)
    except:
        err = traceback.format_exc()
        msg = ('Error in {} Detail: {}'.format(gotthefunc,err))
        print(msg)
        sys.exit()
    else:
        print(displayid,make)
    
    sysinfo['D{}'.format(displayid)] = {}
    sysinfo['D{}'.format(displayid)]['IP'] = ip
    now = str(datetime.datetime.now())
    if outputargs['debugoutput'] == 1:
        output[socketid,5]=('    Internal Process: %s' % name)

    output[socketid,6]=('    ID: {}'.format(displayid))
    output[socketid,7]=('    IP: {}'.format(ip))
    if make == 'nec':
        dispconn = lcnec.DisplayInfo(sysinfo, displayid)
        inputconn = lcnec.Input(sysinfo, displayid)
        coreconn = lcnec.CoreDisplaySettings(sysinfo,displayid)
        volconn = lcnec.Volume(sysinfo, displayid)
        
        if outputargs['macaddressoutput'] == 1:
            macaddress = dispconn.getmacaddress()
            output[socketid,8]=('    MAC: %s' % macaddress)
    elif make == 'sam':
        dispconn = lcsam.DisplayInfo(sysinfo, displayid)
        inputconn = lcsam.Input(sysinfo, displayid)
        coreconn = lcsam.CoreDisplaySettings(sysinfo, displayid)
        volconn = lcsam.Volume(sysinfo, displayid)

#    output[socketid,4]=('Display %s: ' % ip)


    if outputargs['modelserialoutput'] == 1:
        model = dispconn.getmodel()
        serialnumber = dispconn.getserialnumber()
        output[socketid,9]=('    Model: %s' % model)
        output[socketid,10]=('    Serial: %s' % serialnumber)
    if outputargs['firmwareoutput'] == 1:
        firmware1,firmware2,firmware3,firmware4 = dispconn.getfirmware()
        output[socketid,11]=('    Firmware1: %s' % firmware1)
        output[socketid,12]=('    Firmware2: %s' % firmware2)
        output[socketid,13]=('    Firmware3: %s' % firmware3)
        output[socketid,14]=('    Firmware4: %s' % firmware4)
    if outputargs['tempoutput'] == 1:
        temperature1,temperature2,temperature3 = dispconn.gettemperature()
        output[socketid,15]=('    Temp1: %s' % temperature1)
        output[socketid,16]=('    Temp2: %s' % temperature2)
        output[socketid,17]=('    Temp3: %s' % temperature3)
#    if make == 'nec':
#        coreconn = lcnec.CoreDisplaySettings(sysinfo,displayid)
#    elif make == 'sam':
#        coreconn = lcsam.CoreDisplaySettings(sysinfo, displayid)
        
    if outputargs['inputoutput'] == 1:
#        inputconn = lcnec.Input(sysinfo, displayid)
        inputname, inputcode = inputconn.getinput()
        output[socketid,18]=('    lcnec.Input: {}:{}'.format(inputname,inputcode))
    if outputargs['tileoutput'] == 1 and make == 'nec':
        tmconn = lcnec.Tiling(sysinfo, displayid)  
        tmcomp = tmconn.getbcmode()
        tmmode = tmconn.getmode()
        tmhsize = tmconn.gethsize()
        tmvsize = tmconn.getvsize()
        tmpos = tmconn.getpos()
        output[socketid,19]=('    Tile Mode: %s' % tmmode)
        output[socketid,20]=('    Tile Comp: %s' % tmcomp)
        output[socketid,21]=('    Tile HSize: %s' % tmhsize)
        output[socketid,22]=('    Tile VSize: %s' % tmvsize)
        output[socketid,23]=('    Tile Position: %s' % tmpos)
    if outputargs['poweroutput'] == 1:
        power = coreconn.getpower()
        output[socketid,24]=('    Power Status: %s' % power)
    if outputargs['volumeoutput'] == 1:
#        volconn = lcnec.Volume(sysinfo, displayid)
        volume = volconn.getvolume()
        output[socketid,25]=('    Volume Level: %s' % volume)
    if outputargs['debugoutput'] == 1:
        output[socketid,26]=('Process Took {}'.format(time() - ts))
        now = datetime.datetime.now()
        output[socketid,27]=("{0} Work finished: {1}".format(name,str(now)))
    if outputargs['debugoutput'] == 1:
        output[socketid,28]=("{0} Closing network socket {1}".format(name,socketid))
#    s[socketid].close()
    if outputargs['debugoutput'] == 1:
        output[socketid,29]=("{0} Closed network socket {1}".format(name,socketid))
    if sysinfo['SYSTEM']['CONNECTION'] == 'ip':
        q.put(output)
    elif sysinfo['SYSTEM']['CONNECTION'] == 'serial':
        return(output)

def main():

    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    print(gotthefunc, msg)

    parser = argparse.ArgumentParser(description='LAVA Controls - getdisplaystatus')
    parser.add_argument('-a',action="store_true", default=False, dest='all', help = 'All Available Output')
    parser.add_argument('-d', action="store_true", default=False, dest='debug', help = 'Debug output')
    parser.add_argument('-f',action="store_true", default=False, dest='firmware', help = 'Firmware Output Only')
    parser.add_argument('-in', action="store_true", default=False, dest='input', help = 'lcnec.Input Status Output Only')
    parser.add_argument('-mac', action="store_true", default=False, dest='mac', help = 'MAC Address Only')
    parser.add_argument('-ms', action="store_true", default=False, dest='ms', help = 'Model and Serial Output Only')
    parser.add_argument('-p',action="store_true", default=False, dest='power', help = 'Power Status Output Only')
    parser.add_argument('-pdcomms',action="store_true", default=False, dest='pdcomms', help = 'Output PD Comms Config')
    parser.add_argument('-t',action="store_true", default=False, dest='tile', help = 'Tile Status Output Only')
    parser.add_argument('-temp',action="store_true", default=False, dest='temp', help = 'Temp Status Output Only')
    parser.add_argument('-v',action="store_true", default=False, dest='volume', help = 'lcnec.Volume Status Output Only')
    cliargs = parser.parse_args()

    if cliargs.debug:
        logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        logging.getLogger('requests').setLevel(logging.CRITICAL)
        logger = logging.getLogger(__name__)
        outputargs['debugoutput'] = 1
    else:
        logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        logging.getLogger('requests').setLevel(logging.CRITICAL)
        logger = logging.getLogger(__name__)
        outputargs['debugoutput'] = 0

    if cliargs.all:
        outputargs['macaddressoutput'] = 1
        outputargs['modelserialoutput'] = 1
        outputargs['firmwareoutput'] = 1
        outputargs['tempoutput'] = 1
        outputargs['inputoutput'] = 1
        outputargs['tileoutput'] = 1
        outputargs['poweroutput'] = 1
        outputargs['volumeoutput'] = 1

    if cliargs.firmware:
        outputargs['firmwareoutput'] = 1

    if cliargs.input:
        outputargs['inputoutput'] = 1
        
    if cliargs.mac:
        outputargs['macaddressoutput'] = 1

    if cliargs.ms:
        outputargs['modelserialoutput'] = 1

    if cliargs.pdcomms:
        outputargs['pdcommsoutput'] = 1

    if cliargs.power:
        outputargs['poweroutput'] = 1

    if cliargs.tile:
        outputargs['tileoutput'] = 1
        
    if cliargs.temp:
        outputargs['tempoutput'] = 1

    if cliargs.volume:
        outputargs['volumeoutput'] = 1

    config = configparser.ConfigParser()
    config.read([lavacontrolsconfigfile])
    sysinfo = {}
    sysinfo['SYSTEM'] = {}
    sysinfo['WALL'] = {}
    sysinfo['SYSTEM']['SERIALPORT'] = None
    try:
        sysinfo['SYSTEM']['CONNECTION'] = config.get('system', 'connection')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No connection type (ip or serial) specified in configuration file. Defaulting to IP!')
        print(gotthefunc, msg)
        sysinfo['SYSTEM']['CONNECTION'] = 'ip'
        pass
    except configparser.Error as e:
        msg = ('Config read entry system connection error! Halting!')
        print(gotthefunc, msg)
        sys.exit()
    except:
        msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
        print(gotthefunc, msg)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        print(gotthefunc, msg)
        sys.exit()
    else:
        msg = ('CONFIG: Got connection type {}'.format(sysinfo['SYSTEM']['CONNECTION']))
        print(gotthefunc, msg)    
    try:
        sysinfo['SYSTEM']['MODE'] = config.get('system', 'mode')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No mode specified in configuration file. Defaulting to wall.')
        print(gotthefunc, msg)
        sysinfo['SYSTEM']['MODE'] = 'wall'
        pass
    except configparser.Error as e:
        msg = ('Config read entry system mode error. Moving on.')
        print(gotthefunc, msg)
        sysinfo['SYSTEM']['MODE'] = 'wall'
        pass
    except:
        msg = ('Error in {0} Reading Config! Moving on.'.format(gotthefunc))
        print(gotthefunc, msg)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        print(gotthefunc, msg)
        sysinfo['SYSTEM']['MODE'] = 'wall'
        pass
    else:
        msg = ('CONFIG: Got mode {}'.format(sysinfo['SYSTEM']['MODE']))
        print(gotthefunc, msg)
    if sysinfo['SYSTEM']['MODE'] == 'wall':
        try:
            sysinfo['WALL']['LAYOUT'] = config.get('wall', 'layout')
        except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
            msg = ('No layout specified in configuration file. Halting!')
            print(gotthefunc, msg)
            sys.exit()
        except configparser.Error as e:
            msg = ('Config read entry wall layout error. Halting!')
            print(gotthefunc, msg)
            sys.exit()
        except:
            msg = ('Error in {0} Reading Config! Moving on.'.format(gotthefunc))
            print(gotthefunc, msg)
            err = traceback.format_exc()
            msg = ('Error Detail: {0}'.format(err))
            print(gotthefunc, msg)
            sys.exit()
        else:
            msg = ('CONFIG: Got mode {}'.format(sysinfo['WALL']['LAYOUT']))
            print(gotthefunc, msg)
        
    tsmain = time()
    socketnum = 0
    
        
    if sysinfo['SYSTEM']['CONNECTION'] == 'ip':
        # Load display IP addresses from configuration
        IP = {}
        ipnum = 0
        for key, val in config.items('network'):
            if 'ip' in key:
                IP[ipnum]=val
                ipnum += 1
        q = Queue()
        workers = []
        for ipid, ipaddr in IP.items():
            now = datetime.datetime.now()
    #        print("Process started: %s" % str(now))
    #        logger.info('Queueing {}'.format(ipaddr))
            worker = Process(target=DisplayWorker, args=(sysinfo, outputargs, ipaddr, PORT, socketnum, q))
            workers.append(worker)
            worker.start()
            socketnum += 1
        results = {}
        for i in range(socketnum):
            results.update(q.get())
        for w in workers:
            w.join()
    elif sysinfo['SYSTEM']['CONNECTION'] == 'serial':
        try:
            sysinfo['SYSTEM']['SERIALPORT'] = config.get('serial', 'port')
        except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
            msg = ('No serial port specified in configuration file. Defaulting to /dev/ttyUSB0')
            print(gotthefunc, msg)
            sysinfo['SYSTEM']['SERIALPORT'] = '/dev/ttyUSB0'
            pass
        except configparser.Error as e:
            msg = ('Config read entry system serial port error! Halting!')
            print(gotthefunc, msg)
            sys.exit()
        except:
            msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
            print(gotthefunc, msg)
            err = traceback.format_exc()
            msg = ('Error Detail: {0}'.format(err))
            print(gotthefunc, msg)
            sys.exit()
        if sysinfo['SYSTEM']['MODE'] == 'single':
            results = DisplayWorker(sysinfo, outputargs)
        elif sysinfo['SYSTEM']['MODE'] == 'wall':
            layout = sysinfo['WALL']['LAYOUT'].split('x')
            displaycount = int(int(layout[0])+int(layout[1]))
            x = 1
            y = 0
            results = {}
            while x < displaycount:
                result = DisplayWorker(sysinfo, outputargs, socketid=y, id=x)
                lccore.update_dict(results, result)
                x += 1
                y += 1
    else:
        msg = ('Invalid connection type specified in configuration file. Halting!')
        print(gotthefunc, msg)
        sys.exit()

    sortedresults = collections.OrderedDict(sorted(results.items()))
    for key,val in sortedresults.items():
        if outputargs['debugoutput'] == 1:
            print("{0} = {1}".format(key,val))
        else:
            print("{}".format(val))
    if outputargs['debugoutput'] == 1:
        print('Took {}'.format(time() - tsmain))

if __name__ == '__main__':
   main()