##
# Copyright (c) 2021 Xilinx, Inc.
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
# associated documentation files (the "Software"), to deal in the Software without restriction, including 
# without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
# copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to 
# the following conditions: 
# 
# The above copyright notice and this permission notice shall be included in all copies or substantial 
# portions of the Software. 
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# 
#
#
##
import socket
import threading
import os,sys,inspect,importlib,json
import logging
import datetime
from backend import header
from multiprocessing import Lock
import re

currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
apidir = os.path.join(parentdir,"api")
connectionstatus = 0
numberofnps = 0

boardname = ""
xsdbport = ""
runsyswatch = False

class SocketClient:
    def __init__(self, host, port, active_user, sock=None, connectionstatus=0, boardpermission="M"):
        logging.basicConfig(level=logging.DEBUG)
        self.sock = sock
        self.host = host
        self.port = port
        self.log = logging.getLogger("__Socket__")
        self.mute = Lock()
        self.connectionstatus = connectionstatus
        self.active_user = active_user
        self.boardpermission = boardpermission
        self.mebaseaddress = ["0x20000000000", "0x20000000000", "0x20000000000", "0x20000000000"]
        self.defaulttarget = "Versal"
        self.beampowertarget = "PSM Microblaze"
        #self.get_gpio_switch()

    def createLog(self,filename,command):
        currenttime = datetime.datetime.now().strftime("%Y-%m-%d")
        loggedinuser = self.active_user
        logdirectory = os.path.join(apidir,"Log",loggedinuser)
        if not os.path.exists(logdirectory):
            os.makedirs(logdirectory)
        self.logfile = open(os.path.join(logdirectory, "%s.txt" % filename), "a")
        self.logfile.write(str(command)+"\n")
        self.logfile.close()

    def connect(self):
        if (self.sock != None):
            self.sock.close()
        self.sock=None
        self.connectionstatus = 0
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.log.debug('*** Connecting to %s server port ******%d. ' % (self.host, self.port))
        self.createLog("connectionstatus",'*** Connecting to '+str(self.host)+' server port ******'+str(self.port)+'. ')
        try:
            self.sock.connect((self.host, self.port))
            self.connectionstatus = 1
            self.log.debug("**** Connection to server successful")

        except socket.error as msg:
            self.sock.close()
            self.sock=None
            self.connectionstatus = 0
            self.log.debug('**** ERROR: '+str(msg)+' while connecting to %s server at port %d.' % (self.host, self.port))
            return 1;
        return 0;
    def reconnect(self):
        self.log.debug("**** Re-connecting to server...")
        if not self.connectionstatus:
            self.connect()

    def disconnect(self):
        if (self.sock != None):
            self.sock.close()
            self.connectionstatus = 0
        self.sock=None
        self.createLog("connectionstatus", '*** successfully disconnected. ')

    def send_msg(self, ln, cmdsrc, target="",beam_target=""):
        self.mute.acquire()
        data = 'error'
        flag = 0
        self.createLog(cmdsrc, ln)
        if target == "":
            target = self.defaulttarget
        # print("target set here", target)
        os.system("gpioset %s %s=0 %s=0" %( self.gpio_chip, self.gpio_pins[0], self.gpio_pins[1]))
        to_bytes = str.encode("ta\n")
        self.sock.send(to_bytes)
        data = self.sock.recv(4096).decode("utf-8").strip()
        # if "ta" not in ln:
        if beam_target!="":
            to_bytes = str.encode("targets -set -filter {name=~\"%s*\" }\n"%beam_target)
        else:
            to_bytes = str.encode("targets -set -filter {name=~\"%s*\" }\n"%target)
        self.sock.send(to_bytes)
        data = self.sock.recv(4096).decode("utf-8").strip()
        to_bytes = str.encode("ta\n")
        self.sock.send(to_bytes)
        data = self.sock.recv(4096).decode("utf-8").strip()
        if "mrd" in ln:
            ln = "return [concat '["+ln.replace('\n', '')+"]' ' DONE']\n"
        if "mrd" in ln or "source" in ln:
            flag = 1
        if(self.connectionstatus and (self.sock != None) and (len(ln) > 0)):
            if self.boardpermission == "S" and "mwr" in ln:
                data = "Error: No write permission"
            else:
                to_bytes = str.encode(ln)
                try:
                    self.sock.send(to_bytes)
                    data = self.sock.recv(4096).decode("utf-8").strip()
                    if "error " not in str(data):
                        if flag:
                            while " DONE" not in str(data):
                                data += self.sock.recv(4096).decode("utf-8").strip()
                    if " DONE" in str(data):
                        data = data.replace(" DONE", "")
                except socket.error as msg:
                    self.log.debug('**** ERROR: '+str(msg)+' while sending message to %s server at port %d.' % (self.host, self.port))
                    self.connectionstatus = 0
                    self.reconnect()
        self.createLog(cmdsrc,str(data))
        to_bytes = str.encode("ta\n")
        self.sock.send(to_bytes)
        targetdata = self.sock.recv(4096).decode("utf-8").strip()
        if beam_target == "":
            targetdata = targetdata.replace("\\n","\n")
            # targetdata = " ".join(((targetdata).replace("\\r", "").replace("\\n", "_").replace("\\", "").replace("\'", "")).split())
            targetdata = re.search("\*.*",targetdata,re.MULTILINE)
            if targetdata != None:
                self.defaulttarget = targetdata.group(0).split("*")[1].strip()
            # print("-------------",updated_target)
        os.system("gpioget %s %s %s > /dev/null" %( self.gpio_chip, self.gpio_pins[0], self.gpio_pins[1]))
        self.mute.release()
        return data
    
    def di_send_msg(self, ln, cmdsrc, target="",beam_target="", is_tcl_script=False):
        self.mute.acquire()
        data = 'error'
        flag = 0
        self.createLog(cmdsrc, ln)
        if target == "":
            target = self.defaulttarget
        # print("target set here", target)
        os.system("gpioset %s %s=0 %s=0" %( self.gpio_chip, self.gpio_pins[0], self.gpio_pins[1]))
        to_bytes = str.encode("ta\n")
        self.sock.send(to_bytes)
        data = self.sock.recv(4096).decode("utf-8").strip()
        # if "ta" not in ln:
        if beam_target!="":
            to_bytes = str.encode("targets -set -filter {name=~\"%s*\" }\n"%beam_target)
        else:
            to_bytes = str.encode("targets -set -filter {name=~\"%s*\" }\n"%target)
        self.sock.send(to_bytes)
        data = self.sock.recv(4096).decode("utf-8").strip()
        to_bytes = str.encode("ta\n")
        self.sock.send(to_bytes)
        data = self.sock.recv(4096).decode("utf-8").strip()
        if "print" in ln or "source":
            ln = "return [concat '["+ln.replace('\n', '')+"]' ' DONE']\n"
        if "print" in ln or "source" in ln:
            flag = 1
        if(self.connectionstatus and (self.sock != None) and (len(ln) > 0)):
            if self.boardpermission == "S" and "mwr" in ln:
                data = "Error: No write permission"
            else:
                to_bytes = str.encode(ln)
                try:
                    self.sock.send(to_bytes)
                    data = self.sock.recv(4096).decode("utf-8").strip()
                    #if "error " not in str(data):
                    if not is_tcl_script and flag:
                        while "DONE" not in str(data):
                            data += self.sock.recv(4096).decode("utf-8").strip()
                    if "DONE" in str(data):
                        data = data.replace("DONE", "")
                except socket.error as msg:
                    self.log.debug('**** ERROR: '+str(msg)+' while sending message to %s server at port %d.' % (self.host, self.port))
                    self.connectionstatus = 0
                    self.reconnect()
        self.createLog(cmdsrc,str(data))
        to_bytes = str.encode("ta\n")
        self.sock.send(to_bytes)
        targetdata = self.sock.recv(4096).decode("utf-8").strip()
        if beam_target == "":
            targetdata = targetdata.replace("\\n","\n")
            targetdata = re.search("\*.*",targetdata,re.MULTILINE)
            if targetdata != None:
                self.defaulttarget = targetdata.group(0).split("*")[1].strip()
        os.system("gpioget %s %s %s > /dev/null" %( self.gpio_chip, self.gpio_pins[0], self.gpio_pins[1]))
        self.mute.release()
        return data
    
    def get_msg(self):
        data = 'error'
        if (self.sock != None):
            try:
                data = self.sock.recv(1024)
            except socket.error as msg:
                self.log.debug('**** ERROR: '+str(msg)+' while receiving from %s server at port %d.' % (self.host, self.port))
                self.reconnect()
        return data

    def get_gpio_switch(self):
        self.gpio_pins = []
        self.gpio_chip = ""
        for pin in ["SYSCTLR_JTAG_S0", "SYSCTLR_JTAG_S1"]:
            data = os.popen("gpiofind %s" % pin).read().split()
            self.gpio_pins.append(data[1])
            self.gpio_chip = data[0]

if __name__ == '__main__':
    sclient = SocketClient('localhost', 9000)
    conn_status = sclient.connect()

    if conn_status == 0:
        data = sclient.get_msg()
        data = sclient.get_msg()
        sclient.disconnect()


